Compare commits
	
		
			171 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2563e9d1d6 | |||
| 24bec66390 | |||
| 3d30734dc2 | |||
| cbc1d53cc2 | |||
| 9a1f7a13f8 | |||
| d48b9b7f91 | |||
| 358865dc6a | |||
| e750c37473 | |||
| 71a589c10c | |||
| 3281239ff5 | |||
| 3e2cdddd5a | |||
| 1d4c3e51c7 | |||
| 1694c71528 | |||
| 169ffc1b0b | |||
| 89eb3259d4 | |||
| ffd88ffc66 | |||
| 6c2d13bd17 | |||
| 8d92a75ef0 | |||
| acb86ae266 | |||
| 473cf978b4 | |||
| 4debab2636 | |||
| e91e197873 | |||
| b30b6734a1 | |||
| dadb80608a | |||
| 16c9969fb9 | |||
| dc1644ed25 | |||
| 2cc9b3e1ed | |||
| 415f340a68 | |||
| 2d4869128d | |||
| ae8bc01d9b | |||
| e2552d9442 | |||
| 5d1b5d80b6 | |||
| 366804aa29 | |||
| 9240e0c903 | |||
| 7baaf2cff3 | |||
| 9fbc1ba031 | |||
| 6704c07db0 | |||
| a87165148a | |||
| ec4bcd093b | |||
| 5552055b98 | |||
| 03aa67034d | |||
| fc21427685 | |||
| 819b02a204 | |||
| de0bd5fd57 | |||
| 8aa1d94a1a | |||
| f8a59133a2 | |||
| c382f770dc | |||
| cde4ec13ef | |||
| ecad1ea839 | |||
| 6b91b22713 | |||
| e34c91b2cc | |||
| 822759a688 | |||
| b606037890 | |||
| 74d9b94119 | |||
| b1e9f955b0 | |||
| e0356fa360 | |||
| fbc67eeb98 | |||
| 09fd73b130 | |||
| 259a76f46b | |||
| c6504c2eaf | |||
| 7d104a1514 | |||
| b3bd61c89e | |||
| e49dca0275 | |||
| 03125b3a2d | |||
| a523379b3a | |||
| aa6348a29a | |||
| b9f0f1a69a | |||
| a284806d3c | |||
| 7e10c1db65 | |||
| 11790638d6 | |||
| 0583cbe266 | |||
| 2e6874c822 | |||
| 2ce41990bf | |||
| c8aeba38ba | |||
| 5e02502a5c | |||
| 382cc3d844 | |||
| dd74d9ee89 | |||
| 383f82807f | |||
| d4579a9a41 | |||
| 66a07c6a51 | |||
| 66ffd8e936 | |||
| 44f07ca231 | |||
| ff14f024af | |||
| dccf7c6c8d | |||
| 10d7955f99 | |||
| 64ade901de | |||
| eb0dd3f781 | |||
| 66e6cd80d3 | |||
| 959b32de1c | |||
| 9c6dc5b424 | |||
| aaab95d414 | |||
| 7d4e93912c | |||
| d65b463547 | |||
| 6e5a4bcb39 | |||
| 1a799dc30a | |||
| e3d2676858 | |||
| 42851686ca | |||
| e3943d868a | |||
| 7654b795c7 | |||
| 489244f1a9 | |||
| cbcce336d6 | |||
| 52a96b2a4f | |||
| ce6002a631 | |||
| 84a9cf069a | |||
| 83f19a7572 | |||
| a1a4c8b56d | |||
| d8901126d0 | |||
| 854db4ece8 | |||
| 07f2e65fc7 | |||
| ccf09f97d5 | |||
| 8f9a4ebc04 | |||
| f1833f13d5 | |||
| 6a81e369fa | |||
| 597e9e1ea9 | |||
| 9bb027ec4c | |||
| fbbbaa5d49 | |||
| aaec5a3fc9 | |||
| 7cd24cd51d | |||
| c81b34c1d0 | |||
| 7b1acc494d | |||
| 6ff90694e2 | |||
| 157c7c66b5 | |||
| 93249258c6 | |||
| 01c01a46fa | |||
| 0e2a10fe94 | |||
| 0b9f3de47c | |||
| bc239eead1 | |||
| 7a09869b0c | |||
| bdc0de6ada | |||
| 6870a7f9b1 | |||
| ace1a1b063 | |||
| d87b879cc3 | |||
| 65b36f8e69 | |||
| 87387a54f2 | |||
| b497cebe76 | |||
| 0fa107a75b | |||
| b34e3aeed0 | |||
| 35b18d72fd | |||
| 4b80f30afb | |||
| 86c54e04a8 | |||
| ef9fc596f5 | |||
| ad34e455ce | |||
| 01fdd0bee2 | |||
| 32ffa345cd | |||
| 6fc3c16073 | |||
| 7d58657c80 | |||
| fcd657c10e | |||
| 4ab77c5557 | |||
| 2bbaa500f4 | |||
| 722a20e141 | |||
| 041c24a837 | |||
| 39a3baa00b | |||
| f7acbb1eaa | |||
| e6fbf7aa5b | |||
| 87926e69db | |||
| 36a084eab6 | |||
| a9e319e0c0 | |||
| ea23b97231 | |||
| 7df76f9642 | |||
| f6db117a5e | |||
| 23c3cd605d | |||
| 77690702c0 | |||
| e0093480d9 | |||
| c7679b7a67 | |||
| e6ac34bde8 | |||
| 8c4b595c30 | |||
| be629e5c6b | |||
| 63569684a3 | |||
| 5937a0d7ce | |||
| 4512272c1c | |||
| ce1f3842e0 | 
							
								
								
									
										24
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								.drone.yml
									
									
									
									
									
								
							| @@ -4,18 +4,6 @@ type: docker | ||||
| name: build:dev | ||||
|  | ||||
| steps: | ||||
|   - name: build dev | ||||
|     image: plugins/docker | ||||
|     depends_on: [clone] | ||||
|     settings: | ||||
|       username: | ||||
|         from_secret: DOCKER_REGISTRY_USER | ||||
|       password: | ||||
|         from_secret: DOCKER_REGISTRY_PASSWORD | ||||
|       repo: registry.odit.services/lfk/frontend | ||||
|       tags: | ||||
|         - dev | ||||
|       registry: registry.odit.services | ||||
|   - name: run full license export | ||||
|     depends_on: ["clone"] | ||||
|     image: node:alpine | ||||
| @@ -33,6 +21,18 @@ steps: | ||||
|       remote: git@git.odit.services:lfk/frontend.git | ||||
|       ssh_key: | ||||
|         from_secret: GITLAB_SSHKEY | ||||
|   - name: build dev | ||||
|     image: plugins/docker | ||||
|     depends_on: [clone] | ||||
|     settings: | ||||
|       username: | ||||
|         from_secret: DOCKER_REGISTRY_USER | ||||
|       password: | ||||
|         from_secret: DOCKER_REGISTRY_PASSWORD | ||||
|       repo: registry.odit.services/lfk/frontend | ||||
|       tags: | ||||
|         - dev | ||||
|       registry: registry.odit.services | ||||
|  | ||||
| trigger: | ||||
|   branch: | ||||
|   | ||||
							
								
								
									
										15
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,10 +1,11 @@ | ||||
| .vscode | ||||
| .idea | ||||
| node_modules | ||||
| dist | ||||
| dist-ssr | ||||
| public/env.js | ||||
| /build | ||||
| yarn.lock | ||||
| build | ||||
| package-lock.json | ||||
| yarn.lock | ||||
| *.map | ||||
| public/env.js | ||||
| public/sw.js | ||||
| public/index.html | ||||
| public/workbox-*.js | ||||
| svelte.config.js | ||||
| public/index.html | ||||
|   | ||||
							
								
								
									
										13
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| { | ||||
|     "recommendations": [ | ||||
|         "2gua.rainbow-brackets", | ||||
|         "christian-kohler.npm-intellisense", | ||||
|         "remimarsal.prettier-now", | ||||
|         "svelte.svelte-vscode", | ||||
|         "lokalise.i18n-ally", | ||||
|         "fivethree.vscode-svelte-snippets" | ||||
|     ], | ||||
|     "unwantedRecommendations": [ | ||||
|         "antfu.i18n-ally" | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "i18n-ally.localesPaths": "src/locales", | ||||
|     "i18n-ally.keystyle": "nested" | ||||
| } | ||||
							
								
								
									
										180
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										180
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -2,10 +2,190 @@ | ||||
|  | ||||
| All notable changes to this project will be documented in this file. Dates are displayed in UTC. | ||||
|  | ||||
| #### [0.5.0](https://git.odit.services/lfk/frontend/compare/0.4.0...0.5.0) | ||||
|  | ||||
| - Merge pull request 'feature/12-user-management' (#39) from feature/12-user-management into dev [`#12`](https://git.odit.services/lfk/frontend/issues/12) | ||||
| - Merge pull request 'feature/13-runner-management' (#42) from feature/13-runner-management into dev [`#13`](https://git.odit.services/lfk/frontend/issues/13) | ||||
| - Merge pull request 'added permissions to dashboard sidebar' (#41) from feature/40-dynamic-sidebar-options into dev [`#41`](https://git.odit.services/lfk/frontend/issues/41) | ||||
| - basic UserPermissions view [`9c6dc5b`](https://git.odit.services/lfk/frontend/commit/9c6dc5b4247e76be63a92294556be1e4a9154256) | ||||
| - ✨ basic RunnerDetail [`03aa670`](https://git.odit.services/lfk/frontend/commit/03aa67034d5fc4ec64d5646a6583666301bb538e) | ||||
| - 👀 ImportRunnerModal - layout cleanup [`2cc9b3e`](https://git.odit.services/lfk/frontend/commit/2cc9b3e1ede47bb1a4b598afc260d0bc06ed0902) | ||||
| - ✨ basic ImportRunnerModal with CSV input [`b1e9f95`](https://git.odit.services/lfk/frontend/commit/b1e9f955b054cd17cb83f8364b6bc2b22159142f) | ||||
| - ✨ basic xlsx + csv parsing [`74d9b94`](https://git.odit.services/lfk/frontend/commit/74d9b94119b49a77ff3e787a84f76c1e2e13d9e4) | ||||
| - 🧹 darkmode classes cleanup [`259a76f`](https://git.odit.services/lfk/frontend/commit/259a76f46b9ac01b177100dfce0db367b517b3a0) | ||||
| - UserDetail - basic permission badges [`7d4e939`](https://git.odit.services/lfk/frontend/commit/7d4e93912c4e1fd8a875f42a0f11b906f1ca0091) | ||||
| - added permissions to dashboard sidebar [`acb86ae`](https://git.odit.services/lfk/frontend/commit/acb86ae2666ec3c336973184ec5cb1e2ffba9ebe) | ||||
| - UserPermissions - working edit [`10d7955`](https://git.odit.services/lfk/frontend/commit/10d7955f99b1a640eb1ac83764c114a25cfcc07b) | ||||
| - 🐞 ImportRunnerModal - table overflow fix [`dc1644e`](https://git.odit.services/lfk/frontend/commit/dc1644ed25b82afe6a51fbe0e1de69ddf54ef81d) | ||||
| - 🧹 general runner cleanup [`9240e0c`](https://git.odit.services/lfk/frontend/commit/9240e0c90380fc6b44a3dde480bd2d29476b2a0f) | ||||
| - ✨ formatting [`6e5a4bc`](https://git.odit.services/lfk/frontend/commit/6e5a4bcb39259fc376262c16ad95555fd9cf7a0b) | ||||
| - ✨ ImportRunnerModal usage in TeamDetail [`de0bd5f`](https://git.odit.services/lfk/frontend/commit/de0bd5fd57edba75faaa2ba097a2c7fb2f621f9a) | ||||
| - 💣 process breaking changes for lib v0.3.0 [`dadb806`](https://git.odit.services/lfk/frontend/commit/dadb80608a55980f7b647d41c91061055287a250) | ||||
| - 👀 basic ui interaction for ImportRunnerModal [`c382f77`](https://git.odit.services/lfk/frontend/commit/c382f770dc05f57a471389d3d4d012fd8439c014) | ||||
| - 🐞 [bugfix] RunnerDetail update [`ec4bcd0`](https://git.odit.services/lfk/frontend/commit/ec4bcd093b6c94f2e3597de6de98db5ca47c5876) | ||||
| - 🏃♂️ support for runner group edit [`8d92a75`](https://git.odit.services/lfk/frontend/commit/8d92a75ef03854397a7c01ed9ea22967b994f9f2) | ||||
| - AddRunnerModal - improved team select [`1694c71`](https://git.odit.services/lfk/frontend/commit/1694c71528f9114622daae1bd16aaed9f94fc4ee) | ||||
| - 🙋♂️🔒 UserDetail - permission layout ui [`65b36f8`](https://git.odit.services/lfk/frontend/commit/65b36f8e695479809566213ebcb64d51a7c6a52e) | ||||
| - 🌎 added locale based csv/xlsx header parsing [`b606037`](https://git.odit.services/lfk/frontend/commit/b606037890856c1115315bb61d8469ff66dc4289) | ||||
| - AddUserModal - username/email validation [`ff14f02`](https://git.odit.services/lfk/frontend/commit/ff14f024afda77e6f5f9e95a8f34e50cc8509790) | ||||
| - UserDetail - link to permission page [`aaab95d`](https://git.odit.services/lfk/frontend/commit/aaab95d41400629683d89ee16013b6b4328c582c) | ||||
| - UserDetail - add permission layout [`d65b463`](https://git.odit.services/lfk/frontend/commit/d65b463547a5cb4fb6f6de0f95795a66ebe92e31) | ||||
| - RunnerDetail - button text fixes [`5552055`](https://git.odit.services/lfk/frontend/commit/5552055b989313e8999e424b6ad26c4398e8c6fa) | ||||
| - 🧹 RunnerDetail cleanup + i18n 🌎 [`a871651`](https://git.odit.services/lfk/frontend/commit/a87165148a3ccdb4c4e10f2c6545d5fea9e78b30) | ||||
| - ↙ added default fallback parsing to ImportRunnerModal [`ecad1ea`](https://git.odit.services/lfk/frontend/commit/ecad1ea839dbcea396217cb18fe335694406cdb1) | ||||
| - 🌎 i18n [`366804a`](https://git.odit.services/lfk/frontend/commit/366804aa29eb0231e40017d1e5c3419486070174) | ||||
| - ✨ ImportRunnerModal - compatibility for multi-component access [`f8a5913`](https://git.odit.services/lfk/frontend/commit/f8a59133a2534efdc9e13104eb64157a9724e746) | ||||
| - ✨ use ImportRunnerModal in OrgDetail + Orgs [`8aa1d94`](https://git.odit.services/lfk/frontend/commit/8aa1d94a1aa976cc6cb4f7d3cc798822861676ad) | ||||
| - ✨ ImportRunnerModal - hide team when loading from TeamDetail [`819b02a`](https://git.odit.services/lfk/frontend/commit/819b02a204cd43baaaf85d82cf0209a38028e6ce) | ||||
| - 🚀 ImportRunnerModal - working demo [`6b91b22`](https://git.odit.services/lfk/frontend/commit/6b91b227136580180d5300b2614a6148995c4640) | ||||
| - 🌎 i18n [`ae8bc01`](https://git.odit.services/lfk/frontend/commit/ae8bc01d9bab994aad6407102f3a6ef0167d008b) | ||||
| - 🐞 fixed merge conflict errors [`cbc1d53`](https://git.odit.services/lfk/frontend/commit/cbc1d53cc257cbf60172dd4356831e43671ec626) | ||||
| - ✨ basic cancel event dispatch from ImportRunnerModal [`cde4ec1`](https://git.odit.services/lfk/frontend/commit/cde4ec13efef8423464ebe90a4f0212a3be6cf77) | ||||
| - 🧹 AddUserModal cleanup [`44f07ca`](https://git.odit.services/lfk/frontend/commit/44f07ca231307ebdbe38b822408c3965077053ef) | ||||
| - 🙋♂️ UserDetail - active/inactive user state edit [`86c54e0`](https://git.odit.services/lfk/frontend/commit/86c54e04a84917383d3726b324c416ca1bd74627) | ||||
| - UserDetail - update permission badges on change save [`358865d`](https://git.odit.services/lfk/frontend/commit/358865dc6a8e5a084078696048df99a67564a9e1) | ||||
| - i18n 🌍 [`415f340`](https://git.odit.services/lfk/frontend/commit/415f340a68078e86fcf28d24b069ca030e28fc82) | ||||
| - 👀 UsersOverview - disable advanced search [`b34e3ae`](https://git.odit.services/lfk/frontend/commit/b34e3aeed0f026308288fb568542cc0ba587b576) | ||||
| - 🙋♂️ UserDetail - disable profile picture edit [`ef9fc59`](https://git.odit.services/lfk/frontend/commit/ef9fc596f57d63c1bf9c26614d746bf37394f832) | ||||
| - 🌎 i18n [`2d48691`](https://git.odit.services/lfk/frontend/commit/2d4869128d2af5e7fa5e4bcf1d6b687fca6761e9) | ||||
| - 🌎 i18n in ImportRunnerModal headers [`e34c91b`](https://git.odit.services/lfk/frontend/commit/e34c91b2cc64221cd553e80974a14448e3afc340) | ||||
| - 🌎 last i18n keys [`6c2d13b`](https://git.odit.services/lfk/frontend/commit/6c2d13bd1721918f1ef847563cfad61284c40487) | ||||
| - Fixed User group update [`1a799dc`](https://git.odit.services/lfk/frontend/commit/1a799dc30a4f051305e5279bb3325e64ab08f323) | ||||
| - 🌎 UserDetail - more i18n keys [`e750c37`](https://git.odit.services/lfk/frontend/commit/e750c374739104c6aaac1abace9e9632b40342da) | ||||
| - ⏫ general dependency bump [`b30b673`](https://git.odit.services/lfk/frontend/commit/b30b6734a1f4cc384f06147dfc657f2a1a05b3c0) | ||||
| - 💻 updated VSCode recommended extensions [`fc21427`](https://git.odit.services/lfk/frontend/commit/fc214276859189c6e9b6fdc91eb440cf4afda7c0) | ||||
| - 🐞 improved version builder from template [`09fd73b`](https://git.odit.services/lfk/frontend/commit/09fd73b13086025a31b4c1363331230e1c91e593) | ||||
| - ⏫ general dependency bump [`9a1f7a1`](https://git.odit.services/lfk/frontend/commit/9a1f7a13f8669b690fb3f9533df7fdfa2f1bc093) | ||||
| - 🌎 added more i18n keys [`169ffc1`](https://git.odit.services/lfk/frontend/commit/169ffc1b0b76c74350060c97218643789e0ea4f7) | ||||
| - 😦 added missing dependencies [`822759a`](https://git.odit.services/lfk/frontend/commit/822759a6884eab81f62d96bd7e6f038fa8bc1926) | ||||
| - fixed runner permissions [`e0356fa`](https://git.odit.services/lfk/frontend/commit/e0356fa360b74f91b38dc09351930c8c0778f845) | ||||
| - Added friles to ignore [`9fbc1ba`](https://git.odit.services/lfk/frontend/commit/9fbc1ba0315fc0a2b7f3d95339cb17fc74e57903) | ||||
| - ✨ new UsersEmptyState [`959b32d`](https://git.odit.services/lfk/frontend/commit/959b32de1c6fc74bec585630b3025babc1a8edc4) | ||||
| - 🐞 fix package release for index template compatibility [`24bec66`](https://git.odit.services/lfk/frontend/commit/24bec66390bfddd57c0cd1d35de94a79bdbf477c) | ||||
| - new license file version [CI SKIP] [`3d30734`](https://git.odit.services/lfk/frontend/commit/3d30734dc2716a88d823fe7a7650f17d14a8f18d) | ||||
| - new license file version [CI SKIP] [`3281239`](https://git.odit.services/lfk/frontend/commit/3281239ff5b4968fb1dc8547e7c16f0789ec2b8c) | ||||
| - 🐞 alphabetically sort permission targets in UserDetail [`ffd88ff`](https://git.odit.services/lfk/frontend/commit/ffd88ffc666d9d3add43dfa4ec4b6e01ca7084b5) | ||||
| - new license file version [CI SKIP] [`e91e197`](https://git.odit.services/lfk/frontend/commit/e91e197873e524b93b2cf79286c66c8b776fbae2) | ||||
| - Deleted files to ignore [`6704c07`](https://git.odit.services/lfk/frontend/commit/6704c07db0a00b17327a4b693b3f74ed41a1a41d) | ||||
| - new license file version [CI SKIP] [`c6504c2`](https://git.odit.services/lfk/frontend/commit/c6504c2eaf1d0c5fb14a4035ccbf347d851068d9) | ||||
|  | ||||
| #### [0.4.0](https://git.odit.services/lfk/frontend/compare/0.3.1...0.4.0) | ||||
|  | ||||
| > 17 January 2021 | ||||
|  | ||||
| - Merge commit 'a284806d3cb769030a4e28d0403190b746f8fc61' into dev [`#37`](https://git.odit.services/lfk/frontend/issues/37) | ||||
| - ✨ AddRunnerModal [`66ffd8e`](https://git.odit.services/lfk/frontend/commit/66ffd8e936010960766e7f9021319d549e1d3e6b) | ||||
| - ✨ RunnersOverview [`66a07c6`](https://git.odit.services/lfk/frontend/commit/66a07c6a51674a92d5e8459f250de2ab5ff6d902) | ||||
| - ⚡ improved dev scripts for speed starts [`383f828`](https://git.odit.services/lfk/frontend/commit/383f82807f4090bdd2c3dcdc695b75093b854031) | ||||
| - 🚀RELEASE v0.4.0 [`7d104a1`](https://git.odit.services/lfk/frontend/commit/7d104a151422a7c8306ef4d9f0ab25d26f8eb78b) | ||||
| - ✨ dynamic contact info in AddRunnerModal [`d4579a9`](https://git.odit.services/lfk/frontend/commit/d4579a9a410a27676e0ed0285bd124696153aae4) | ||||
| - 🐞 fix cross-env logic for faster dev starts ⚡ [`2ce4199`](https://git.odit.services/lfk/frontend/commit/2ce41990bf9911db11eb556ce4c9aa3b3e5ca16c) | ||||
| - 👩💻 developer configs/ recommendations for VSCode [`5e02502`](https://git.odit.services/lfk/frontend/commit/5e02502a5c2b8ebf798cbc21856b4425f8510041) | ||||
| - ⚡ re-enable PWA functionality via serviceworker [`a284806`](https://git.odit.services/lfk/frontend/commit/a284806d3cb769030a4e28d0403190b746f8fc61) | ||||
| - 🔨 cleaned up build process + Dockerfile [`7e10c1d`](https://git.odit.services/lfk/frontend/commit/7e10c1db659c21cd737b5d1e10bf3e61c4e0de94) | ||||
| - 🔨 cleaned up build process + Dockerfile [`1179063`](https://git.odit.services/lfk/frontend/commit/11790638d68e8c43f91448bd0c35f910a3d9e446) | ||||
| - ⚡ improved serviceworker + PWA logic [`0583cbe`](https://git.odit.services/lfk/frontend/commit/0583cbe2664f8832c5eaa7fb155b3e6deccb2ed3) | ||||
| - apply new gitignore config [`2e6874c`](https://git.odit.services/lfk/frontend/commit/2e6874c822f2f8e9a8a7b74b4765631ba08f0255) | ||||
| - ⚡ PWA optimizations [`dccf7c6`](https://git.odit.services/lfk/frontend/commit/dccf7c6c8de0da0f25ad77e20822fb4fbf68a61a) | ||||
| - ⏫ general dependency bumps [`03125b3`](https://git.odit.services/lfk/frontend/commit/03125b3a2d0ad4b12cffdec98f27da14f3f45f77) | ||||
| - 🐞 gitignore fix [`e49dca0`](https://git.odit.services/lfk/frontend/commit/e49dca02754aa57cbe464066f11505db4a9b5ca9) | ||||
| - gitignore fix [`a523379`](https://git.odit.services/lfk/frontend/commit/a523379b3a5f7f3cffecca82c0c066167da046ca) | ||||
| - fix package:dev script [`aa6348a`](https://git.odit.services/lfk/frontend/commit/aa6348a29a6e9ecb9789681b7195527c4eef19e4) | ||||
| - new license file version [CI SKIP] [`382cc3d`](https://git.odit.services/lfk/frontend/commit/382cc3d844bf0af7c46492907f8a9a78fadc25d0) | ||||
| - 🧹 gitignore changes in public/index.html & svelte.config.js [`dd74d9e`](https://git.odit.services/lfk/frontend/commit/dd74d9ee89b80c46ce3d3347a3c7cbe34373019c) | ||||
|  | ||||
| #### [0.3.1](https://git.odit.services/lfk/frontend/compare/0.3.0...0.3.1) | ||||
|  | ||||
| > 16 January 2021 | ||||
|  | ||||
| - Merge pull request 'feature/16-org-management' (#35) from feature/16-org-management into dev [`#16`](https://git.odit.services/lfk/frontend/issues/16) | ||||
| - 🏃♂️🏃♂️🏃♂️ basic UI components for team management [`d87b879`](https://git.odit.services/lfk/frontend/commit/d87b879cc3d6c771a8a9932409e39068e1b2acdb) | ||||
| - ✨ TeamDetail with edit,delete [`ccf09f9`](https://git.odit.services/lfk/frontend/commit/ccf09f97d5fb476113f24a9559a48bccd75fd0a5) | ||||
| - 🔒 ConfirmTeamDeletion in TeamsOverview [`cbcce33`](https://git.odit.services/lfk/frontend/commit/cbcce336d68b0752daeaf4b5608c43ff6fa11c0d) | ||||
| - 🔒 ConfirmOrgDeletion in OrgDetail [`d890112`](https://git.odit.services/lfk/frontend/commit/d8901126d0cc91cabe3b94a30a83f36e6288126d) | ||||
| - ✨ basic TeamsOverview [`597e9e1`](https://git.odit.services/lfk/frontend/commit/597e9e1ea9da7c73bdcb8ef1ae1a13dfa68ff5a3) | ||||
| - ✨ UX - ConfirmOrgDeletion cancel event reflection in datatable [`84a9cf0`](https://git.odit.services/lfk/frontend/commit/84a9cf069a4aa0940eaacc87ea67e745deabe939) | ||||
| - 🐞 fix Dashboard sidebar responsiveness [`6a81e36`](https://git.odit.services/lfk/frontend/commit/6a81e369fa20f0bb2846365a45f96e91e95fe2e7) | ||||
| - 🧹 Dashboard - drop header bar [`f1833f1`](https://git.odit.services/lfk/frontend/commit/f1833f13d57595c23abf29bce1a2795cbb05a116) | ||||
| - ✨ AddTeamModal working [`9bb027e`](https://git.odit.services/lfk/frontend/commit/9bb027ec4c73483907d396180f739dc3a11b2404) | ||||
| - 🧹 TeamDetail cleanup [`7654b79`](https://git.odit.services/lfk/frontend/commit/7654b795c756ca198bad77068823032714408535) | ||||
| - 🚀RELEASE v0.3.1 [`64ade90`](https://git.odit.services/lfk/frontend/commit/64ade901ded75fa738c713446343a209eca89ce6) | ||||
| - 🤝 attribution/ credits for icons and illustrations [`eb0dd3f`](https://git.odit.services/lfk/frontend/commit/eb0dd3f781f739c6511588a8e153c14a39096025) | ||||
| - 🔒 re-enable confirmation in OrgOverview [`ce6002a`](https://git.odit.services/lfk/frontend/commit/ce6002a631dd3c140f3892c750d052e89c135653) | ||||
| - ✨ added new empty states [`66e6cd8`](https://git.odit.services/lfk/frontend/commit/66e6cd80d39ef18a29fd8ac80fbac929bd0c4f8c) | ||||
| - Merge pull request 'feature/14-team-management' (#36) from feature/14-team-management into dev [`4285168`](https://git.odit.services/lfk/frontend/commit/42851686caae69e6672f48cd7df77ee4c2e49092) | ||||
| - 🧹 TeamsOverview - formatting [`6870a7f`](https://git.odit.services/lfk/frontend/commit/6870a7f9b1fce2f06182dafa502f6dc4bb818bd3) | ||||
| - 🔒 ConfirmOrgDeletion in OrgOverview [`83f19a7`](https://git.odit.services/lfk/frontend/commit/83f19a7572255b5c095c68d688a963dbe3cf4a75) | ||||
| - 🧹 Team cleanups [`07f2e65`](https://git.odit.services/lfk/frontend/commit/07f2e65fc722c0328ee5a8dc4d01fc89c906fa86) | ||||
| - 🔒 ConfirmTeamDeletion in TeamDetail [`489244f`](https://git.odit.services/lfk/frontend/commit/489244f1a9636b9807e751073443f1c767f7b8ca) | ||||
| - 🐞 fix deletion in TeamDetail + TeamsOverview [`e3943d8`](https://git.odit.services/lfk/frontend/commit/e3943d868a6131ea561c4000159b77ff70a4af8b) | ||||
| - 🐞 fix component mount in TeamsEmptyState [`ace1a1b`](https://git.odit.services/lfk/frontend/commit/ace1a1b06379d922df2593c3a7aec77d5f958512) | ||||
| - new license file version [CI SKIP] [`aaec5a3`](https://git.odit.services/lfk/frontend/commit/aaec5a3fc94e13c6d29ea16ab110151aa6dc1156) | ||||
| - 🧹 drop tmp modification from UsersOverview [`854db4e`](https://git.odit.services/lfk/frontend/commit/854db4ece8388a70b8a50c5b4c7ce9be974a2616) | ||||
|  | ||||
| #### [0.3.0](https://git.odit.services/lfk/frontend/compare/0.2.1...0.3.0) | ||||
|  | ||||
| > 15 January 2021 | ||||
|  | ||||
| - Merge pull request 'feature/16-org-management' (#32) from feature/16-org-management into dev [`#16`](https://git.odit.services/lfk/frontend/issues/16) | ||||
| - Merge commit 'fcd657c10ea14290455cfb0bf2de89375a664143' into dev [`#31`](https://git.odit.services/lfk/frontend/issues/31) | ||||
| - ✨ added Org base components [`bdc0de6`](https://git.odit.services/lfk/frontend/commit/bdc0de6adab2db95e6075a993bd50317e9e36eb5) | ||||
| - 🚀RELEASE v0.3.0 [`7cd24cd`](https://git.odit.services/lfk/frontend/commit/7cd24cd51d52efff22f4ee0817f2d89f6efb35ed) | ||||
| - 🏬 OrgDetail ui [`7a09869`](https://git.odit.services/lfk/frontend/commit/7a09869b0ca9d5dd38cf0c0010b71ab9ce6c6f40) | ||||
| - 🏁 finish basic functionality of AddOrgModal + OrgDetail [`9324925`](https://git.odit.services/lfk/frontend/commit/93249258c6b0f34da22f0ed5d290b437d221a8fd) | ||||
| - ✨ basic functionality in OrgDetail [`0e2a10f`](https://git.odit.services/lfk/frontend/commit/0e2a10fe94075b3cda8ef3ebae6a6159a2e6bbf9) | ||||
| - 🐞 fix sidebar mobile-md scaling [`fcd657c`](https://git.odit.services/lfk/frontend/commit/fcd657c10ea14290455cfb0bf2de89375a664143) | ||||
| - 🔨 reorder CI build order for correct license exporting [`7d58657`](https://git.odit.services/lfk/frontend/commit/7d58657c800e9e494c1d1f098ad0dc34d1f0cd8d) | ||||
| - 💬 AddOrgModal bindings [`bc239ee`](https://git.odit.services/lfk/frontend/commit/bc239eead1d1a29c9662d94db797c6bf23e43166) | ||||
| - ✨ improvements in OrgOverview [`0b9f3de`](https://git.odit.services/lfk/frontend/commit/0b9f3de47c0c8b750c38da07e53927ba55ef3499) | ||||
| - 🔨 config compatibility for new Snowpack V3 bundler [`32ffa34`](https://git.odit.services/lfk/frontend/commit/32ffa345cde67b1affe2750add620522c0a24577) | ||||
| - 🧹 general component cleanup [`157c7c6`](https://git.odit.services/lfk/frontend/commit/157c7c66b57c3a5053c780682402d3615fbc046a) | ||||
| - ⏫ basic dependency bump [`6fc3c16`](https://git.odit.services/lfk/frontend/commit/6fc3c16073555fffd55773218c5c2eed361f076b) | ||||
| - 🌎 i18n [`01c01a4`](https://git.odit.services/lfk/frontend/commit/01c01a46faa9332a2e437861fdb42afe962da833) | ||||
| - ⏫ bump gridjs to 3.2.2 [`6ff9069`](https://git.odit.services/lfk/frontend/commit/6ff90694e288356e16f5226a6e0a054e6fec8b3f) | ||||
| - [tmp] - disable serviceworker [`0fa107a`](https://git.odit.services/lfk/frontend/commit/0fa107a75bc33046758bc42523262a566da9f60f) | ||||
| - new license file version [CI SKIP] [`ad34e45`](https://git.odit.services/lfk/frontend/commit/ad34e455ceedc0e0ccca5ccd86f7f011a7667873) | ||||
| - ⏫ Bump Dockerfile builder to 15.5.1-alpine3.12 [`01fdd0b`](https://git.odit.services/lfk/frontend/commit/01fdd0bee2199bb58373aecc9942591a2d45011a) | ||||
| - new license file version [CI SKIP] [`87926e6`](https://git.odit.services/lfk/frontend/commit/87926e69dbd5b93b2c98cd68fb31913259e3cc00) | ||||
| - 🐳 Dockerfile - drop js sourcemaps [`4b80f30`](https://git.odit.services/lfk/frontend/commit/4b80f30afbfb8c16b9395fb310f309c516c48a17) | ||||
|  | ||||
| #### [0.2.1](https://git.odit.services/lfk/frontend/compare/0.2.0...0.2.1) | ||||
|  | ||||
| > 13 January 2021 | ||||
|  | ||||
| - 🔒 UserDetail - WIP on Permissions [`36a084e`](https://git.odit.services/lfk/frontend/commit/36a084eab6bd0c922da29ebb2d260ba803bf9675) | ||||
| - 👪 UserDetail - group edit support [`a9e319e`](https://git.odit.services/lfk/frontend/commit/a9e319e0c0f2bfaba42bc3af875462e394489dc2) | ||||
| - 🙋♂️ UserDetails - group updating [`041c24a`](https://git.odit.services/lfk/frontend/commit/041c24a837d58ff46362699b54e8088f22ba2daa) | ||||
| - ⚡ shared state reactivity - AddUserModal-Users-UsersOverview [`f7acbb1`](https://git.odit.services/lfk/frontend/commit/f7acbb1eaa14ddf41e29ff1db631520123218289) | ||||
| - ✨ AddUserModal + UserDetail - optional username field [`7df76f9`](https://git.odit.services/lfk/frontend/commit/7df76f9642b528586fd654f27148b5502400ffdf) | ||||
| - UserDetail - fixed group updating [`e6fbf7a`](https://git.odit.services/lfk/frontend/commit/e6fbf7aa5b230733ffd92fdfe851b9f86001c859) | ||||
| - 🚀RELEASE v0.2.1 [`2bbaa50`](https://git.odit.services/lfk/frontend/commit/2bbaa500f4ca56e41613cac12228f4e3327180ab) | ||||
| - 💬 UserDetail - info Toasts [`ea23b97`](https://git.odit.services/lfk/frontend/commit/ea23b972315db39e1f800366d52e3bacf9eaf58b) | ||||
| - 🔨 optimized release script [`23c3cd6`](https://git.odit.services/lfk/frontend/commit/23c3cd605db950a5620ea709748216f7ea034385) | ||||
| - 🐞 fix package release script: locales directory [`722a20e`](https://git.odit.services/lfk/frontend/commit/722a20e141079302da8876be1ccc55026c588048) | ||||
| - ⏫ bump @odit/lfk-client-js to 0.0.11 [`f6db117`](https://git.odit.services/lfk/frontend/commit/f6db117a5eca5c9692a4c5d83159917a712a0e63) | ||||
| - 🐞 UserDetail - fix permission reactivity by assignments [`39a3baa`](https://git.odit.services/lfk/frontend/commit/39a3baa00b0575ee3150c7ae26f32995e2e857d9) | ||||
|  | ||||
| #### [0.2.0](https://git.odit.services/lfk/frontend/compare/0.1.6...0.2.0) | ||||
|  | ||||
| > 11 January 2021 | ||||
|  | ||||
| - 🔒 added rendering based on permission level [`5937a0d`](https://git.odit.services/lfk/frontend/commit/5937a0d7ce970d38ddb0e4c6a02d11f8c8b53e9b) | ||||
| - ❌ UserDetail - delete [`4512272`](https://git.odit.services/lfk/frontend/commit/4512272c1c2a5c861d8ac4c9908244df125dc3d9) | ||||
| - 🖊 UserDetail - reactivity on edit + update functionality [`ce1f384`](https://git.odit.services/lfk/frontend/commit/ce1f3842e0f581d9e94ea6079cf223d945f7465d) | ||||
| - 🚀RELEASE v0.2.0 [`7769070`](https://git.odit.services/lfk/frontend/commit/77690702c0461b71ff1b65a44bfdc331d5a42c3e) | ||||
| - [tmp] - disable darkmode + re-enable sw [`c7679b7`](https://git.odit.services/lfk/frontend/commit/c7679b7a67b362a7fbf796f612892bdfac50731c) | ||||
| - 🕕 set manual refresh time to 2min [`be629e5`](https://git.odit.services/lfk/frontend/commit/be629e5c6b076bf9a28dcc953e97478d244d8413) | ||||
| - 🐞 [tmp] - nginx.conf - disable .js file caching [`e6ac34b`](https://git.odit.services/lfk/frontend/commit/e6ac34bde8d288a45e83fc6723a2bbe952f2622a) | ||||
| - ℹ update jwtinfo store on token refresh [`6356968`](https://git.odit.services/lfk/frontend/commit/63569684a392bf0c24c9c2efd7945114d1a230a5) | ||||
|  | ||||
| #### [0.1.6](https://git.odit.services/lfk/frontend/compare/0.1.5...0.1.6) | ||||
|  | ||||
| > 10 January 2021 | ||||
|  | ||||
| - ✨ UsersOverview - user delete [`f0c100a`](https://git.odit.services/lfk/frontend/commit/f0c100aee47d6a5aeb0995db79b268f33bf316e9) | ||||
| - 🔒 UserDetail - added basic layout for permission change [`81c1537`](https://git.odit.services/lfk/frontend/commit/81c1537bada2c87127385d9110d48459cd1b505f) | ||||
| - 🚀RELEASE v0.1.6 [`ee01c3a`](https://git.odit.services/lfk/frontend/commit/ee01c3a059c435add68938657955cbd2c5851c50) | ||||
| - 📧 UserDetail - email input [`f856c6a`](https://git.odit.services/lfk/frontend/commit/f856c6ae3792c93aee148339d89e3978db6e9293) | ||||
| - ✨ UserDetail multiselect layout for groups [`98ecfab`](https://git.odit.services/lfk/frontend/commit/98ecfab0325e35c5418775f7162049b56e5f332f) | ||||
| - UserDetail - placeholder for permission picker 🔒 [`b948b8c`](https://git.odit.services/lfk/frontend/commit/b948b8c1a48e5b4bc6a083139d26100ef4499970) | ||||
|   | ||||
| @@ -1,12 +1,11 @@ | ||||
| FROM node:15.4.0-alpine3.12 | ||||
| FROM node:15.5.1-alpine3.12 | ||||
| WORKDIR /app | ||||
| RUN npm i -g pnpm | ||||
| COPY package.json ./ | ||||
| RUN pnpm i | ||||
| COPY package.json *.config.js workbox-config.js ./ | ||||
| COPY package.json *.config.js workbox-config.js template-copy.js index.template.html s-config.template.js ./ | ||||
| COPY src ./src | ||||
| COPY public ./public | ||||
| RUN pnpm run build:sw | ||||
| RUN pnpm run build | ||||
| # final image | ||||
| FROM alpine | ||||
|   | ||||
| @@ -10,10 +10,11 @@ | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||||
|   <meta name="description" content="Lauf Für Kaya! - Admin" /> | ||||
|   <title>Lauf für Kaya! - Admin</title> | ||||
|   __TAILWIND_INSERT__ | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|   <span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.1.6-RELEASE_INFO</span> | ||||
|   <span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.5.0-RELEASE_INFO</span> | ||||
|   <noscript>You need to enable JavaScript to run this app.</noscript> | ||||
|   <script src="/env.js"></script> | ||||
|   <script defer type="module" src="/_dist_/index.js"></script> | ||||
| @@ -9,7 +9,7 @@ http { | ||||
|         location / { | ||||
|             try_files $uri $uri/ /index.html; | ||||
|         } | ||||
|         location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { | ||||
|         location ~* \.(?:ico|css|gif|jpe?g|png)$ { | ||||
|             expires 1y; | ||||
|             add_header Pragma public; | ||||
|             add_header Cache-Control "public"; | ||||
|   | ||||
							
								
								
									
										32
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,40 +1,44 @@ | ||||
| { | ||||
| 	"name": "@odit/lfk-frontend", | ||||
| 	"version": "0.1.6", | ||||
| 	"version": "0.5.0", | ||||
| 	"scripts": { | ||||
| 		"i18n-order": "node order.js", | ||||
| 		"dev": "snowpack dev", | ||||
| 		"build": "snowpack build", | ||||
| 		"dev:all": "yarn prebuild && snowpack dev", | ||||
| 		"dev": "cross-env NODE_ENV_ODIT=development_fast node template-copy.js && yarn build:sw && snowpack dev", | ||||
| 		"build": "yarn prebuild && snowpack build", | ||||
| 		"prebuild": "cross-env NODE_ENV_ODIT=production node template-copy.js && yarn build:sw", | ||||
| 		"build:sw": "workbox generateSW workbox-config.js", | ||||
| 		"release": "release-it", | ||||
| 		"changelog": "npx auto-changelog --commit-limit false --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs", | ||||
| 		"licenses:export": "license-exporter --json -o public" | ||||
| 	}, | ||||
| 	"dependencies": { | ||||
| 		"@odit/lfk-client-js": "0.0.10", | ||||
| 		"@odit/lfk-client-js": "0.3.0", | ||||
| 		"csvtojson": "^2.0.10", | ||||
| 		"filepond": "4.25.1", | ||||
| 		"gridjs": "3.2.1", | ||||
| 		"gridjs": "3.2.2", | ||||
| 		"localforage": "1.9.0", | ||||
| 		"lodash.isequal": "^4.5.0", | ||||
| 		"svelte-filepond": "0.0.1", | ||||
| 		"svelte-focus-trap": "1.0.1", | ||||
| 		"svelte-i18n": "3.3.0", | ||||
| 		"tailwindcss": "2.0.2", | ||||
| 		"tinro": "0.5.6", | ||||
| 		"tinro": "0.5.9", | ||||
| 		"toastify-js": "1.9.3", | ||||
| 		"validator": "13.5.2" | ||||
| 		"validator": "13.5.2", | ||||
| 		"xlsx": "^0.16.9" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"@odit/license-exporter": "0.0.9", | ||||
| 		"@snowpack/plugin-svelte": "3.4.1", | ||||
| 		"@snowpack/plugin-svelte": "3.5.2", | ||||
| 		"auto-changelog": "^2.2.1", | ||||
| 		"autoprefixer": "10.2.1", | ||||
| 		"autoprefixer": "10.2.3", | ||||
| 		"cross-env": "^7.0.3", | ||||
| 		"postcss": "8.2.4", | ||||
| 		"postcss-load-config": "3.0.0", | ||||
| 		"release-it": "^14.2.2", | ||||
| 		"snowpack": "3.0.0-rc.2", | ||||
| 		"svelte": "3.31.2", | ||||
| 		"svelte-preprocess": "4.6.1", | ||||
| 		"snowpack": "3.0.11", | ||||
| 		"svelte": "3.32.0", | ||||
| 		"svelte-preprocess": "4.6.3", | ||||
| 		"workbox-cli": "6.0.2" | ||||
| 	}, | ||||
| 	"release-it": { | ||||
| @@ -51,7 +55,7 @@ | ||||
| 			"publish": false | ||||
| 		}, | ||||
| 		"hooks": { | ||||
| 			"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js  && git add public/index.html" | ||||
| 			"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js  && git add index.template.html && node order.js  && git add src/locales" | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1,11 +1,18 @@ | ||||
| { | ||||
| 	"name": "Lauf für Kaya! - Admin", | ||||
| 	"short_name": "LfK!Admin", | ||||
| 	"start_url": ".", | ||||
| 	"start_url": "/?utm_source=pwa", | ||||
| 	"orientation": "portrait-primary", | ||||
| 	"display": "standalone", | ||||
| 	"background_color": "#fff", | ||||
| 	"theme_color": "#fff", | ||||
| 	"description": "Lauf für Kaya! - Admin", | ||||
| 	"shortcuts": [ | ||||
| 		{ | ||||
| 			"name": "Users", | ||||
| 			"url": "/users/?utm_source=pwa" | ||||
| 		} | ||||
| 	], | ||||
| 	"icons": [ | ||||
| 		{ | ||||
| 			"src": "/favicon.png", | ||||
|   | ||||
| @@ -1,2 +0,0 @@ | ||||
| if(!self.define){const e=e=>{"require"!==e&&(e+=".js");let r=Promise.resolve();return i[e]||(r=new Promise((async r=>{if("document"in self){const i=document.createElement("script");i.src=e,document.head.appendChild(i),i.onload=r}else importScripts(e),r()}))),r.then((()=>{if(!i[e])throw new Error(`Module ${e} didn’t register its module`);return i[e]}))},r=(r,i)=>{Promise.all(r.map(e)).then((e=>i(1===e.length?e[0]:e)))},i={require:Promise.resolve(r)};self.define=(r,s,o)=>{i[r]||(i[r]=Promise.resolve().then((()=>{let i={};const c={uri:location.origin+r.slice(1)};return Promise.all(s.map((r=>{switch(r){case"exports":return i;case"module":return c;default:return e(r)}}))).then((e=>{const r=o(...e);return i.default||(i.default=r),i}))})))}}define("./sw.js",["./workbox-c8ead010"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.precacheAndRoute([{url:"favicon.ico",revision:"ba44f340afba5bb1a07f14decc15dd04"},{url:"favicon.png",revision:"07a9941cec62319578fa2a1734db9959"},{url:"favicon.svg",revision:"689d6c6fda51e359c0e5725d9e905064"},{url:"index.html",revision:"931c34f3675364dcc09411aa0f223776"},{url:"logo.svg",revision:"4c9e31a1f4268d7e36e22cda7656e561"},{url:"manifest.webmanifest",revision:"75c93eb352c4877216e77b1d7f73445f"},{url:"robots.txt",revision:"61c27d2cd39a713f7829422c3d9edcc7"}],{})})); | ||||
| //# sourceMappingURL=sw.js.map | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								s-config.template.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								s-config.template.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| const sveltePreprocess = require('svelte-preprocess'); | ||||
| const preprocess = sveltePreprocess(__insert__); | ||||
|  | ||||
| module.exports = { | ||||
| 	preprocess | ||||
| }; | ||||
| @@ -5,10 +5,11 @@ module.exports = { | ||||
| 		src: '/_dist_' | ||||
| 	}, | ||||
| 	plugins: [ '@snowpack/plugin-svelte' ], | ||||
| 	install: [ | ||||
| 		/* ... */ | ||||
| 	routes: [ | ||||
| 		/* Enable an SPA Fallback in development: */ | ||||
| 		{ match: 'routes', src: '.*', dest: '/index.html' } | ||||
| 	], | ||||
| 	installOptions: { | ||||
| 	packageOptions: { | ||||
| 		/* ... */ | ||||
| 		sourceMap: false | ||||
| 	}, | ||||
| @@ -18,13 +19,8 @@ module.exports = { | ||||
| 	buildOptions: { | ||||
| 		/* ... */ | ||||
| 	}, | ||||
| 	proxy: { | ||||
| 		/* ... */ | ||||
| 	}, | ||||
| 	alias: { | ||||
| 		/* ... */ | ||||
| 	}, | ||||
| 	experiments: { | ||||
| 		optimize: { bundle: true, minify: true } | ||||
| 	} | ||||
| 	optimize: { bundle: true, minify: true } | ||||
| }; | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| <script> | ||||
|   import "./TailwindStyles.svelte"; | ||||
|   import "toastify-js/src/toastify.css"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   import { Route, router } from "tinro"; | ||||
|   router.subscribe((routeInfo) => { | ||||
|     window.scrollTo(0, 0); | ||||
| @@ -30,7 +31,6 @@ | ||||
|   import Login from "./components/Login.svelte"; | ||||
|   import Dashboard from "./components/Dashboard.svelte"; | ||||
|   import store from "./store.js"; | ||||
|   import NotFound from "./components/NotFound.svelte"; | ||||
|   import ForgotPassword from "./components/ForgotPassword.svelte"; | ||||
|   import MainDashContent from "./components/MainDashContent.svelte"; | ||||
|   import Users from "./components/Users.svelte"; | ||||
| @@ -40,16 +40,18 @@ | ||||
|   import Orgs from "./components/Orgs.svelte"; | ||||
|   import Runners from "./components/Runners.svelte"; | ||||
|   import Footer from "./components/Footer.svelte"; | ||||
|   import Tracks from "./components/Tracks.svelte"; | ||||
|   import TracksOverview from "./components/TracksOverview.svelte"; | ||||
|   import OrgDetail from "./components/OrgDetail.svelte"; | ||||
|   import Teams from "./components/Teams.svelte"; | ||||
|   import { OpenAPI, AuthService } from "@odit/lfk-client-js"; | ||||
|   import { OpenAPI } from "@odit/lfk-client-js"; | ||||
|   import UserDetail from "./components/UserDetail.svelte"; | ||||
|   OpenAPI.BASE = config.baseurl; | ||||
|   import { register as registerSW } from "./swmodule"; | ||||
|   import TeamDetail from "./components/TeamDetail.svelte"; | ||||
|   import UserPermissions from "./components/UserPermissions.svelte"; | ||||
|   import RunnerDetail from "./components/RunnerDetail.svelte"; | ||||
|   store.init(); | ||||
|   // registerSW(); | ||||
|   registerSW(); | ||||
| </script> | ||||
|  | ||||
| <Route> | ||||
| @@ -71,8 +73,13 @@ | ||||
|           <Route path="/"> | ||||
|             <Users /> | ||||
|           </Route> | ||||
|           <Route path="/:userid" let:params> | ||||
|             <UserDetail {params} /> | ||||
|           <Route path="/:userid/*" let:params> | ||||
|             <Route path="/"> | ||||
|               <UserDetail {params} /> | ||||
|             </Route> | ||||
|             <Route path="/permissions/"> | ||||
|               <UserPermissions {params} /> | ||||
|             </Route> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/tracks/*"> | ||||
| @@ -81,11 +88,21 @@ | ||||
|           </Route> | ||||
|           <Route path="/:trackid" let:params /> | ||||
|         </Route> | ||||
|         <Route path="/runners"> | ||||
|           <Runners /> | ||||
|         <Route path="/runners/*"> | ||||
|           <Route path="/"> | ||||
|             <Runners /> | ||||
|           </Route> | ||||
|           <Route path="/:runnerid" let:params> | ||||
|             <RunnerDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/teams"> | ||||
|           <Teams /> | ||||
|         <Route path="/teams/*"> | ||||
|           <Route path="/"> | ||||
|             <Teams /> | ||||
|           </Route> | ||||
|           <Route path="/:teamid" let:params> | ||||
|             <TeamDetail {params} /> | ||||
|           </Route> | ||||
|         </Route> | ||||
|         <Route path="/orgs/*"> | ||||
|           <Route path="/"> | ||||
|   | ||||
| @@ -163,7 +163,35 @@ | ||||
|         </div> | ||||
|       {/await} | ||||
|     </div> | ||||
|     <h2 class="text-4xl font-display font-semibold md:text-5xl">{$_('faq')}</h2> | ||||
|     <div class="w-full leading-8 mt-8"> | ||||
|       <p class="text-xl font-medium">{$_('icon-image-credits')}</p> | ||||
|       <ul class="list-disc"> | ||||
|         <li> | ||||
|           <a | ||||
|             class="underline" | ||||
|             target="_blank" | ||||
|             rel="noopener noreferrer" | ||||
|             href="https://storyset.com">https://storyset.com</a> | ||||
|         </li> | ||||
|         <li> | ||||
|           <a | ||||
|             class="underline" | ||||
|             target="_blank" | ||||
|             rel="noopener noreferrer" | ||||
|             href="https://undraw.co">https://undraw.co</a> | ||||
|         </li> | ||||
|         <li> | ||||
|           <a | ||||
|             class="underline" | ||||
|             target="_blank" | ||||
|             rel="noopener noreferrer" | ||||
|             href="https://remixicon.com">https://remixicon.com</a> | ||||
|         </li> | ||||
|       </ul> | ||||
|     </div> | ||||
|     <h2 class="mt-4 text-4xl font-display font-semibold md:text-5xl"> | ||||
|       {$_('faq')} | ||||
|     </h2> | ||||
|     <div class="mt-6 border-t-2 border-gray-100 pt-10"> | ||||
|       <dl class="md:grid md:grid-cols-2 md:gap-8"> | ||||
|         <div> | ||||
|   | ||||
							
								
								
									
										162
									
								
								src/components/AddOrgModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/components/AddOrgModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import { RunnerOrganizationService } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   export let modal_open; | ||||
|   export let current_organizations; | ||||
|   let name_input_dom; | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: name = ""; | ||||
|   $: processed_last_submit = true; | ||||
|   $: isOrgnameValid = name.trim().length !== 0; | ||||
|   $: createbtnenabled = isOrgnameValid; | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       const toast = Toastify({ | ||||
|         text: "Organization is being added...", | ||||
|         duration: -1, | ||||
|       }).showToast(); | ||||
|       RunnerOrganizationService.runnerOrganizationControllerPost({ | ||||
|         name, | ||||
|         address: undefined, | ||||
|         contact: undefined, | ||||
|       }) | ||||
|         .then((result) => { | ||||
|           console.log(result); | ||||
|           name = ""; | ||||
|           modal_open = false; | ||||
|           // | ||||
|           Toastify({ | ||||
|             text: "Organization added", | ||||
|             duration: 500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|           current_organizations = current_organizations.concat([result]); | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|           // | ||||
|           toast.hideToast(); | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24"><path | ||||
|                   d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 Create a new Organization | ||||
|               </h3> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   Please provide the required information to add a new | ||||
|                   organization. | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-6"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="firstname" | ||||
|                     class="block text-sm font-medium text-gray-700">Name</label> | ||||
|                   <input | ||||
|                     use:focus | ||||
|                     autocomplete="off" | ||||
|                     placeholder="Name" | ||||
|                     class:border-red-500={!isOrgnameValid} | ||||
|                     class:focus:border-red-500={!isOrgnameValid} | ||||
|                     class:focus:ring-red-500={!isOrgnameValid} | ||||
|                     bind:value={name} | ||||
|                     bind:this={name_input_dom} | ||||
|                     type="text" | ||||
|                     name="firstname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if !isOrgnameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       Organization name is required | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('create')} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('cancel')} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										312
									
								
								src/components/AddRunnerModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								src/components/AddRunnerModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,312 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import { | ||||
|     RunnerService, | ||||
|     RunnerTeamService, | ||||
|     RunnerOrganizationService, | ||||
|   } from "@odit/lfk-client-js"; | ||||
|   import isEmail from "validator/es/lib/isEmail"; | ||||
|   import isMobilePhone from "validator/es/lib/isMobilePhone"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   export let modal_open; | ||||
|   export let current_runners; | ||||
|   $: selected_team = undefined; | ||||
|   let firstname_input; | ||||
|   let lastname_input; | ||||
|   let middlename_input; | ||||
|   let phone_input; | ||||
|   let email_input; | ||||
|   let teams = []; | ||||
|   RunnerTeamService.runnerTeamControllerGetAll().then((val) => { | ||||
|     teams = val; | ||||
|   }); | ||||
|   let orgs = []; | ||||
|   RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => { | ||||
|     orgs = val; | ||||
|   }); | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: middlename_input_value = ""; | ||||
|   $: phone_input_value = ""; | ||||
|   $: email_input_value = ""; | ||||
|   $: lastname_input_value = ""; | ||||
|   $: firstname_input_value = ""; | ||||
|   $: processed_last_submit = true; | ||||
|   $: isPhoneValidOrEmpty = | ||||
|     isMobilePhone( | ||||
|       phone_input_value | ||||
|         .replaceAll("(", "") | ||||
|         .replaceAll(")", "") | ||||
|         .replaceAll("-", "") | ||||
|         .replaceAll(" ", "") | ||||
|     ) || phone_input_value === ""; | ||||
|   $: isEmailValidOrEmpty = | ||||
|     isEmail(email_input_value) || email_input_value === ""; | ||||
|   $: isLastnameValid = lastname_input_value.trim().length !== 0; | ||||
|   $: isFirstnameValid = firstname_input_value.trim().length !== 0; | ||||
|   $: createbtnenabled = | ||||
|     isFirstnameValid && | ||||
|     isLastnameValid && | ||||
|     isEmailValidOrEmpty && | ||||
|     isPhoneValidOrEmpty; | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       const toast = Toastify({ | ||||
|         text: "Runner is being added...", | ||||
|         duration: -1, | ||||
|       }).showToast(); | ||||
|       let postdata = { | ||||
|         group: selected_team, | ||||
|         firstname: firstname_input_value, | ||||
|         lastname: lastname_input_value, | ||||
|       }; | ||||
|       if (middlename_input_value) { | ||||
|         postdata.middlename = middlename_input_value; | ||||
|       } | ||||
|       if (phone_input_value) { | ||||
|         postdata.phone = phone_input_value; | ||||
|       } | ||||
|       if (email_input_value) { | ||||
|         postdata.email = email_input_value; | ||||
|       } | ||||
|       RunnerService.runnerControllerPost(postdata) | ||||
|         .then((result) => { | ||||
|           firstname_input_value = ""; | ||||
|           lastname_input_value = ""; | ||||
|           middlename_input_value = ""; | ||||
|           email_input_value = ""; | ||||
|           modal_open = false; | ||||
|           // | ||||
|           Toastify({ | ||||
|             text: "Runner added", | ||||
|             duration: 500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|           current_runners.push(result); | ||||
|           current_runners = current_runners; | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|           // | ||||
|           toast.hideToast(); | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 {$_('create-a-new-runner')} | ||||
|               </h3> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   {$_('please-provide-the-required-information-to-add-a-new-runner')} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-6"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="firstname" | ||||
|                     class="block text-sm font-medium text-gray-700">{$_('first-name')}</label> | ||||
|                   <input | ||||
|                     use:focus | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_('first-name')} | ||||
|                     class:border-red-500={!isFirstnameValid} | ||||
|                     class:focus:border-red-500={!isFirstnameValid} | ||||
|                     class:focus:ring-red-500={!isFirstnameValid} | ||||
|                     bind:value={firstname_input_value} | ||||
|                     bind:this={firstname_input} | ||||
|                     type="text" | ||||
|                     name="firstname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if !isFirstnameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       {$_('first-name-is-required')} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="trackname" | ||||
|                     class="block text-sm font-medium text-gray-700">{$_('middle-name')}</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_('middle-name')} | ||||
|                     bind:value={middlename_input_value} | ||||
|                     bind:this={middlename_input} | ||||
|                     type="text" | ||||
|                     name="trackname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="lastname" | ||||
|                     class="block text-sm font-medium text-gray-700">Last Name</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder="Last Name" | ||||
|                     class:border-red-500={!isLastnameValid} | ||||
|                     class:focus:border-red-500={!isLastnameValid} | ||||
|                     class:focus:ring-red-500={!isLastnameValid} | ||||
|                     bind:value={lastname_input_value} | ||||
|                     bind:this={lastname_input} | ||||
|                     type="text" | ||||
|                     name="lastname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if !isLastnameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       {$_('last-name-is-required')} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="team" | ||||
|                     class="block text-sm font-medium text-gray-700">{$_('team')}</label> | ||||
|                   <select | ||||
|                     name="team" | ||||
|                     bind:value={selected_team} | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> | ||||
|                     {#each teams as team} | ||||
|                       <option value={team.id}> | ||||
|                         {team.parentGroup.name} | ||||
|                         > | ||||
|                         {team.name} | ||||
|                       </option> | ||||
|                     {/each} | ||||
|                     {#each orgs as org} | ||||
|                       <option value={org.id}>{org.name}</option> | ||||
|                     {/each} | ||||
|                   </select> | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="phone" | ||||
|                     class="block text-sm font-medium text-gray-700">Phone</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder="Phone" | ||||
|                     class:border-red-500={!isPhoneValidOrEmpty} | ||||
|                     class:focus:border-red-500={!isPhoneValidOrEmpty} | ||||
|                     class:focus:ring-red-500={!isPhoneValidOrEmpty} | ||||
|                     bind:value={phone_input_value} | ||||
|                     bind:this={phone_input} | ||||
|                     type="tel" | ||||
|                     name="phone" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if !isPhoneValidOrEmpty} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       {$_('the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number')} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="email" | ||||
|                     class="block text-sm font-medium text-gray-700">{$_('e-mail-adress')}</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_('e-mail-adress')} | ||||
|                     class:border-red-500={!isEmailValidOrEmpty} | ||||
|                     class:focus:border-red-500={!isEmailValidOrEmpty} | ||||
|                     class:focus:ring-red-500={!isEmailValidOrEmpty} | ||||
|                     bind:value={email_input_value} | ||||
|                     bind:this={email_input} | ||||
|                     type="email" | ||||
|                     name="email" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if !isEmailValidOrEmpty} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       {$_('valid-email-is-required')} | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('create')} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('cancel')} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										181
									
								
								src/components/AddTeamModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								src/components/AddTeamModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import { | ||||
|     RunnerOrganizationService, | ||||
|     RunnerTeamService, | ||||
|   } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   export let modal_open; | ||||
|   export let current_teams; | ||||
|   let teamname_input_dom; | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: teamname = ""; | ||||
|   $: processed_last_submit = true; | ||||
|   $: isTeamNameValid = teamname.trim().length !== 0; | ||||
|   $: createbtnenabled = isTeamNameValid; | ||||
|   (function () { | ||||
|     document.onkeydown = function (e) { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         if (createbtnenabled === true) { | ||||
|           createbtnenabled = false; | ||||
|           submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   $: parentGroup = undefined; | ||||
|   $: orgs = []; | ||||
|   RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => { | ||||
|     orgs = val; | ||||
|   }); | ||||
|   function submit() { | ||||
|     if (processed_last_submit === true) { | ||||
|       processed_last_submit = false; | ||||
|       const toast = Toastify({ | ||||
|         text: "Team is being added...", | ||||
|         duration: -1, | ||||
|       }).showToast(); | ||||
|       RunnerTeamService.runnerTeamControllerPost({ | ||||
|         parentGroup, | ||||
|         name: teamname, | ||||
|       }) | ||||
|         .then((result) => { | ||||
|           teamname = ""; | ||||
|           modal_open = false; | ||||
|           // | ||||
|           Toastify({ | ||||
|             text: "Team added", | ||||
|             duration: 500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|           current_teams.push(result); | ||||
|           current_teams = current_teams; | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
|         }) | ||||
|         .finally(() => { | ||||
|           processed_last_submit = true; | ||||
|           // | ||||
|           toast.hideToast(); | ||||
|         }); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       modal_open = false; | ||||
|     }}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 Create a new team | ||||
|               </h3> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   Please provide the required information to add a new team. | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="grid grid-cols-6 gap-6"> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="firstname" | ||||
|                     class="block text-sm font-medium text-gray-700">{$_('team-name')}</label> | ||||
|                   <input | ||||
|                     use:focus | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_('team-name')} | ||||
|                     class:border-red-500={!isTeamNameValid} | ||||
|                     class:focus:border-red-500={!isTeamNameValid} | ||||
|                     class:focus:ring-red-500={!isTeamNameValid} | ||||
|                     bind:value={teamname} | ||||
|                     bind:this={teamname_input_dom} | ||||
|                     type="text" | ||||
|                     name="firstname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                   {#if !isTeamNameValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       team name is required | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="firstname" | ||||
|                     class="block text-sm font-medium text-gray-700">{$_('organization')}</label> | ||||
|                   <select | ||||
|                     bind:value={parentGroup} | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> | ||||
|                     {#each orgs as t} | ||||
|                       <option value={t.id}>{t.name}</option> | ||||
|                     {/each} | ||||
|                   </select> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | ||||
|           <button | ||||
|             disabled={!createbtnenabled} | ||||
|             class:opacity-50={!createbtnenabled} | ||||
|             on:click={submit} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('create')} | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               modal_open = false; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('cancel')} | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
| @@ -2,20 +2,21 @@ | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import { tracks as tracksstore } from "../store.js"; | ||||
|   import { UserService } from "@odit/lfk-client-js"; | ||||
|   import isEmail from "validator/es/lib/isEmail"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import "toastify-js/src/toastify.css"; | ||||
|   export let modal_open; | ||||
|   export let current_users; | ||||
|   let firstname_input; | ||||
|   let lastname_input; | ||||
|   let middlename_input; | ||||
|   let username_input; | ||||
|   let password_input; | ||||
|   let email_input; | ||||
|   function focus(el) { | ||||
|     el.focus(); | ||||
|   } | ||||
|   $: username_input_value = ""; | ||||
|   $: middlename_input_value = ""; | ||||
|   $: password_input_value = ""; | ||||
|   $: email_input_value = ""; | ||||
| @@ -27,7 +28,10 @@ | ||||
|   $: isLastnameValid = lastname_input_value.trim().length !== 0; | ||||
|   $: isFirstnameValid = firstname_input_value.trim().length !== 0; | ||||
|   $: createbtnenabled = | ||||
|     isFirstnameValid && isLastnameValid && isEmailValid && isPasswordValid; | ||||
|     isFirstnameValid && | ||||
|     isLastnameValid && | ||||
|     isPasswordValid && | ||||
|     !(!isEmailValid && username_input_value.trim().length === 0); | ||||
|   (function () { | ||||
|     document.onkeydown = function (e) { | ||||
|       e = e || window.event; | ||||
| @@ -53,13 +57,16 @@ | ||||
|         firstname: firstname_input_value, | ||||
|         lastname: lastname_input_value, | ||||
|         middlename: middlename_input_value, | ||||
|         email: email_input_value, | ||||
|         password: password_input_value, | ||||
|         email: email_input_value, | ||||
|         username: username_input_value, | ||||
|       }) | ||||
|         .then((result) => { | ||||
|           firstname_input_value = ""; | ||||
|           lastname_input_value = ""; | ||||
|           middlename_input_value = ""; | ||||
|           email_input_value = ""; | ||||
|           username_input_value = ""; | ||||
|           modal_open = false; | ||||
|           // | ||||
|           Toastify({ | ||||
| @@ -67,12 +74,8 @@ | ||||
|             duration: 500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|           let storeval = []; | ||||
|           tracksstore.subscribe((val) => { | ||||
|             storeval = val; | ||||
|           }); | ||||
|           storeval.push(result); | ||||
|           tracksstore.set(storeval); | ||||
|           current_users.push(result); | ||||
|           current_users = current_users; | ||||
|         }) | ||||
|         .catch((err) => { | ||||
|           // | ||||
| @@ -213,6 +216,19 @@ | ||||
|                     </span> | ||||
|                   {/if} | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="trackname" | ||||
|                     class="block text-sm font-medium text-gray-700">{$_('username')}</label> | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_('username')} | ||||
|                     bind:value={username_input_value} | ||||
|                     bind:this={username_input} | ||||
|                     type="text" | ||||
|                     name="trackname" | ||||
|                     class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|                 </div> | ||||
|                 <div class="col-span-6"> | ||||
|                   <label | ||||
|                     for="email" | ||||
| @@ -220,18 +236,21 @@ | ||||
|                   <input | ||||
|                     autocomplete="off" | ||||
|                     placeholder={$_('e-mail-adress')} | ||||
|                     class:border-red-500={!isEmailValid} | ||||
|                     class:focus:border-red-500={!isEmailValid} | ||||
|                     class:focus:ring-red-500={!isEmailValid} | ||||
|                     bind:value={email_input_value} | ||||
|                     bind:this={email_input} | ||||
|                     type="email" | ||||
|                     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} | ||||
|                   <!-- {#if !isEmailValid} | ||||
|                     <span | ||||
|                       class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|                       {$_('valid-email-is-required')} | ||||
|                       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} | ||||
|                 </div> | ||||
|   | ||||
| @@ -30,7 +30,7 @@ | ||||
|   <Table /> | ||||
| </div> | ||||
| <div | ||||
|   class="widget w-full p-4 mb-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|   class="widget w-full p-4 mb-4 rounded-lg bg-white border border-grey-100"> | ||||
|   <div class="flex flex-row items-center justify-between mb-6"> | ||||
|     <div class="flex flex-col"> | ||||
|       <div class="text-sm font-light text-grey-500">Regular</div> | ||||
|   | ||||
							
								
								
									
										100
									
								
								src/components/ConfirmOrgDeletion.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/components/ConfirmOrgDeletion.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import { RunnerOrganizationService } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   export let modal_open; | ||||
|   export let delete_org; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   function cancelDelete() { | ||||
|     modal_open = false; | ||||
|     dispatch("cancelDelete", { id: delete_org.id }); | ||||
|   } | ||||
|   function deleteOrg() { | ||||
|     RunnerOrganizationService.runnerOrganizationControllerRemove( | ||||
|       delete_org.id, | ||||
|       true | ||||
|     ) | ||||
|       .then((resp) => { | ||||
|         Toastify({ | ||||
|           text: "Organization deleted", | ||||
|           duration: 500, | ||||
|           backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|         }).showToast(); | ||||
|         location.replace("./"); | ||||
|       }) | ||||
|       .catch((err) => { | ||||
|         // | ||||
|       }); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={cancelDelete}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 Attention! | ||||
|               </h3> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   Do you want to delete the organization | ||||
|                   {delete_org.name}?<br />All associated teams and runners will | ||||
|                   be deleted too! | ||||
|                 </p> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | ||||
|           <button | ||||
|             on:click={deleteOrg} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             Confirm, delete organization and associated teams+runners. | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={cancelDelete} | ||||
|             type="button" | ||||
|             class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             Cancel, keep organization | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
							
								
								
									
										97
									
								
								src/components/ConfirmTeamDeletion.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/components/ConfirmTeamDeletion.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import { RunnerTeamService } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   export let modal_open; | ||||
|   export let delete_team; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   function cancelDelete() { | ||||
|     modal_open = false; | ||||
|     dispatch("cancelDelete", { id: delete_team.id }); | ||||
|   } | ||||
|   function deleteTeam() { | ||||
|     RunnerTeamService.runnerTeamControllerRemove(delete_team.id, true) | ||||
|       .then((resp) => { | ||||
|         Toastify({ | ||||
|           text: "Team deleted", | ||||
|           duration: 500, | ||||
|           backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|         }).showToast(); | ||||
|         location.replace("./"); | ||||
|       }) | ||||
|       .catch((err) => { | ||||
|         // | ||||
|       }); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={cancelDelete}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> | ||||
|               <h3 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 Attention! | ||||
|               </h3> | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   Do you want to delete the team | ||||
|                   {delete_team.name}?<br />All associated runners will be | ||||
|                   deleted too! | ||||
|                 </p> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> | ||||
|           <button | ||||
|             on:click={deleteTeam} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             Confirm, delete team and associated runners. | ||||
|           </button> | ||||
|           <button | ||||
|             on:click={cancelDelete} | ||||
|             type="button" | ||||
|             class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             Cancel, keep team | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
| @@ -1,4 +1,4 @@ | ||||
| <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
| <div class="w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|   <div class="flex flex-row items-center justify-between mb-6"> | ||||
|     <div class="flex flex-col"> | ||||
|       <div class="text-sm font-light text-grey-500">Conversions</div> | ||||
| @@ -18,16 +18,16 @@ | ||||
|           <div class="flex flex-col w-full"> | ||||
|             <ul class="list-none"> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                   href="/">Today</a></li> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                   href="/">This week</a></li> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                   href="/">This month</a></li> | ||||
|               <li><a | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                   class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                   href="/">This year</a></li> | ||||
|             </ul> | ||||
|           </div> | ||||
|   | ||||
| @@ -323,7 +323,7 @@ | ||||
|       </li> | ||||
|     </ul> | ||||
|   </div> | ||||
|   <div class="main w-full bg-grey-50 text-grey-900 dark:bg-grey-900 dark:text-white"> | ||||
|   <div class="main w-full bg-grey-50 text-grey-900"> | ||||
|     <div class="navbar navbar-1 border-b"> | ||||
|       <div class="navbar-inner w-full flex items-center justify-start"><button class="mx-4"><svg stroke="currentColor" | ||||
|             fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" size="20" | ||||
| @@ -733,7 +733,7 @@ | ||||
|       <div class="flex flex-col lg:flex-row w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4"> | ||||
|         <div class="w-full lg:w-1/4"> | ||||
|           <div | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-xs uppercase font-light text-grey-500">Users</div> | ||||
| @@ -751,7 +751,7 @@ | ||||
|         </div> | ||||
|         <div class="w-full lg:w-1/4"> | ||||
|           <div | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-xs uppercase font-light text-grey-500">Sessions</div> | ||||
| @@ -766,7 +766,7 @@ | ||||
|         </div> | ||||
|         <div class="w-full lg:w-1/4"> | ||||
|           <div | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-xs uppercase font-light text-grey-500">Bounce rate</div> | ||||
| @@ -783,7 +783,7 @@ | ||||
|         </div> | ||||
|         <div class="w-full lg:w-1/4"> | ||||
|           <div | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|             class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-xs uppercase font-light text-grey-500">Session duration</div> | ||||
| @@ -803,7 +803,7 @@ | ||||
|           <ConversionsChart /> | ||||
|         </div> | ||||
|         <div class="w-full lg:w-1/3"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between mb-6"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-sm font-light text-grey-500">Sessions</div> | ||||
| @@ -823,16 +823,16 @@ | ||||
|                     <div class="flex flex-col w-full"> | ||||
|                       <ul class="list-none"> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">Today</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This week</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This month</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This year</a></li> | ||||
|                       </ul> | ||||
|                     </div> | ||||
| @@ -923,7 +923,7 @@ | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4"> | ||||
|         <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|         <div class="w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|           <div class="flex flex-row items-center justify-between mb-6"> | ||||
|             <div class="flex flex-col"> | ||||
|               <div class="text-sm font-light text-grey-500">Users</div> | ||||
| @@ -1211,15 +1211,15 @@ | ||||
|               </table> | ||||
|               <div class="flex flex-row w-full items-center justify-around lg:justify-between my-4"> | ||||
|                 <div class="flex flex-wrap items-center justify-start space-x-2 pagination"><button | ||||
|                     class="btn btn-default bg-transparent hover:bg-grey-200 text-grey-900 dark:text-white">Next</button><button | ||||
|                     class="btn btn-default bg-transparent hover:bg-grey-200 text-grey-900 dark:text-white">Last</button> | ||||
|                     class="btn btn-default bg-transparent hover:bg-grey-200 text-grey-900">Next</button><button | ||||
|                     class="btn btn-default bg-transparent hover:bg-grey-200 text-grey-900">Last</button> | ||||
|                 </div><span class="hidden lg:block">Page | ||||
|                   <!-- --> <b>1 | ||||
|                     <!-- --> of | ||||
|                     <!-- -->11 | ||||
|                   </b> | ||||
|                 </span><select | ||||
|                   class="hidden lg:block form-select text-sm bg-white dark:bg-grey-800 dark:border-grey-800 outline-none shadow-none focus:shadow-none"> | ||||
|                   class="hidden lg:block form-select text-sm bg-white outline-none shadow-none focus:shadow-none"> | ||||
|                   <option selected="" value="10">Show 10</option> | ||||
|                   <option value="25">Show 25</option> | ||||
|                   <option value="50">Show 50</option> | ||||
| @@ -1232,7 +1232,7 @@ | ||||
|       </div> | ||||
|       <div class="flex flex-col lg:flex-row w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4"> | ||||
|         <div class="w-full lg:w-1/2"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between mb-6"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-sm font-light text-grey-500">Project status</div> | ||||
| @@ -1252,16 +1252,16 @@ | ||||
|                     <div class="flex flex-col w-full"> | ||||
|                       <ul class="list-none"> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">Today</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This week</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This month</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This year</a></li> | ||||
|                       </ul> | ||||
|                     </div> | ||||
| @@ -1336,7 +1336,7 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="w-full lg:w-1/2"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between mb-6"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-sm font-light text-grey-500">Sales</div> | ||||
| @@ -1356,16 +1356,16 @@ | ||||
|                     <div class="flex flex-col w-full"> | ||||
|                       <ul class="list-none"> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">Today</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This week</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This month</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This year</a></li> | ||||
|                       </ul> | ||||
|                     </div> | ||||
| @@ -1578,7 +1578,7 @@ | ||||
|       </div> | ||||
|       <div class="flex flex-col lg:flex-row w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4"> | ||||
|         <div class="w-full lg:w-1/3"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between mb-6"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-sm font-light text-grey-500">Activities</div> | ||||
| @@ -1598,16 +1598,16 @@ | ||||
|                     <div class="flex flex-col w-full"> | ||||
|                       <ul class="list-none"> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">Today</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This week</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This month</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This year</a></li> | ||||
|                       </ul> | ||||
|                     </div> | ||||
| @@ -1619,7 +1619,7 @@ | ||||
|               <div class="flex flex-col w-full"> | ||||
|                 <div class="flex relative justify-start items-start"> | ||||
|                   <div class="h-full w-6 absolute inset-0 flex items-center justify-center"> | ||||
|                     <div class="h-full w-1 bg-grey-200 dark:bg-grey-800 pointer-events-none"></div> | ||||
|                     <div class="h-full w-1 bg-grey-200 pointer-events-none"></div> | ||||
|                   </div> | ||||
|                   <div | ||||
|                     class="flex-shrink-0 w-6 h-6 rounded-full inline-flex items-center justify-center bg-blue-500 text-white relative z-10 font-medium text-sm"> | ||||
| @@ -1636,7 +1636,7 @@ | ||||
|                 </div> | ||||
|                 <div class="flex relative justify-start items-start"> | ||||
|                   <div class="h-full w-6 absolute inset-0 flex items-center justify-center"> | ||||
|                     <div class="h-full w-1 bg-grey-200 dark:bg-grey-800 pointer-events-none"></div> | ||||
|                     <div class="h-full w-1 bg-grey-200 pointer-events-none"></div> | ||||
|                   </div> | ||||
|                   <div | ||||
|                     class="flex-shrink-0 w-6 h-6 rounded-full inline-flex items-center justify-center bg-blue-500 text-white relative z-10 font-medium text-sm"> | ||||
| @@ -1653,7 +1653,7 @@ | ||||
|                 </div> | ||||
|                 <div class="flex relative justify-start items-start"> | ||||
|                   <div class="h-full w-6 absolute inset-0 flex items-center justify-center"> | ||||
|                     <div class="h-full w-1 bg-grey-200 dark:bg-grey-800 pointer-events-none"></div> | ||||
|                     <div class="h-full w-1 bg-grey-200 pointer-events-none"></div> | ||||
|                   </div> | ||||
|                   <div | ||||
|                     class="flex-shrink-0 w-6 h-6 rounded-full inline-flex items-center justify-center bg-blue-500 text-white relative z-10 font-medium text-sm"> | ||||
| @@ -1670,7 +1670,7 @@ | ||||
|                 </div> | ||||
|                 <div class="flex relative justify-start items-start"> | ||||
|                   <div class="h-full w-6 absolute inset-0 flex items-center justify-center"> | ||||
|                     <div class="h-full w-1 bg-grey-200 dark:bg-grey-800 pointer-events-none"></div> | ||||
|                     <div class="h-full w-1 bg-grey-200 pointer-events-none"></div> | ||||
|                   </div> | ||||
|                   <div | ||||
|                     class="flex-shrink-0 w-6 h-6 rounded-full inline-flex items-center justify-center bg-blue-500 text-white relative z-10 font-medium text-sm"> | ||||
| @@ -1690,7 +1690,7 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="w-full lg:w-2/3"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|           <div class="w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|             <div class="flex flex-row items-center justify-between mb-6"> | ||||
|               <div class="flex flex-col"> | ||||
|                 <div class="text-sm font-light text-grey-500">To do</div> | ||||
| @@ -1710,16 +1710,16 @@ | ||||
|                     <div class="flex flex-col w-full"> | ||||
|                       <ul class="list-none"> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">Today</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This week</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This month</a></li> | ||||
|                         <li><a | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890" | ||||
|                             class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100" | ||||
|                             href="/">This year</a></li> | ||||
|                       </ul> | ||||
|                     </div> | ||||
| @@ -1831,7 +1831,7 @@ | ||||
|                 </div> | ||||
|                 <form> | ||||
|                   <div class="form-element"><input type="text" value="" name="todo" | ||||
|                       class="form-input text-sm bg-white dark:bg-grey-800 dark:border-grey-800" | ||||
|                       class="form-input text-sm bg-white" | ||||
|                       placeholder="Add new..."></div> | ||||
|                 </form> | ||||
|                 <div class="flex flex-row items-center justify-between"> | ||||
|   | ||||
| @@ -1,52 +1,29 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { active } from "tinro"; | ||||
|   import localForage from "localforage"; | ||||
|   import { router } from "tinro"; | ||||
|  | ||||
|   import store from "../store"; | ||||
|   import { router } from "tinro"; | ||||
|   import NoComponentLoaded from "./NoComponentLoaded.svelte"; | ||||
|   import { AuthService, OpenAPI } from "@odit/lfk-client-js"; | ||||
|  | ||||
|   let activePage = "dashboard"; | ||||
|   import { AuthService } from "@odit/lfk-client-js"; | ||||
|   let dropdown1 = false; | ||||
|   let navOpen = false; | ||||
|   function ismobile() { | ||||
|     let check = false; | ||||
|     (function (a) { | ||||
|       if ( | ||||
|         /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( | ||||
|           a | ||||
|         ) || | ||||
|         /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( | ||||
|           a.substr(0, 4) | ||||
|         ) | ||||
|       ) | ||||
|         check = true; | ||||
|     })(navigator.userAgent || navigator.vendor || window.opera); | ||||
|     return check; | ||||
|   } | ||||
|   $: mobile = ismobile(); | ||||
|   $: navOpen = false; | ||||
|   function logout() { | ||||
|     localForage.clear(); | ||||
|     location.replace("/"); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <section class="min-h-screen bg-gray-50 dark:bg-black dark:text-gray-100"> | ||||
| <section class="min-h-screen bg-gray-50"> | ||||
|   <nav | ||||
|     class:hidden={!navOpen && mobile} | ||||
|     class="select-none fixed top-0 left-0 z-20 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 md:translate-x-0"> | ||||
|     class:-translate-x-full={!navOpen} | ||||
|     class:translate-x-0={navOpen} | ||||
|     class="select-none fixed top-0 left-0 z-20 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 md:translate-x-0 bg-gray-50"> | ||||
|     <a href="/" class="flex items-center px-4 py-5"> | ||||
|       <img | ||||
|         src="/lfk-logo.png" | ||||
|         alt="Logo" | ||||
|         class="h-10" /> | ||||
|       <img src="/lfk-logo.png" alt="Logo" class="h-10" /> | ||||
|       <h3 class="text-lg">Lauf für Kaya! Admin</h3> | ||||
|     </a> | ||||
|     <nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation"> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/'} | ||||
|         class:bg-gray-100={$router.path === '/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/"> | ||||
| @@ -60,89 +37,93 @@ | ||||
|         </svg> | ||||
|         <span>{$_('dashboard-title')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path.includes('/orgs/')} | ||||
|         class:bg-gray-100={$router.path.includes('/orgs/')} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/orgs/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 24 24" | ||||
|           width="24" | ||||
|           height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|           <path | ||||
|             d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg> | ||||
|         <span>{$_('orgs')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/users/'} | ||||
|         class:bg-gray-100={$router.path === '/users/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/users/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 640 512"><path | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:GET')} | ||||
|         <a | ||||
|           class:bg-gray-100={$router.path.includes('/orgs/')} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|           href="/orgs/"> | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|         <span>{$_('users')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/runners/'} | ||||
|         class:bg-gray-100={$router.path === '/runners/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/runners/"> | ||||
|         <svg | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 24 24" | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|           <path | ||||
|             d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> | ||||
|         <span>{$_('runners')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/teams/'} | ||||
|         class:bg-gray-100={$router.path === '/teams/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/teams/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 640 512"><path | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             width="24" | ||||
|             height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg> | ||||
|           <span>{$_('orgs')}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes('USER:GET')} | ||||
|         <a | ||||
|           class:bg-gray-100={$router.path === '/users/'} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|           href="/users/"> | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|         <span>{$_('teams')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/tracks/'} | ||||
|         class:bg-gray-100={$router.path === '/tracks/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/tracks/"> | ||||
|         <svg | ||||
|           class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|           fill="currentColor" | ||||
|           width="24" | ||||
|           height="24" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 640 512"><path | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 640 512"><path | ||||
|               fill="currentColor" | ||||
|               d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|           <span>{$_('users')}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')} | ||||
|         <a | ||||
|           class:bg-gray-100={$router.path === '/runners/'} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|           href="/runners/"> | ||||
|           <svg | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 24 24" | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" /></svg> | ||||
|         <span>{$_('tracks')}</span> | ||||
|       </a> | ||||
|             width="24" | ||||
|             height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|             <path | ||||
|               d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> | ||||
|           <span>{$_('runners')}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:GET')} | ||||
|         <a | ||||
|           class:bg-gray-100={$router.path === '/teams/'} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|           href="/teams/"> | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 640 512"><path | ||||
|               fill="currentColor" | ||||
|               d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|           <span>{$_('teams')}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       {#if store.state.jwtinfo.userdetails.permissions.includes('TRACK:GET')} | ||||
|         <a | ||||
|           class:bg-gray-100={$router.path === '/tracks/'} | ||||
|           class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|           href="/tracks/"> | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600" | ||||
|             fill="currentColor" | ||||
|             width="24" | ||||
|             height="24" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 640 512"><path | ||||
|               fill="currentColor" | ||||
|               d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" /></svg> | ||||
|           <span>{$_('tracks')}</span> | ||||
|         </a> | ||||
|       {/if} | ||||
|       <a | ||||
|         class:dark:bg-gray-900={activePage === 'blub'} | ||||
|         class:bg-gray-100={activePage === 'blub'} | ||||
|         class:bg-gray-100={false} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="#"> | ||||
|         <svg | ||||
| @@ -161,8 +142,7 @@ | ||||
|       <div> | ||||
|         <div | ||||
|           tabindex="0" | ||||
|           class:dark:bg-gray-900={activePage === 'blub'} | ||||
|           class:bg-gray-100={activePage === 'blub'} | ||||
|           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={() => { | ||||
| @@ -237,7 +217,6 @@ | ||||
|         <span>{$_('changelog')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/settings/'} | ||||
|         class:bg-gray-100={$router.path === '/settings/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/settings/"> | ||||
| @@ -254,7 +233,6 @@ | ||||
|         <span>{$_('settings')}</span> | ||||
|       </a> | ||||
|       <a | ||||
|         class:dark:bg-gray-900={$router.path === '/about/'} | ||||
|         class:bg-gray-100={$router.path === '/about/'} | ||||
|         class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" | ||||
|         href="/about/"> | ||||
| @@ -291,65 +269,10 @@ | ||||
|     </nav> | ||||
|   </nav> | ||||
|   <div class="ml-0 transition md:ml-60"> | ||||
|     <header | ||||
|       class="flex items-center justify-between w-full px-4 border-b h-14"> | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           navOpen = !navOpen; | ||||
|         }} | ||||
|         class="block btn btn-light md:hidden"> | ||||
|         <span class="sr-only">Menu</span> | ||||
|         <svg | ||||
|           class="w-4 h-4" | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           viewBox="0 0 20 20" | ||||
|           fill="currentColor"> | ||||
|           <path | ||||
|             fill-rule="evenodd" | ||||
|             d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" | ||||
|             clip-rule="evenodd" /> | ||||
|         </svg> | ||||
|       </button> | ||||
|       <!-- <div class="hidden -ml-3 form-icon md:block w-96"> | ||||
|         <svg | ||||
|           xmlns="http://www.w3.org/2000/svg" | ||||
|           fill="none" | ||||
|           viewBox="0 0 24 24" | ||||
|           stroke="currentColor"> | ||||
|           <path | ||||
|             stroke-linecap="round" | ||||
|             stroke-linejoin="round" | ||||
|             stroke-width="2" | ||||
|             d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> | ||||
|         </svg> | ||||
|         <input | ||||
|           class="border-0 form-input" | ||||
|           placeholder="Search for articles..." /> | ||||
|       </div> --> | ||||
|       <div class="flex items-end"> | ||||
|         <a href="#" class="flex text-gray-500"> | ||||
|           <svg | ||||
|             class="flex-shrink-0 w-5 h-5" | ||||
|             xmlns="http://www.w3.org/2000/svg" | ||||
|             viewBox="0 0 20 20" | ||||
|             fill="currentColor"> | ||||
|             <path | ||||
|               d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z" /> | ||||
|           </svg> | ||||
|         </a> | ||||
|         <a href="/profile/" class="ml-4"> | ||||
|           <img | ||||
|             class="h-8 w-8 rounded-full" | ||||
|             src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" | ||||
|             alt="Profile Picture" /> | ||||
|         </a> | ||||
|       </div> | ||||
|     </header> | ||||
|     <slot> | ||||
|       <NoComponentLoaded /> | ||||
|     </slot> | ||||
|   </div> | ||||
|   <!-- Sidebar Backdrop --> | ||||
|   <div | ||||
|     on:click={() => { | ||||
|       navOpen = false; | ||||
|   | ||||
| @@ -2,7 +2,6 @@ | ||||
|   import { _, json } from "svelte-i18n"; | ||||
|   import { getlang } from "./datatable_i18n"; | ||||
|   import { Grid } from "gridjs"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   // | ||||
|   let table; | ||||
|   const datatable = new Grid({ | ||||
|   | ||||
| @@ -1,9 +1,12 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   const releaseinfo = document | ||||
|     .getElementById("buildinfo") | ||||
|     .textContent.replace("RELEASE_INFO-", "") | ||||
|     .replace("-RELEASE_INFO", ""); | ||||
|   $: releaseinfo = ""; | ||||
|   setTimeout(() => { | ||||
|     releaseinfo = document | ||||
|       .getElementById("buildinfo") | ||||
|       .textContent.replace("RELEASE_INFO-", "") | ||||
|       .replace("-RELEASE_INFO", ""); | ||||
|   }, 1500); | ||||
|   const year = new Date().getFullYear(); | ||||
| </script> | ||||
|  | ||||
|   | ||||
							
								
								
									
										286
									
								
								src/components/ImportRunnerModal.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								src/components/ImportRunnerModal.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,286 @@ | ||||
| <script> | ||||
|   import csv from "csvtojson"; | ||||
|   import { read as readXlsx, utils as xlsx_utils } from "xlsx"; | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { clickOutside } from "./outsideclick"; | ||||
|   import { focusTrap } from "svelte-focus-trap"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import { ImportService } from "@odit/lfk-client-js"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   export let opened_from; | ||||
|   export let passed_org; | ||||
|   export let passed_orgs; | ||||
|   export let passed_team; | ||||
|   export let import_modal_open; | ||||
|   $: searchvalue = ""; | ||||
|   const dispatch = createEventDispatcher(); | ||||
|   function cancelModal() { | ||||
|     import_modal_open = false; | ||||
|     dispatch("cancel"); | ||||
|   } | ||||
|   (() => { | ||||
|     document.onkeydown = (e) => { | ||||
|       e = e || window.event; | ||||
|       if (e.key === "Escape") { | ||||
|         import_modal_open = false; | ||||
|       } | ||||
|       if (e.keyCode === 13) { | ||||
|         // | ||||
|       } | ||||
|     }; | ||||
|   })(); | ||||
|   let selected_org; | ||||
|   let files; | ||||
|   let recent_processed = true; | ||||
|   $: json_output = []; | ||||
|   $: { | ||||
|     if (files) { | ||||
|       if ( | ||||
|         files[0].type === | ||||
|         "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | ||||
|       ) { | ||||
|         const reader = new FileReader(); | ||||
|         reader.addEventListener("load", async (e) => { | ||||
|           const data = new Uint8Array(e.target.result); | ||||
|           const out = readXlsx(data, { type: "array" }); | ||||
|           json_output = xlsx_utils.sheet_to_json( | ||||
|             out.Sheets[Object.keys(out.Sheets)[0]] | ||||
|           ); | ||||
|         }); | ||||
|         reader.readAsArrayBuffer(files[0]); | ||||
|       } else { | ||||
|         const reader = new FileReader(); | ||||
|         reader.addEventListener("load", async (e) => { | ||||
|           json_output = await csv({ | ||||
|             delimiter: [";", ","], | ||||
|             trim: true, | ||||
|           }).fromString(e.target.result); | ||||
|         }); | ||||
|         reader.readAsText(files[0]); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   function importAction() { | ||||
|     if (recent_processed === true) { | ||||
|       const toast = Toastify({ | ||||
|         text: "Runners are being imported...", | ||||
|         duration: -1, | ||||
|       }).showToast(); | ||||
|       recent_processed = false; | ||||
|       const mapped = json_output.map(function (runner) { | ||||
|         return { | ||||
|           firstname: runner[`${$_("csv_import__firstname")}`], | ||||
|           middlename: runner[`${$_("csv_import__middlename")}`], | ||||
|           lastname: runner[`${$_("csv_import__lastname")}`], | ||||
|           team: | ||||
|             runner[`${$_("csv_import__team")}`] || | ||||
|             runner[`${$_("csv_import__class")}`], | ||||
|         }; | ||||
|       }); | ||||
|       let org = 0; | ||||
|       if (opened_from === "OrgDetail") { | ||||
|         org = passed_org.id; | ||||
|       } | ||||
|       if (opened_from === "OrgOverview") { | ||||
|         org = parseInt(selected_org); | ||||
|       } | ||||
|       if (opened_from === "OrgOverview" || opened_from === "OrgDetail") { | ||||
|         ImportService.importControllerPostOrgsJson(org, mapped) | ||||
|           .then((resp) => { | ||||
|             toast.hideToast(); | ||||
|             recent_processed = true; | ||||
|             Toastify({ | ||||
|               text: "Import finished", | ||||
|               duration: 500, | ||||
|               backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|             }).showToast(); | ||||
|             cancelModal(); | ||||
|           }) | ||||
|           .catch((err) => { | ||||
|             toast.hideToast(); | ||||
|             recent_processed = true; | ||||
|           }); | ||||
|       } | ||||
|       if (opened_from === "TeamDetail") { | ||||
|         ImportService.importControllerPostTeamsJson(passed_team.id, mapped) | ||||
|           .then((resp) => { | ||||
|             toast.hideToast(); | ||||
|             recent_processed = true; | ||||
|             Toastify({ | ||||
|               text: "Import finished", | ||||
|               duration: 500, | ||||
|               backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|             }).showToast(); | ||||
|             cancelModal(); | ||||
|           }) | ||||
|           .catch((err) => { | ||||
|             toast.hideToast(); | ||||
|             recent_processed = true; | ||||
|           }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#if import_modal_open} | ||||
|   <div | ||||
|     class="fixed z-10 inset-0 overflow-y-auto" | ||||
|     use:focusTrap | ||||
|     use:clickOutside | ||||
|     on:click_outside={() => { | ||||
|       import_modal_open = false; | ||||
|     }}> | ||||
|     <div | ||||
|       class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> | ||||
|       <div class="fixed inset-0 transition-opacity" aria-hidden="true"> | ||||
|         <div | ||||
|           class="absolute inset-0 bg-gray-500 opacity-75" | ||||
|           data-id="modal_backdrop" /> | ||||
|       </div> | ||||
|       <span | ||||
|         class="hidden sm:inline-block sm:align-middle sm:h-screen" | ||||
|         aria-hidden="true">​</span> | ||||
|       <div | ||||
|         class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" | ||||
|         role="dialog" | ||||
|         aria-modal="true" | ||||
|         aria-labelledby="modal-headline"> | ||||
|         <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> | ||||
|           <div class="sm:flex sm:items-start"> | ||||
|             <div | ||||
|               class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"> | ||||
|               <svg | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 class="h-6 w-6 text-blue-600" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> | ||||
|             </div> | ||||
|             <div class="mt-3 text-center sm:mt-0 sm:ml-2 sm:text-left w-full"> | ||||
|               <h3 class="text-lg leading-6 font-bold mt-2 text-gray-900"> | ||||
|                 {$_('runner-import')} | ||||
|               </h3> | ||||
|             </div> | ||||
|           </div> | ||||
|           <div class="mt-5 text-center sm:mt-0 sm:ml-2 sm:text-left w-full"> | ||||
|             {#if json_output.length === 0} | ||||
|               <div class="mt-2 mb-6"> | ||||
|                 <p class="text-sm text-gray-500"> | ||||
|                   {$_('please-provide-the-required-csv-xlsx-file')} | ||||
|                 </p> | ||||
|               </div> | ||||
|               <div class="overflow-hidden relative mt-4 mb-4"> | ||||
|                 <input | ||||
|                   accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" | ||||
|                   bind:files | ||||
|                   type="file" /> | ||||
|               </div> | ||||
|             {/if} | ||||
|             {#if json_output.length > 0} | ||||
|               {#if opened_from === 'OrgOverview'} | ||||
|                 <p>{$_('import__target-organization')}</p> | ||||
|                 <select | ||||
|                   name="team" | ||||
|                   bind:value={selected_org} | ||||
|                   class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> | ||||
|                   {#each passed_orgs as o} | ||||
|                     <option value={o.id}>{o.name}</option> | ||||
|                   {/each} | ||||
|                 </select> | ||||
|                 <p>{$_('bitte-bestaetige-diese-laeufer-fuer-den-import')}</p> | ||||
|               {/if} | ||||
|               {#if opened_from === 'OrgDetail'} | ||||
|                 <p> | ||||
|                   {$_('runnerimport_verify_runners_org', { | ||||
|                     values: { org_name: passed_org.name }, | ||||
|                   })} | ||||
|                 </p> | ||||
|               {/if} | ||||
|               <input | ||||
|                 type="search" | ||||
|                 bind:value={searchvalue} | ||||
|                 placeholder={$_('datatable.search')} | ||||
|                 aria-label={$_('datatable.search')} | ||||
|                 class="p-2 w-full" /> | ||||
|               <div class="relative w-full mt-4 mb-4"> | ||||
|                 <div class="w-full overflow-x-auto"> | ||||
|                   <table class="divide-y divide-gray-200 w-full"> | ||||
|                     <thead class="bg-gray-50"> | ||||
|                       <tr> | ||||
|                         <th | ||||
|                           scope="col" | ||||
|                           class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                           {$_('csv_import__firstname')} | ||||
|                         </th> | ||||
|                         <th | ||||
|                           scope="col" | ||||
|                           class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                           {$_('csv_import__middlename')} | ||||
|                         </th> | ||||
|                         <th | ||||
|                           scope="col" | ||||
|                           class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                           {$_('csv_import__lastname')} | ||||
|                         </th> | ||||
|                         {#if opened_from !== 'TeamDetail'} | ||||
|                           <th | ||||
|                             scope="col" | ||||
|                             class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                             {$_('csv_import__team')} | ||||
|                           </th> | ||||
|                         {/if} | ||||
|                       </tr> | ||||
|                     </thead> | ||||
|                     <tbody class="divide-y divide-gray-200"> | ||||
|                       {#each json_output as runner} | ||||
|                         {#if Object.values(runner) | ||||
|                           .toString() | ||||
|                           .toLowerCase() | ||||
|                           .includes(searchvalue)} | ||||
|                           <tr> | ||||
|                             <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                               {runner[`${$_('csv_import__firstname')}`]} | ||||
|                             </td> | ||||
|                             <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                               {runner[`${$_('csv_import__middlename')}`] || ''} | ||||
|                             </td> | ||||
|                             <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                               {runner[`${$_('csv_import__lastname')}`]} | ||||
|                             </td> | ||||
|                             {#if opened_from !== 'TeamDetail'} | ||||
|                               <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                                 {runner[`${$_('csv_import__team')}`] || runner[`${$_('csv_import__class')}`] || '---'} | ||||
|                               </td> | ||||
|                             {/if} | ||||
|                           </tr> | ||||
|                         {/if} | ||||
|                       {/each} | ||||
|                     </tbody> | ||||
|                   </table> | ||||
|                 </div> | ||||
|                 <button | ||||
|                   on:click={importAction} | ||||
|                   type="button" | ||||
|                   class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|                   {$_('import-runners')} | ||||
|                 </button> | ||||
|                 <button | ||||
|                   on:click={() => { | ||||
|                     json_output = []; | ||||
|                     cancelModal(); | ||||
|                   }} | ||||
|                   type="button" | ||||
|                   class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|                   {$_('cancel')} | ||||
|                 </button> | ||||
|               </div> | ||||
|             {/if} | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| {/if} | ||||
| @@ -1,83 +1,238 @@ | ||||
| <script> | ||||
|   import { RunnerOrganizationService } from "@odit/lfk-client-js"; | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import store from "../store"; | ||||
|   import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte"; | ||||
|   import ImportRunnerModal from "./ImportRunnerModal.svelte"; | ||||
|   import PromiseError from "./PromiseError.svelte"; | ||||
|   $: delete_triggered = false; | ||||
|   $: save_enabled = !data_changed; | ||||
|   export let params; | ||||
|   let orgdata = {}; | ||||
|   let original = {}; | ||||
|   $: data_loaded = false; | ||||
|   $: data_changed = JSON.stringify(orgdata) === JSON.stringify(original); | ||||
|   const promise = RunnerOrganizationService.runnerOrganizationControllerGetOne( | ||||
|     params.orgid | ||||
|   ).then((value) => { | ||||
|     data_loaded = true; | ||||
|     orgdata = Object.assign(orgdata, value); | ||||
|     original = Object.assign(original, value); | ||||
|   }); | ||||
|   let modal_open = false; | ||||
|   let delete_org = {}; | ||||
|   function deleteOrganization() { | ||||
|     // RunnerOrganizationService.runnerOrganizationControllerRemove( | ||||
|     //   original.id, | ||||
|     //   false | ||||
|     // ) | ||||
|     //   .then((resp) => { | ||||
|     //     Toastify({ | ||||
|     //       text: "Organization deleted", | ||||
|     //       duration: 500, | ||||
|     //       backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|     //     }).showToast(); | ||||
|     //     location.replace("./"); | ||||
|     //   }) | ||||
|     //   .catch((err) => { | ||||
|     modal_open = true; | ||||
|     delete_org = original; | ||||
|     // }); | ||||
|   } | ||||
|   function submit() { | ||||
|     if (data_loaded === true && save_enabled) { | ||||
|       Toastify({ | ||||
|         text: "updating organization", | ||||
|         duration: 2500, | ||||
|       }).showToast(); | ||||
|       RunnerOrganizationService.runnerOrganizationControllerPut( | ||||
|         original.id, | ||||
|         orgdata | ||||
|       ) | ||||
|         .then((resp) => { | ||||
|           Object.assign(original, orgdata); | ||||
|           original = orgdata; | ||||
|           Object.assign(original, orgdata); | ||||
|           // | ||||
|           Toastify({ | ||||
|             text: "updated organization", | ||||
|             duration: 2500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|         }) | ||||
|         .catch((err) => {}); | ||||
|     } else { | ||||
|     } | ||||
|   } | ||||
|   export let import_modal_open = false; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     Orgs | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500"> | ||||
|     configure the tracks & minimum lap times | ||||
|   </p> | ||||
|   <div class="flex flex-row mb-4"> | ||||
|     <div class="w-full"> | ||||
|       <nav class="w-full flex"> | ||||
|         <ol class="list-none flex flex-row items-center justify-start"> | ||||
|           <li class="mr-2 flex items-center"> | ||||
|             <svg | ||||
|               stroke="currentColor" | ||||
|               fill="none" | ||||
|               stroke-width="2" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke-linecap="round" | ||||
|               stroke-linejoin="round" | ||||
|               class="h-3 w-3 stroke-current" | ||||
|               height="1em" | ||||
|               width="1em" | ||||
|               xmlns="http://www.w3.org/2000/svg"><path | ||||
|                 d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> | ||||
|               <polyline points="9 22 9 12 15 12 15 22" /></svg> | ||||
|           </li> | ||||
|           <li class="flex items-center"> | ||||
|             <a class="mr-2" href="/">Home</a><svg | ||||
|               stroke="currentColor" | ||||
|               fill="none" | ||||
|               stroke-width="2" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke-linecap="round" | ||||
|               stroke-linejoin="round" | ||||
|               class="h-3 w-3 mr-2 stroke-current" | ||||
|               height="1em" | ||||
|               width="1em" | ||||
|               xmlns="http://www.w3.org/2000/svg"><line | ||||
|                 x1="5" | ||||
|                 y1="12" | ||||
|                 x2="19" | ||||
|                 y2="12" /> | ||||
|               <polyline points="12 5 19 12 12 19" /></svg> | ||||
|           </li> | ||||
|           <li class="mr-2 flex items-center"> | ||||
|             <svg | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               viewBox="0 0 24 24" | ||||
|               width="24" | ||||
|               height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|               <path | ||||
|                 d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z" /></svg> | ||||
|           </li> | ||||
|           <li class="flex items-center"> | ||||
|             <a class="mr-2" href="./">Orgs</a><svg | ||||
|               stroke="currentColor" | ||||
|               fill="none" | ||||
|               stroke-width="2" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke-linecap="round" | ||||
|               stroke-linejoin="round" | ||||
|               class="h-3 w-3 mr-2 stroke-current" | ||||
|               height="1em" | ||||
|               width="1em" | ||||
|               xmlns="http://www.w3.org/2000/svg"><line | ||||
|                 x1="5" | ||||
|                 y1="12" | ||||
|                 x2="19" | ||||
|                 y2="12" /> | ||||
|               <polyline points="12 5 19 12 12 19" /></svg> | ||||
|           </li> | ||||
|           <li class="flex items-center"> | ||||
|             <span class="mr-2">Org-Details #{params.orgid}</span> | ||||
|           </li> | ||||
|         </ol> | ||||
|       </nav> | ||||
| <ImportRunnerModal | ||||
|   on:cancelDelete={(event) => { | ||||
|     import_modal_open = false; | ||||
|   }} | ||||
|   passed_team={{}} | ||||
|   passed_orgs={[]} | ||||
|   passed_org={orgdata} | ||||
|   opened_from="OrgDetail" | ||||
|   bind:import_modal_open /> | ||||
| <ConfirmOrgDeletion bind:modal_open bind:delete_org /> | ||||
| {#if data_loaded} | ||||
|   <section class="container p-5"> | ||||
|     <div class="mb-8 text-3xl font-extrabold leading-tight"> | ||||
|       {original.name} | ||||
|       <span data-id="org_actions_${orgdata.id}"> | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:IMPORT')} | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               import_modal_open = true; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('import-runners')} | ||||
|           </button> | ||||
|         {/if} | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes('USER:DELETE')} | ||||
|           {#if delete_triggered} | ||||
|             <button | ||||
|               on:click={deleteOrganization} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-delete')}</button> | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = !delete_triggered; | ||||
|               }} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> | ||||
|           {/if} | ||||
|           {#if !delete_triggered} | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = true; | ||||
|               }} | ||||
|               type="button" | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-organization')}</button> | ||||
|           {/if} | ||||
|         {/if} | ||||
|         {#if !delete_triggered} | ||||
|           <button | ||||
|             on:click={submit} | ||||
|             disabled={!save_enabled} | ||||
|             class:opacity-50={!save_enabled} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> | ||||
|         {/if} | ||||
|       </span> | ||||
|     </div> | ||||
|   </div> | ||||
| </section> | ||||
|     <div class="flex flex-row mb-4"> | ||||
|       <div class="w-full"> | ||||
|         <nav class="w-full flex"> | ||||
|           <ol class="list-none flex flex-row items-center justify-start"> | ||||
|             <li class="mr-2 flex items-center"> | ||||
|               <svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><path | ||||
|                   d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> | ||||
|                 <polyline points="9 22 9 12 15 12 15 22" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <a class="mr-2" href="/">Home</a><svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="mr-2 flex items-center"> | ||||
|               <svg | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 width="24" | ||||
|                 height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <a class="mr-2" href="./">Orgs</a><svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <span class="mr-2">Org-Details #{params.orgid}</span> | ||||
|             </li> | ||||
|           </ol> | ||||
|         </nav> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label for="name" class="font-medium text-gray-700">Name</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder="Name" | ||||
|         type="text" | ||||
|         bind:value={orgdata.name} | ||||
|         name="name" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
|         for="contact" | ||||
|         class="font-medium text-gray-700">{$_('contact')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('contact')} | ||||
|         type="text" | ||||
|         bind:value={orgdata.contact} | ||||
|         name="contact" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
|         for="address" | ||||
|         class="font-medium text-gray-700">{$_('address')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('address')} | ||||
|         type="text" | ||||
|         bind:value={orgdata.address} | ||||
|         name="address" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|     </div> | ||||
|   </section> | ||||
| {:else} | ||||
|   {#await promise} | ||||
|     organization detail is being loaded... | ||||
|   {:catch error} | ||||
|     <PromiseError /> | ||||
|   {/await} | ||||
| {/if} | ||||
|   | ||||
							
								
								
									
										174
									
								
								src/components/OrgOverview.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/components/OrgOverview.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   let modal_open = false; | ||||
|   let delete_org = {}; | ||||
|   import { RunnerOrganizationService } from "@odit/lfk-client-js"; | ||||
|   import store from "../store"; | ||||
|   import OrgsEmptyState from "./OrgsEmptyState.svelte"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte"; | ||||
|   $: searchvalue = ""; | ||||
|   $: active_deletes = []; | ||||
|   export let current_organizations = []; | ||||
|  | ||||
|   const promise = RunnerOrganizationService.runnerOrganizationControllerGetAll().then( | ||||
|     (val) => { | ||||
|       current_organizations = val; | ||||
|     } | ||||
|   ); | ||||
| </script> | ||||
|  | ||||
| <ConfirmOrgDeletion | ||||
|   on:cancelDelete={(event) => { | ||||
|     modal_open = false; | ||||
|     active_deletes[event.detail.id] = false; | ||||
|   }} | ||||
|   bind:modal_open | ||||
|   bind:delete_org /> | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:GET')} | ||||
|   {#await promise} | ||||
|     <div | ||||
|       class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|       role="alert"> | ||||
|       <p class="font-bold">organizations are being loaded...</p> | ||||
|       <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|     </div> | ||||
|   {:then} | ||||
|     {#if current_organizations.length === 0} | ||||
|       <OrgsEmptyState /> | ||||
|     {:else} | ||||
|       <input | ||||
|         type="search" | ||||
|         bind:value={searchvalue} | ||||
|         placeholder={$_('datatable.search')} | ||||
|         aria-label={$_('datatable.search')} | ||||
|         class="gridjs-input gridjs-search-input mb-4" /> | ||||
|       <div | ||||
|         class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> | ||||
|         <table class="divide-y divide-gray-200 w-full"> | ||||
|           <thead class="bg-gray-50"> | ||||
|             <tr> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Name | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Address | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Contact | ||||
|               </th> | ||||
|               <th scope="col" class="relative px-6 py-3"> | ||||
|                 <span class="sr-only">Action</span> | ||||
|               </th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody class="divide-y divide-gray-200"> | ||||
|             {#each current_organizations as o} | ||||
|               {#if Object.values(o) | ||||
|                 .toString() | ||||
|                 .toLowerCase() | ||||
|                 .includes(searchvalue)} | ||||
|                 <tr data-rowid="org_{o.id}"> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       <div class="ml-4"> | ||||
|                         <div | ||||
|                           class="text-sm font-medium text-gray-900"> | ||||
|                           {o.name} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       <div class="ml-4"> | ||||
|                         <div | ||||
|                           class="text-sm font-medium text-gray-900"> | ||||
|                           {#if o.address} | ||||
|                             {JSON.stringify(o.address)} | ||||
|                           {:else}no address specified{/if} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       <div class="ml-4"> | ||||
|                         <div | ||||
|                           class="text-sm font-medium text-gray-900"> | ||||
|                           {#if o.contact} | ||||
|                             {JSON.stringify(o.contact)} | ||||
|                           {:else}no contact specified{/if} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   {#if active_deletes[o.id] === true} | ||||
|                     <td | ||||
|                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           active_deletes[o.id] = false; | ||||
|                         }} | ||||
|                         tabindex="0" | ||||
|                         class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel | ||||
|                         Delete</button> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           RunnerOrganizationService.runnerOrganizationControllerRemove(o.id, false) | ||||
|                             .then((resp) => { | ||||
|                               current_organizations = current_organizations.filter((obj) => obj.id !== o.id); | ||||
|                               Toastify({ | ||||
|                                 text: 'Organization deleted', | ||||
|                                 duration: 500, | ||||
|                                 backgroundColor: | ||||
|                                   'linear-gradient(to right, #00b09b, #96c93d)', | ||||
|                               }).showToast(); | ||||
|                             }) | ||||
|                             .catch((err) => { | ||||
|                               modal_open = true; | ||||
|                               delete_org = o; | ||||
|                             }); | ||||
|                         }} | ||||
|                         tabindex="0" | ||||
|                         class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Confirm | ||||
|                         Delete</button> | ||||
|                     </td> | ||||
|                   {:else} | ||||
|                     <td | ||||
|                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                       <a | ||||
|                         href="./{o.id}" | ||||
|                         class="text-indigo-600 hover:text-indigo-900">Edit</a> | ||||
|                       {#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:DELETE')} | ||||
|                         <button | ||||
|                           on:click={() => { | ||||
|                             active_deletes[o.id] = true; | ||||
|                           }} | ||||
|                           tabindex="0" | ||||
|                           class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</button> | ||||
|                       {/if} | ||||
|                     </td> | ||||
|                   {/if} | ||||
|                 </tr> | ||||
|               {/if} | ||||
|             {/each} | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|     {/if} | ||||
|   {:catch error} | ||||
|     <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|       <span class="inline-block align-middle mr-8"> | ||||
|         <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|         {error} | ||||
|       </span> | ||||
|     </div> | ||||
|   {/await} | ||||
| {/if} | ||||
| @@ -1,13 +1,51 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import store from "../store"; | ||||
|   import AddOrgModal from "./AddOrgModal.svelte"; | ||||
|   export let modal_open = false; | ||||
|   import OrgOverview from "./OrgOverview.svelte"; | ||||
|   import ImportRunnerModal from "./ImportRunnerModal.svelte"; | ||||
|   let current_organizations = []; | ||||
|   export let import_modal_open = false; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     Orgs | ||||
|     {$_('organizations')} | ||||
|     {#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:CREATE')} | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           modal_open = true; | ||||
|         }} | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|         {$_('create-organization')} | ||||
|       </button> | ||||
|     {/if} | ||||
|     {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:IMPORT')} | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           import_modal_open = true; | ||||
|         }} | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|         {$_('import-runners')} | ||||
|       </button> | ||||
|     {/if} | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500"> | ||||
|     add, delete, edit organizations | ||||
|   </p> | ||||
|   <nav><a class="underline" href="./1">Org 1</a></nav> | ||||
|   <p class="mb-8 text-lg text-gray-500">manage runner organizations</p> | ||||
|   <OrgOverview bind:current_organizations /> | ||||
| </section> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:CREATE')} | ||||
|   <AddOrgModal bind:current_organizations bind:modal_open /> | ||||
|   <ImportRunnerModal | ||||
|     on:cancelDelete={(event) => { | ||||
|       import_modal_open = false; | ||||
|     }} | ||||
|     passed_team={{}} | ||||
|     passed_org={{}} | ||||
|     passed_orgs={current_organizations} | ||||
|     opened_from="OrgOverview" | ||||
|     bind:import_modal_open /> | ||||
| {/if} | ||||
|   | ||||
							
								
								
									
										17
									
								
								src/components/OrgsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/components/OrgsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import AddOrgModal from "./AddOrgModal.svelte"; | ||||
|   import org_empty from "./org_empty.svg"; | ||||
|   let modal_open = false; | ||||
|   let current_organizations = []; | ||||
| </script> | ||||
|  | ||||
| <div class="text-center items-center justify-center"> | ||||
|   <p class="mb-16 text-lg text-gray-500"> | ||||
|     <img class="w-full h-44" src={org_empty} alt="" /> | ||||
|     <span class="font-bold">There are no organizations added yet.</span><br /> | ||||
|     <span>Add your first organization</span> | ||||
|   </p> | ||||
| </div> | ||||
|  | ||||
| <AddOrgModal bind:modal_open bind:current_organizations /> | ||||
| @@ -8,7 +8,7 @@ | ||||
|     </div> | ||||
|   </div> | ||||
|   <div | ||||
|     class="w-full p-4 mb-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890"> | ||||
|     class="w-full p-4 mb-4 rounded-lg bg-white border border-grey-100"> | ||||
|     <div class="flex flex-row items-center justify-start p-4"> | ||||
|       <div class="flex-shrink-0 w-24"> | ||||
|         <img | ||||
|   | ||||
							
								
								
									
										264
									
								
								src/components/RunnerDetail.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								src/components/RunnerDetail.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,264 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import lodashIsEqual from "lodash.isequal"; | ||||
|   import store from "../store"; | ||||
|   import { | ||||
|     RunnerService, | ||||
|     RunnerTeamService, | ||||
|     RunnerOrganizationService, | ||||
|   } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import PromiseError from "./PromiseError.svelte"; | ||||
|   import isEmail from "validator/es/lib/isEmail"; | ||||
|   let data_loaded = false; | ||||
|   export let params; | ||||
|   const runner_promise = RunnerService.runnerControllerGetOne(params.runnerid); | ||||
|   $: delete_triggered = false; | ||||
|   $: original_data = {}; | ||||
|   $: editable = {}; | ||||
|   $: changes_performed = !lodashIsEqual(original_data, editable); | ||||
|   $: isEmailValid = | ||||
|     (editable.email || "") === "" || | ||||
|     (editable.email && isEmail(editable.email || "")); | ||||
|   $: isFirstnameValid = editable.firstname !== ""; | ||||
|   $: isLastnameValid = editable.lastname !== ""; | ||||
|   $: save_enabled = | ||||
|     changes_performed && isFirstnameValid && isLastnameValid && isEmailValid; | ||||
|   runner_promise.then((data) => { | ||||
|     data_loaded = true; | ||||
|     original_data = Object.assign(original_data, data); | ||||
|     original_data.group = original_data.group.id; | ||||
|     editable = Object.assign(editable, original_data); | ||||
|   }); | ||||
|   let orgs = []; | ||||
|   RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => { | ||||
|     orgs = val; | ||||
|   }); | ||||
|   let teams = []; | ||||
|   RunnerTeamService.runnerTeamControllerGetAll().then((val) => { | ||||
|     teams = val; | ||||
|   }); | ||||
|   function submit() { | ||||
|     if (data_loaded === true && save_enabled) { | ||||
|       Toastify({ | ||||
|         text: $_("updating-runner"), | ||||
|         duration: 2500, | ||||
|       }).showToast(); | ||||
|       RunnerService.runnerControllerPut(original_data.id, editable) | ||||
|         .then((resp) => { | ||||
|           Object.assign(original_data, editable); | ||||
|           original_data = editable; | ||||
|           Object.assign(original_data, editable); | ||||
|           Toastify({ | ||||
|             text: $_("runner-updated"), | ||||
|             duration: 2500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|         }) | ||||
|         .catch((err) => {}); | ||||
|     } else { | ||||
|     } | ||||
|   } | ||||
|   function deleteRunner() { | ||||
|     RunnerService.runnerControllerRemove(original_data.id, true) | ||||
|       .then((resp) => { | ||||
|         location.replace("./"); | ||||
|       }) | ||||
|       .catch((err) => {}); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#await runner_promise} | ||||
|   {$_('loading-runners')} | ||||
| {:then} | ||||
|   <section class="container p-5 select-none"> | ||||
|     <div class="flex flex-row mb-4"> | ||||
|       <div class="w-full"> | ||||
|         <nav class="w-full flex"> | ||||
|           <ol class="list-none flex flex-row items-center justify-start"> | ||||
|             <li class="flex items-center"> | ||||
|               <svg | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 class="flex-shrink-0 w-5 h-5 mr-2" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24"><path fill="none" d="M0 0h24v24H0z" /> | ||||
|                 <path | ||||
|                   d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <a class="mr-2" href="./">{$_('runners')}</a><svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <span class="mr-2">{original_data.firstname} | ||||
|                 {original_data.middlename || ''} | ||||
|                 {original_data.lastname}</span> | ||||
|             </li> | ||||
|           </ol> | ||||
|         </nav> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="mb-8 text-3xl font-extrabold leading-tight"> | ||||
|       {original_data.firstname} | ||||
|       {original_data.middlename || ''} | ||||
|       {original_data.lastname} | ||||
|       <span data-id="runner_actions_${editable.id}"> | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:DELETE')} | ||||
|           {#if delete_triggered} | ||||
|             <button | ||||
|               on:click={deleteRunner} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-deletion')}</button> | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = !delete_triggered; | ||||
|               }} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> | ||||
|           {/if} | ||||
|           {#if !delete_triggered} | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = true; | ||||
|               }} | ||||
|               type="button" | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-runner')}</button> | ||||
|           {/if} | ||||
|         {/if} | ||||
|         {#if !delete_triggered} | ||||
|           <button | ||||
|             disabled={!save_enabled} | ||||
|             class:opacity-50={!save_enabled} | ||||
|             type="button" | ||||
|             on:click={submit} | ||||
|             class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> | ||||
|         {/if} | ||||
|       </span> | ||||
|     </div> | ||||
|     <!--  --> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
|         for="firstname" | ||||
|         class="font-medium text-gray-700">{$_('first-name')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('first-name')} | ||||
|         type="text" | ||||
|         class:border-red-500={!isFirstnameValid} | ||||
|         class:focus:border-red-500={!isFirstnameValid} | ||||
|         class:focus:ring-red-500={!isFirstnameValid} | ||||
|         bind:value={editable.firstname} | ||||
|         name="firstname" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|       {#if !isFirstnameValid} | ||||
|         <span | ||||
|           class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|           {$_('first-name-is-required')} | ||||
|         </span> | ||||
|       {/if} | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
|         for="middlename" | ||||
|         class="font-medium text-gray-700">{$_('middle-name')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('middle-name')} | ||||
|         type="text" | ||||
|         bind:value={editable.middlename} | ||||
|         name="middlename" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
|         for="lastname" | ||||
|         class="font-medium text-gray-700">{$_('last-name')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('last-name')} | ||||
|         type="text" | ||||
|         bind:value={editable.lastname} | ||||
|         class:border-red-500={!isLastnameValid} | ||||
|         class:focus:border-red-500={!isLastnameValid} | ||||
|         class:focus:ring-red-500={!isLastnameValid} | ||||
|         name="lastname" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|       {#if !isLastnameValid} | ||||
|         <span | ||||
|           class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|           {$_('last-name-is-required')} | ||||
|         </span> | ||||
|       {/if} | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
|         for="email" | ||||
|         class="font-medium text-gray-700">{$_('e-mail-adress')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('e-mail-adress')} | ||||
|         type="email" | ||||
|         bind:value={editable.email} | ||||
|         class:border-red-500={!isEmailValid} | ||||
|         class:focus:border-red-500={!isEmailValid} | ||||
|         class:focus:ring-red-500={!isEmailValid} | ||||
|         name="email" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|       {#if !isEmailValid} | ||||
|         <span | ||||
|           class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"> | ||||
|           {$_('valid-email-is-required')} | ||||
|         </span> | ||||
|       {/if} | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label for="phone" class="font-medium text-gray-700">{$_('phone')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('phone')} | ||||
|         type="tel" | ||||
|         bind:value={editable.phone} | ||||
|         name="phone" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <span class="font-medium text-gray-700">{$_('group')}</span> | ||||
|       <select | ||||
|         bind:value={editable.group} | ||||
|         name="team" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> | ||||
|         {#each teams as team} | ||||
|           <option value={team.id}> | ||||
|             {team.parentGroup.name} | ||||
|             > | ||||
|             {team.name} | ||||
|           </option> | ||||
|         {/each} | ||||
|         {#each orgs as org} | ||||
|           <option value={org.id}>{org.name}</option> | ||||
|         {/each} | ||||
|       </select> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <span class="font-medium text-gray-700">{$_('distance')}</span> | ||||
|       <br /> | ||||
|       <span class="text-gray-700">{original_data.distance} km</span> | ||||
|     </div> | ||||
|   </section> | ||||
| {:catch error} | ||||
|   <PromiseError {error} /> | ||||
| {/await} | ||||
| @@ -1,10 +1,48 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import store from "../store"; | ||||
|   import AddRunnerModal from "./AddRunnerModal.svelte"; | ||||
|   import ImportRunnerModal from "./ImportRunnerModal.svelte"; | ||||
|   import RunnersOverview from "./RunnersOverview.svelte"; | ||||
|   let current_runners = []; | ||||
|   export let modal_open = false; | ||||
|   export let import_modal_open = false; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     {$_('runners')} | ||||
|     {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:CREATE')} | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           modal_open = true; | ||||
|         }} | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|         Läufer hinzufügen | ||||
|       </button> | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           import_modal_open = true; | ||||
|         }} | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|         Läufer importieren | ||||
|       </button> | ||||
|     {/if} | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500">läuft bei ihnen</p> | ||||
|   <RunnersOverview bind:current_runners /> | ||||
| </section> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:CREATE')} | ||||
|   <AddRunnerModal bind:current_runners bind:modal_open /> | ||||
|   <ImportRunnerModal | ||||
|     on:cancelDelete={(event) => { | ||||
|       import_modal_open = false; | ||||
|     }} | ||||
|     passed_team={{}} | ||||
|     passed_orgs={[]} | ||||
|     passed_org={{}} | ||||
|     opened_from="RunnerOverview" | ||||
|     bind:import_modal_open /> | ||||
| {/if} | ||||
|   | ||||
							
								
								
									
										16
									
								
								src/components/RunnersEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/components/RunnersEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   // import AddUserModal from "./AddUserModal.svelte"; | ||||
|   import runners_empty from "./runners_empty.svg"; | ||||
|   // let modal_open = false; | ||||
| </script> | ||||
|  | ||||
| <div class="text-center items-center justify-center"> | ||||
|   <p class="mb-16 text-lg text-gray-500"> | ||||
|     <img class="w-full h-44" src={runners_empty} alt="" /> | ||||
|     <span class="font-bold">There are no runners added yet.</span><br /> | ||||
|     <span>Add your first runner</span> | ||||
|   </p> | ||||
| </div> | ||||
|  | ||||
| <!-- <AddUserModal bind:modal_open /> --> | ||||
							
								
								
									
										146
									
								
								src/components/RunnersOverview.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/components/RunnersOverview.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { RunnerService } from "@odit/lfk-client-js"; | ||||
|   import store from "../store"; | ||||
|   import RunnersEmptyState from "./RunnersEmptyState.svelte"; | ||||
|   $: searchvalue = ""; | ||||
|   $: active_deletes = []; | ||||
|   export let current_runners = []; | ||||
|   const runners_promise = RunnerService.runnerControllerGetAll().then((val) => { | ||||
|     current_runners = val; | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')} | ||||
|   {#await runners_promise} | ||||
|     <div | ||||
|       class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|       role="alert"> | ||||
|       <p class="font-bold">runners are being loaded...</p> | ||||
|       <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|     </div> | ||||
|   {:then} | ||||
|     {#if current_runners.length === 0} | ||||
|       <RunnersEmptyState /> | ||||
|     {:else} | ||||
|       <input | ||||
|         type="search" | ||||
|         bind:value={searchvalue} | ||||
|         placeholder={$_('datatable.search')} | ||||
|         aria-label={$_('datatable.search')} | ||||
|         class="gridjs-input gridjs-search-input mb-4" /> | ||||
|       <div | ||||
|         class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> | ||||
|         <table class="divide-y divide-gray-200 w-full"> | ||||
|           <thead class="bg-gray-50"> | ||||
|             <tr> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 {$_('name')} | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 {$_('contact-information')} | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 {$_('group')} | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 {$_('distance-in-km')} | ||||
|               </th> | ||||
|               <th scope="col" class="relative px-6 py-3"> | ||||
|                 <span class="sr-only">{$_('action')}</span> | ||||
|               </th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody class="divide-y divide-gray-200"> | ||||
|             {#each current_runners as runner} | ||||
|               {#if Object.values(runner) | ||||
|                 .toString() | ||||
|                 .toLowerCase() | ||||
|                 .includes(searchvalue)} | ||||
|                 <tr data-rowid="user_{runner.id}"> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       <div class="ml-4"> | ||||
|                         <div class="text-sm font-medium text-gray-900"> | ||||
|                           {runner.firstname} | ||||
|                           {runner.middlename || ''} | ||||
|                           {runner.lastname} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     {#if runner.email} | ||||
|                       <div class="text-sm text-gray-500">{runner.email}</div> | ||||
|                     {/if} | ||||
|                     {#if runner.phone} | ||||
|                       <div class="text-sm text-gray-500">{runner.phone}</div> | ||||
|                     {/if} | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     {runner.group.name} | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap">{runner.distance}</td> | ||||
|                   {#if active_deletes[runner.id] === true} | ||||
|                     <td | ||||
|                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           active_deletes[runner.id] = false; | ||||
|                         }} | ||||
|                         tabindex="0" | ||||
|                         class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel | ||||
|                         Delete</button> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           RunnerService.runnerControllerRemove(runner.id, true) | ||||
|                             .then((resp) => { | ||||
|                               current_runners = current_runners.filter((obj) => obj.id !== runner.id); | ||||
|                             }) | ||||
|                             .catch((err) => { | ||||
|                               // error deleting user | ||||
|                             }); | ||||
|                         }} | ||||
|                         tabindex="0" | ||||
|                         class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('confirm-delete')}</button> | ||||
|                     </td> | ||||
|                   {:else} | ||||
|                     <td | ||||
|                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                       <a | ||||
|                         href="./{runner.id}" | ||||
|                         class="text-indigo-600 hover:text-indigo-900">Edit</a> | ||||
|                       {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:DELETE')} | ||||
|                         <button | ||||
|                           on:click={() => { | ||||
|                             active_deletes[runner.id] = true; | ||||
|                           }} | ||||
|                           tabindex="0" | ||||
|                           class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button> | ||||
|                       {/if} | ||||
|                     </td> | ||||
|                   {/if} | ||||
|                 </tr> | ||||
|               {/if} | ||||
|             {/each} | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|     {/if} | ||||
|   {:catch error} | ||||
|     <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|       <span class="inline-block align-middle mr-8"> | ||||
|         <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|         {error} | ||||
|       </span> | ||||
|     </div> | ||||
|   {/await} | ||||
| {/if} | ||||
| @@ -18,7 +18,7 @@ | ||||
|     class="flex flex-col lg:flex-row w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4"> | ||||
|     <a href="/runners/" class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
| @@ -39,7 +39,7 @@ | ||||
|     </a> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
| @@ -64,7 +64,7 @@ | ||||
|     </div> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
| @@ -83,7 +83,7 @@ | ||||
|     </div> | ||||
|     <div class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
| @@ -106,7 +106,7 @@ | ||||
|     </div> | ||||
|     <a href="/teams/" class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
| @@ -135,7 +135,7 @@ | ||||
|     </a> | ||||
|     <a href="/orgs/" class="w-full lg:w-1/4"> | ||||
|       <div | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white"> | ||||
|         class="widget w-full p-4 rounded-lg bg-white border border-grey-100"> | ||||
|         <div class="flex flex-row items-center justify-between"> | ||||
|           <div class="flex flex-col"> | ||||
|             <div class="text-xs uppercase font-light text-grey-500"> | ||||
|   | ||||
							
								
								
									
										244
									
								
								src/components/TeamDetail.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								src/components/TeamDetail.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | ||||
| <script> | ||||
|   import { | ||||
|     RunnerOrganizationService, | ||||
|     RunnerTeamService, | ||||
|   } from "@odit/lfk-client-js"; | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import store from "../store"; | ||||
|   import ImportRunnerModal from "./ImportRunnerModal.svelte"; | ||||
|   import PromiseError from "./PromiseError.svelte"; | ||||
|   import ConfirmTeamDeletion from "./ConfirmTeamDeletion.svelte"; | ||||
|   export let params; | ||||
|   let [teamdata, original, delete_team, orgs, modal_open] = [ | ||||
|     {}, | ||||
|     {}, | ||||
|     {}, | ||||
|     [], | ||||
|     false, | ||||
|   ]; | ||||
|   export let import_modal_open = false; | ||||
|   $: delete_triggered = false; | ||||
|   $: save_enabled = !data_changed; | ||||
|   $: data_loaded = false; | ||||
|   $: data_changed = JSON.stringify(teamdata) === JSON.stringify(original); | ||||
|   // | ||||
|   const promise = RunnerTeamService.runnerTeamControllerGetOne( | ||||
|     params.teamid | ||||
|   ).then((value) => { | ||||
|     data_loaded = true; | ||||
|     teamdata = Object.assign(teamdata, value); | ||||
|     original = Object.assign(original, value); | ||||
|   }); | ||||
|   RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => { | ||||
|     orgs = val; | ||||
|   }); | ||||
|   function deleteTeam() { | ||||
|     RunnerTeamService.runnerTeamControllerRemove(original.id, false) | ||||
|       .then((resp) => { | ||||
|         Toastify({ | ||||
|           text: "Organization deleted", | ||||
|           duration: 500, | ||||
|           backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|         }).showToast(); | ||||
|         location.replace("./"); | ||||
|       }) | ||||
|       .catch((err) => { | ||||
|         modal_open = true; | ||||
|         delete_team = original; | ||||
|       }); | ||||
|   } | ||||
|   function submit() { | ||||
|     if (data_loaded === true && save_enabled) { | ||||
|       Toastify({ | ||||
|         text: "updating team", | ||||
|         duration: 2500, | ||||
|       }).showToast(); | ||||
|       teamdata.parentGroup = teamdata.parentGroup.id; | ||||
|       RunnerTeamService.runnerTeamControllerPut(original.id, teamdata) | ||||
|         .then((resp) => { | ||||
|           Object.assign(original, teamdata); | ||||
|           original = teamdata; | ||||
|           Object.assign(original, teamdata); | ||||
|           // | ||||
|           Toastify({ | ||||
|             text: "updated team", | ||||
|             duration: 2500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|         }) | ||||
|         .catch((err) => {}); | ||||
|     } else { | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <ImportRunnerModal | ||||
|   on:cancelDelete={(event) => { | ||||
|     import_modal_open = false; | ||||
|   }} | ||||
|   passed_team={teamdata} | ||||
|   passed_orgs={[]} | ||||
|   passed_org={{}} | ||||
|   opened_from="TeamDetail" | ||||
|   bind:import_modal_open /> | ||||
| <ConfirmTeamDeletion bind:modal_open bind:delete_team /> | ||||
| {#if data_loaded} | ||||
|   <section class="container p-5"> | ||||
|     <div class="mb-8 text-3xl font-extrabold leading-tight"> | ||||
|       {original.name} | ||||
|       <span data-id="org_actions_${teamdata.id}"> | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:IMPORT')} | ||||
|           <button | ||||
|             on:click={() => { | ||||
|               import_modal_open = true; | ||||
|             }} | ||||
|             type="button" | ||||
|             class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|             {$_('import-runners')} | ||||
|           </button> | ||||
|         {/if} | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:DELETE')} | ||||
|           {#if delete_triggered} | ||||
|             <button | ||||
|               on:click={deleteTeam} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-delete')}</button> | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = !delete_triggered; | ||||
|               }} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> | ||||
|           {/if} | ||||
|           {#if !delete_triggered} | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = true; | ||||
|               }} | ||||
|               type="button" | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-team')}</button> | ||||
|           {/if} | ||||
|         {/if} | ||||
|         {#if !delete_triggered} | ||||
|           <button | ||||
|             on:click={submit} | ||||
|             disabled={!save_enabled} | ||||
|             class:opacity-50={!save_enabled} | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> | ||||
|         {/if} | ||||
|       </span> | ||||
|     </div> | ||||
|     <div class="flex flex-row mb-4"> | ||||
|       <div class="w-full"> | ||||
|         <nav class="w-full flex"> | ||||
|           <ol class="list-none flex flex-row items-center justify-start"> | ||||
|             <li class="mr-2 flex items-center"> | ||||
|               <svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><path | ||||
|                   d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> | ||||
|                 <polyline points="9 22 9 12 15 12 15 22" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <a class="mr-2" href="/">Home</a><svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="mr-2 flex items-center"> | ||||
|               <svg | ||||
|                 class="flex-shrink-0 w-5 h-5 mr-2" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <a class="mr-2" href="./">Teams</a><svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <span class="mr-2">Team-Details #{params.teamid}</span> | ||||
|             </li> | ||||
|           </ol> | ||||
|         </nav> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label for="name" class="font-medium text-gray-700">Name</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder="Name" | ||||
|         type="text" | ||||
|         bind:value={teamdata.name} | ||||
|         name="name" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
|         for="contact" | ||||
|         class="font-medium text-gray-700">{$_('contact')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('contact')} | ||||
|         type="text" | ||||
|         bind:value={teamdata.contact} | ||||
|         name="contact" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label for="org" class="font-medium text-gray-700">Parent Organization</label> | ||||
|       <select | ||||
|         name="org" | ||||
|         bind:value={teamdata.parentGroup} | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2"> | ||||
|         {#each orgs as o} | ||||
|           <option value={o.id}>{o.name}</option> | ||||
|         {/each} | ||||
|       </select> | ||||
|     </div> | ||||
|   </section> | ||||
| {:else} | ||||
|   {#await promise} | ||||
|     team detail is being loaded... | ||||
|   {:catch error} | ||||
|     <PromiseError /> | ||||
|   {/await} | ||||
| {/if} | ||||
| @@ -1,10 +1,31 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import store from "../store"; | ||||
|   import AddTeamModal from "./AddTeamModal.svelte"; | ||||
|   export let modal_open = false; | ||||
|   import TeamsOverview from "./TeamsOverview.svelte"; | ||||
|   console.log(store.state.jwtinfo.userdetails.permissions); | ||||
|   let current_teams=[]; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     {$_('teams')} | ||||
|     {#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')} | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           modal_open = true; | ||||
|         }} | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|         Create Team | ||||
|       </button> | ||||
|     {/if} | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500">everything is more fun together 🏃♂️🏃♀️🏃♂️</p> | ||||
|   <TeamsOverview bind:current_teams /> | ||||
| </section> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')} | ||||
|   <AddTeamModal bind:current_teams bind:modal_open /> | ||||
| {/if} | ||||
|   | ||||
							
								
								
									
										17
									
								
								src/components/TeamsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/components/TeamsEmptyState.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import AddTeamModal from "./AddTeamModal.svelte"; | ||||
|   import team_empty from "./team_empty.svg"; | ||||
|   let modal_open = false; | ||||
|   let current_teams = []; | ||||
| </script> | ||||
|  | ||||
| <div class="text-center items-center justify-center"> | ||||
|   <p class="mb-16 text-lg text-gray-500"> | ||||
|     <img class="w-full h-44" src={team_empty} alt="" /> | ||||
|     <span class="font-bold">There are no teams added yet.</span><br /> | ||||
|     <span>Add your first team</span> | ||||
|   </p> | ||||
| </div> | ||||
|  | ||||
| <AddTeamModal bind:modal_open bind:current_teams /> | ||||
							
								
								
									
										176
									
								
								src/components/TeamsOverview.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/components/TeamsOverview.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,176 @@ | ||||
| <script> | ||||
|   import { t, _ } from "svelte-i18n"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import { RunnerTeamService } from "@odit/lfk-client-js"; | ||||
|   const teams_promise = RunnerTeamService.runnerTeamControllerGetAll(); | ||||
|   import { users as usersstore } from "../store.js"; | ||||
|   import store from "../store"; | ||||
|   import TeamsEmptyState from "./TeamsEmptyState.svelte"; | ||||
|   import ConfirmTeamDeletion from "./ConfirmTeamDeletion.svelte"; | ||||
|   $: searchvalue = ""; | ||||
|   $: active_deletes = []; | ||||
|   export let current_teams = []; | ||||
|   let modal_open = false; | ||||
|   let delete_team = {}; | ||||
|   usersstore.subscribe((val) => { | ||||
|     current_teams = val; | ||||
|   }); | ||||
|   teams_promise.then((data) => { | ||||
|     usersstore.set(data); | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| <ConfirmTeamDeletion | ||||
|   on:cancelDelete={(event) => { | ||||
|     modal_open = false; | ||||
|     active_deletes[event.detail.id] = false; | ||||
|   }} | ||||
|   bind:modal_open | ||||
|   bind:delete_team /> | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:GET')} | ||||
|   {#await teams_promise} | ||||
|     <div | ||||
|       class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|       role="alert"> | ||||
|       <p class="font-bold">teams are being loaded...</p> | ||||
|       <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|     </div> | ||||
|   {:then} | ||||
|     {#if current_teams.length === 0} | ||||
|       <TeamsEmptyState /> | ||||
|     {:else} | ||||
|       <input | ||||
|         type="search" | ||||
|         bind:value={searchvalue} | ||||
|         placeholder={$_('datatable.search')} | ||||
|         aria-label={$_('datatable.search')} | ||||
|         class="gridjs-input gridjs-search-input mb-4" /> | ||||
|       <div | ||||
|         class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> | ||||
|         <table class="divide-y divide-gray-200 w-full"> | ||||
|           <thead class="bg-gray-50"> | ||||
|             <tr> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Name | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 {$_('organization')} | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Contact | ||||
|               </th> | ||||
|               <th scope="col" class="relative px-6 py-3"> | ||||
|                 <span class="sr-only">Action</span> | ||||
|               </th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody class="divide-y divide-gray-200"> | ||||
|             {#each current_teams as t} | ||||
|               {#if Object.values(t) | ||||
|                 .toString() | ||||
|                 .toLowerCase() | ||||
|                 .includes(searchvalue)} | ||||
|                 <tr data-rowid="team_{t.id}"> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       <div class="ml-4"> | ||||
|                         <div | ||||
|                           class="text-sm font-medium text-gray-900"> | ||||
|                           {t.name} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       <div class="ml-4"> | ||||
|                         <div | ||||
|                           class="text-sm font-medium text-gray-900"> | ||||
|                           {#if t.parentGroup} | ||||
|                             {t.parentGroup.name} | ||||
|                           {:else}no organization specified{/if} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       <div class="ml-4"> | ||||
|                         <div | ||||
|                           class="text-sm font-medium text-gray-900"> | ||||
|                           {#if t.contact} | ||||
|                             {JSON.stringify(t.contact)} | ||||
|                           {:else}no contact specified{/if} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                   {#if active_deletes[t.id] === true} | ||||
|                     <td | ||||
|                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           active_deletes[t.id] = false; | ||||
|                         }} | ||||
|                         tabindex="0" | ||||
|                         class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel | ||||
|                         Delete</button> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           RunnerTeamService.runnerTeamControllerRemove(t.id, false) | ||||
|                             .then((resp) => { | ||||
|                               current_teams = current_teams.filter((obj) => obj.id !== t.id); | ||||
|                               Toastify({ | ||||
|                                 text: 'Organization deleted', | ||||
|                                 duration: 500, | ||||
|                                 backgroundColor: | ||||
|                                   'linear-gradient(to right, #00b09b, #96c93d)', | ||||
|                               }).showToast(); | ||||
|                             }) | ||||
|                             .catch((err) => { | ||||
|                               modal_open = true; | ||||
|                               delete_team = t; | ||||
|                             }); | ||||
|                         }} | ||||
|                         tabindex="0" | ||||
|                         class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Confirm | ||||
|                         Delete</button> | ||||
|                     </td> | ||||
|                   {:else} | ||||
|                     <td | ||||
|                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                       <a | ||||
|                         href="./{t.id}" | ||||
|                         class="text-indigo-600 hover:text-indigo-900">Edit</a> | ||||
|                       {#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:DELETE')} | ||||
|                         <button | ||||
|                           on:click={() => { | ||||
|                             active_deletes[t.id] = true; | ||||
|                           }} | ||||
|                           tabindex="0" | ||||
|                           class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</button> | ||||
|                       {/if} | ||||
|                     </td> | ||||
|                   {/if} | ||||
|                 </tr> | ||||
|               {/if} | ||||
|             {/each} | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|     {/if} | ||||
|   {:catch error} | ||||
|     <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|       <span class="inline-block align-middle mr-8"> | ||||
|         <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|         {error} | ||||
|       </span> | ||||
|     </div> | ||||
|   {/await} | ||||
| {/if} | ||||
| @@ -6,7 +6,6 @@ | ||||
|   const tracks_promise = TrackService.trackControllerGetAll(); | ||||
|   import { getlang } from "./datatable_i18n"; | ||||
|   import { Grid, html } from "gridjs"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   import { tracks as tracksstore } from "../store.js"; | ||||
|   $: trackscache = []; | ||||
|   $: blocked = []; | ||||
|   | ||||
| @@ -1,28 +1,112 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import lodashIsEqual from "lodash.isequal"; | ||||
|   import { UserService } from "@odit/lfk-client-js"; | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   import store from "../store"; | ||||
|   import { UserService, UserGroupService } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import PromiseError from "./PromiseError.svelte"; | ||||
|   export let params; | ||||
|   const user_promise = UserService.userControllerGetOne(params.userid); | ||||
|   let data_loaded = false; | ||||
|   let original_data = undefined; | ||||
|   $: editable_userdata = undefined; | ||||
|   let usergroups_array_original = []; | ||||
|   const colors = [ | ||||
|     "#f3558e", | ||||
|     "#17b978", | ||||
|     "#3498db", | ||||
|     "#3f3b3b", | ||||
|     "#775ada", | ||||
|     "#7ed6df_#000000", | ||||
|     "#000000", | ||||
|     "#21e6c1_#000000", | ||||
|     "#c0392b", | ||||
|     "#d35400", | ||||
|     "#7f8c8d", | ||||
|     "#6ab04c", | ||||
|     "#4834d4", | ||||
|     "#ff1f5a", | ||||
|     "#eac100", | ||||
|   ]; | ||||
|   let matched_colors = []; | ||||
|   $: delete_triggered = false; | ||||
|   $: original_data = {}; | ||||
|   $: editable_userdata = {}; | ||||
|   $: allgroups = []; | ||||
|   $: allgroups_ids = []; | ||||
|   $: usergroups_array = []; | ||||
|   $: search_permission = ""; | ||||
|   user_promise.then((data) => { | ||||
|     let current_target = ""; | ||||
|     let colorindex = -1; | ||||
|     // alphabetically sort permissions for color compatibility for target | ||||
|     data.permissions = data.permissions.sort(); | ||||
|     data.permissions.forEach((p) => { | ||||
|       const target = p.split(":")[0]; | ||||
|       if (current_target !== p.split(":")[0]) { | ||||
|         colorindex++; | ||||
|         current_target = p.split(":")[0]; | ||||
|       } | ||||
|       let background = colors[colorindex]; | ||||
|       let foreground = "#fff"; | ||||
|       if (background.includes("_")) { | ||||
|         foreground = background.split("_")[1]; | ||||
|         background = background.split("_")[0]; | ||||
|       } | ||||
|       matched_colors[target] = [background, foreground]; | ||||
|     }); | ||||
|     // | ||||
|     data_loaded = true; | ||||
|     original_data = data; | ||||
|     original_data = Object.assign(original_data, data); | ||||
|     editable_userdata = data; | ||||
|     data.groups.forEach((g) => { | ||||
|       usergroups_array = usergroups_array.concat([g.id]); | ||||
|     }); | ||||
|     usergroups_array_original = usergroups_array; | ||||
|     allgroups.forEach((g) => { | ||||
|       allgroups_ids.push(g.id); | ||||
|     }); | ||||
|   }); | ||||
|   // $: changes_performed = lodashIsEqual(original_data, editable_userdata); | ||||
|   $: changes_performed = !lodashIsEqual({ test: 1 }, { test: 1 }); | ||||
|   UserGroupService.userGroupControllerGetAll().then((data) => { | ||||
|     allgroups = data; | ||||
|   }); | ||||
|   $: changes_performed = !lodashIsEqual(original_data, editable_userdata); | ||||
|   $: groups_changed = | ||||
|     JSON.stringify(usergroups_array) === | ||||
|     JSON.stringify(usergroups_array_original); | ||||
|   $: save_enabled = changes_performed || !groups_changed; | ||||
|   function submit() { | ||||
|     if (data_loaded === true && changes_performed === true) { | ||||
|       console.log("ok, submitting..."); | ||||
|     } else { | ||||
|       console.log("no changes performed"); | ||||
|     if (data_loaded === true && save_enabled) { | ||||
|       editable_userdata.groups = usergroups_array; | ||||
|       Toastify({ | ||||
|         text: $_("updating-user"), | ||||
|         duration: 2500, | ||||
|       }).showToast(); | ||||
|       UserService.userControllerPut(original_data.id, editable_userdata) | ||||
|         .then((resp) => { | ||||
|           Object.assign(original_data, resp); | ||||
|           Object.assign(editable_userdata, resp); | ||||
|           original_data.permissions = resp.permissions; | ||||
|           usergroups_array = []; | ||||
|           resp.groups.forEach((g) => { | ||||
|             usergroups_array = usergroups_array.concat([g.id]); | ||||
|           }); | ||||
|           usergroups_array_original = usergroups_array; | ||||
|           // | ||||
|           Toastify({ | ||||
|             text: $_("user-updated"), | ||||
|             duration: 2500, | ||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|           }).showToast(); | ||||
|         }) | ||||
|         .catch((err) => {}); | ||||
|     } | ||||
|   } | ||||
|   function deleteUser() { | ||||
|     UserService.userControllerRemove(original_data.id, true) | ||||
|       .then((resp) => { | ||||
|         location.replace("./"); | ||||
|       }) | ||||
|       .catch((err) => {}); | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| {#await user_promise} | ||||
| @@ -63,71 +147,73 @@ | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <span class="mr-2">{user.firstname} | ||||
|                 {user.middlename || ''} | ||||
|                 {user.lastname}</span> | ||||
|               <span class="mr-2">{original_data.firstname} | ||||
|                 {original_data.middlename || ''} | ||||
|                 {original_data.lastname}</span> | ||||
|             </li> | ||||
|           </ol> | ||||
|         </nav> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="mb-8 text-3xl font-extrabold leading-tight"> | ||||
|       {user.firstname} | ||||
|       {user.middlename || ''} | ||||
|       {user.lastname} | ||||
|       <span data-id="user_actions_${user.id}"> | ||||
|         <button | ||||
|           class="hidden w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-400 text-base font-medium text-white sm:w-auto sm:text-sm" | ||||
|           data-userid="${user.id}" | ||||
|           onclick="user__delete_cancel()">{$_('cancel')}</button> | ||||
|         <button | ||||
|           class="hidden w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-500 text-base font-medium text-white sm:w-auto sm:text-sm" | ||||
|           data-userid="${user.id}" | ||||
|           onclick="user__delete_confirm()">{$_('confirm-delete')}</button> | ||||
|         <button | ||||
|           type="button" | ||||
|           class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-user')}</button> | ||||
|         <button | ||||
|           disabled={!changes_performed} | ||||
|           class:opacity-50={!changes_performed} | ||||
|           type="button" | ||||
|           on:click={submit} | ||||
|           class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> | ||||
|     <div class="mb-8 text-3xl font-extrabold"> | ||||
|       {original_data.firstname} | ||||
|       {original_data.middlename || ''} | ||||
|       {original_data.lastname} | ||||
|       <span data-id="user_actions_${editable_userdata.id}"> | ||||
|         {#if store.state.jwtinfo.userdetails.permissions.includes('USER:DELETE')} | ||||
|           {#if delete_triggered} | ||||
|             <button | ||||
|               on:click={deleteUser} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-delete')}</button> | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = !delete_triggered; | ||||
|               }} | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> | ||||
|           {/if} | ||||
|           {#if !delete_triggered} | ||||
|             <button | ||||
|               on:click={() => { | ||||
|                 delete_triggered = true; | ||||
|               }} | ||||
|               type="button" | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-user')}</button> | ||||
|           {/if} | ||||
|         {/if} | ||||
|         {#if !delete_triggered} | ||||
|           <button | ||||
|             disabled={!save_enabled} | ||||
|             class:opacity-50={!save_enabled} | ||||
|             type="button" | ||||
|             on:click={submit} | ||||
|             class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> | ||||
|         {/if} | ||||
|       </span> | ||||
|     </div> | ||||
|  | ||||
|     <!--  --> | ||||
|     <div class="mt-2 flex items-center"> | ||||
|     <div class="mt-3 text-sm w-full"> | ||||
|       <p class="ml-1 font-medium text-gray-700">Profile Picture</p> | ||||
|       <img | ||||
|         alt={$_('profile-picture')} | ||||
|         class="inline-block h-20 w-20 rounded-full overflow-hidden bg-gray-100" | ||||
|         src={user.profilePic} /> | ||||
|       <!-- <span | ||||
|         class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100"><svg | ||||
|           class="h-full w-full text-gray-300" | ||||
|           fill="currentColor" | ||||
|           viewBox="0 0 24 24"><path | ||||
|             d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" /></svg></span> --> | ||||
|       <button | ||||
|         type="button" | ||||
|         class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Change</button> | ||||
|         class="h-20 w-20 rounded-full overflow-hidden bg-gray-100" | ||||
|         src={editable_userdata.profilePic} /> | ||||
|     </div> | ||||
|     <!--  --> | ||||
|     <div class="mt-3 text-sm w-full"> | ||||
|       <input | ||||
|         id="enabled" | ||||
|         on:change={() => { | ||||
|           editable_userdata.enabled = !editable_userdata.enabled; | ||||
|           // TODO: this reactive set does not work? | ||||
|         }} | ||||
|         name="enabled" | ||||
|         type="checkbox" | ||||
|         checked={editable_userdata.enabled} | ||||
|         class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> | ||||
|       <label | ||||
|         for="enabled" | ||||
|         class="ml-1 font-medium text-gray-700">Active?</label> | ||||
|       <p class="text-gray-500">set the user active/ inactive</p> | ||||
|       <br /> | ||||
|       <p class="text-gray-500"> | ||||
|         <input | ||||
|           id="enabled" | ||||
|           on:change={() => { | ||||
|             editable_userdata.enabled = !editable_userdata.enabled; | ||||
|           }} | ||||
|           name="enabled" | ||||
|           type="checkbox" | ||||
|           checked={editable_userdata.enabled} | ||||
|           class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" /> | ||||
|         set the user active/ inactive | ||||
|       </p> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <label | ||||
| @@ -178,65 +264,56 @@ | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <span class="font-medium">{$_('groups')}</span> | ||||
|       <select | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" | ||||
|         multiple> | ||||
|         <option>Admins</option> | ||||
|         <option>Finanzen</option> | ||||
|         <option>...</option> | ||||
|       </select> | ||||
|       <label | ||||
|         for="username" | ||||
|         class="font-medium text-gray-700">{$_('username')}</label> | ||||
|       <input | ||||
|         autocomplete="off" | ||||
|         placeholder={$_('username')} | ||||
|         type="text" | ||||
|         bind:value={editable_userdata.username} | ||||
|         name="username" | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||
|     </div> | ||||
|     <div class="text-sm w-full"> | ||||
|       <span class="font-medium">Permissions</span> | ||||
|       <div | ||||
|         class="border-4 border-dashed rounded h-96 mb-4 p-5 text-lg text-center"> | ||||
|         <!--  --> | ||||
|         <div class="flex flex-wrap -mx-1 overflow-hidden"> | ||||
|           <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> | ||||
|             <button | ||||
|               type="button" | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">></button> | ||||
|           </div> | ||||
|  | ||||
|           <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> | ||||
|             <button | ||||
|               type="button" | ||||
|               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm"><</button> | ||||
|           </div> | ||||
|         </div> | ||||
|         <!--  --> | ||||
|         <div class="flex flex-wrap -mx-1 overflow-hidden"> | ||||
|           <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> | ||||
|             <div | ||||
|               class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||
|               <p | ||||
|                 class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||
|                 DEMO_PERMISSION | ||||
|               </p> | ||||
|               <p | ||||
|                 class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||
|                 DEMO_PERMISSION | ||||
|               </p> | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
|           <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> | ||||
|             <div | ||||
|               class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||
|               <p | ||||
|                 class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||
|                 DEMO_PERMISSION | ||||
|               </p> | ||||
|               <p | ||||
|                 class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||
|                 DEMO_PERMISSION | ||||
|               </p> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|         TODO: permission picker 🔒 | ||||
|       <span class="font-medium">{$_('groups')}</span> | ||||
|       <!-- svelte-ignore a11y-no-onchange --> | ||||
|       <select | ||||
|         bind:value={usergroups_array} | ||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" | ||||
|         multiple> | ||||
|         {#each allgroups as g} | ||||
|           {#if usergroups_array.includes(g.id)} | ||||
|             <option selected value={g.id}>{g.name}</option> | ||||
|           {:else} | ||||
|             <option value={g.id}>{g.name}</option> | ||||
|           {/if} | ||||
|         {/each} | ||||
|       </select> | ||||
|     </div> | ||||
|     <div class="text-sm w-full mt-8"> | ||||
|       <p class="font-medium mb-4"> | ||||
|         {$_('permissions')} | ||||
|         <a | ||||
|           class="px-4 py-2 bg-gray-500 rounded-md text-white" | ||||
|           href="/users/{params.userid}/permissions/">{$_('edit-permissions')}</a> | ||||
|       </p> | ||||
|       <div class="w-full sm:my-px sm:px-px sm:w-1/2"> | ||||
|         <input | ||||
|           autocomplete="off" | ||||
|           placeholder="Search for permission" | ||||
|           type="text" | ||||
|           bind:value={search_permission} | ||||
|           class="mt-4 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||
|       </div> | ||||
|       {#each original_data.permissions as p} | ||||
|         {#if p.toLowerCase().includes(search_permission.toLowerCase())} | ||||
|           <span | ||||
|             style="background:{matched_colors[p.split(':')[0]][0]};color:{matched_colors[p.split(':')[0]][1]};" | ||||
|             class="mt-1 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-indigo-100 rounded">{p}</span> | ||||
|           <!--  --> | ||||
|         {/if} | ||||
|       {/each} | ||||
|     </div> | ||||
|   </section> | ||||
| {:catch error} | ||||
|   | ||||
							
								
								
									
										241
									
								
								src/components/UserPermissions.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								src/components/UserPermissions.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import { | ||||
|     UserService, | ||||
|     PermissionService, | ||||
|     CreatePermission, | ||||
|   } from "@odit/lfk-client-js"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import PromiseError from "./PromiseError.svelte"; | ||||
|   export let params; | ||||
|   let [ | ||||
|     grantedPermissions_initial, | ||||
|     grantedPermissions, | ||||
|     inheritedPermissions, | ||||
|     to_add, | ||||
|     to_delete, | ||||
|     allpermissions, | ||||
|     promises, | ||||
|   ] = [[], [], [], [], [], [], []]; | ||||
|   $: original_data = {}; | ||||
|   $: save_enabled = | ||||
|     JSON.stringify(grantedPermissions) === | ||||
|     JSON.stringify(grantedPermissions_initial); | ||||
|   const user_promise = UserService.userControllerGetOne(params.userid); | ||||
|   user_promise.then((data) => { | ||||
|     original_data = Object.assign(original_data, data); | ||||
|   }); | ||||
|   function submit() { | ||||
|     Toastify({ | ||||
|       text: "updating permissions...", | ||||
|       duration: 2500, | ||||
|     }).showToast(); | ||||
|     to_delete.forEach((d) => { | ||||
|       promises = promises.concat([ | ||||
|         PermissionService.permissionControllerRemove(d, true), | ||||
|       ]); | ||||
|     }); | ||||
|     to_add.forEach((a) => { | ||||
|       promises = promises.concat([ | ||||
|         PermissionService.permissionControllerPost(a), | ||||
|       ]); | ||||
|     }); | ||||
|     Promise.all(promises).then((values) => { | ||||
|       promises = []; | ||||
|       to_delete.forEach((d) => { | ||||
|         to_delete = to_delete.filter((o) => o !== d); | ||||
|       }); | ||||
|       to_add.forEach((a) => { | ||||
|         to_add = to_add.filter( | ||||
|           (o) => o.target + ":" + o.action !== a.target + ":" + a.action | ||||
|         ); | ||||
|       }); | ||||
|       Toastify({ | ||||
|         text: "Permissions updated!", | ||||
|         duration: 2500, | ||||
|         backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||
|       }).showToast(); | ||||
|     }); | ||||
|   } | ||||
|   Object.values(CreatePermission.target).forEach((t) => { | ||||
|     Object.values(CreatePermission.action).forEach((a) => { | ||||
|       allpermissions = allpermissions.concat([{ target: t, action: a }]); | ||||
|     }); | ||||
|   }); | ||||
|   UserService.userControllerGetPermissions(params.userid).then((val) => { | ||||
|     val.inherited.forEach((p) => { | ||||
|       inheritedPermissions = inheritedPermissions.concat([p]); | ||||
|     }); | ||||
|     val.directlyGranted.forEach((p) => { | ||||
|       grantedPermissions = grantedPermissions.concat([p]); | ||||
|     }); | ||||
|     grantedPermissions_initial = grantedPermissions; | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| {#await user_promise} | ||||
|   <!--  --> | ||||
| {:then user} | ||||
|   <section class="container p-5 select-none"> | ||||
|     <div class="flex flex-row mb-4"> | ||||
|       <div class="w-full"> | ||||
|         <nav class="w-full flex"> | ||||
|           <ol class="list-none flex flex-row items-center justify-start"> | ||||
|             <li class="flex items-center"> | ||||
|               <svg | ||||
|                 class="flex-shrink-0 w-5 h-5 mr-2" | ||||
|                 fill="currentColor" | ||||
|                 width="24" | ||||
|                 height="24" | ||||
|                 xmlns="http://www.w3.org/2000/svg" | ||||
|                 viewBox="0 0 640 512"><path | ||||
|                   fill="currentColor" | ||||
|                   d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <a class="mr-2" href="./">{$_('users')}</a><svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <span class="mr-2"><a href="../">{original_data.firstname} | ||||
|                   {original_data.middlename || ''} | ||||
|                   {original_data.lastname}</a></span> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <svg | ||||
|                 stroke="currentColor" | ||||
|                 fill="none" | ||||
|                 stroke-width="2" | ||||
|                 viewBox="0 0 24 24" | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 class="h-3 w-3 mr-2 stroke-current" | ||||
|                 height="1em" | ||||
|                 width="1em" | ||||
|                 xmlns="http://www.w3.org/2000/svg"><line | ||||
|                   x1="5" | ||||
|                   y1="12" | ||||
|                   x2="19" | ||||
|                   y2="12" /> | ||||
|                 <polyline points="12 5 19 12 12 19" /></svg> | ||||
|             </li> | ||||
|             <li class="flex items-center"> | ||||
|               <span class="mr-2">Permissions</span> | ||||
|             </li> | ||||
|           </ol> | ||||
|         </nav> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="mb-8 text-3xl font-extrabold"> | ||||
|       Permissions: | ||||
|       {original_data.firstname} | ||||
|       {original_data.middlename || ''} | ||||
|       {original_data.lastname} | ||||
|       <span> | ||||
|         {#if promises.length === 0} | ||||
|           <button | ||||
|             disabled={save_enabled} | ||||
|             class:opacity-50={save_enabled} | ||||
|             type="button" | ||||
|             on:click={submit} | ||||
|             class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button> | ||||
|         {:else} | ||||
|           <button | ||||
|             type="button" | ||||
|             class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-yellow-600 text-base font-medium text-white hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500 sm:ml-3 sm:w-auto sm:text-sm">Applying | ||||
|             Changes</button> | ||||
|         {/if} | ||||
|       </span> | ||||
|     </div> | ||||
|     <!--  --> | ||||
|     <div class="flex flex-wrap -mx-1 overflow-hidden"> | ||||
|       <div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">verfügbare</div> | ||||
|       <div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">erteilte</div> | ||||
|       <div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">geerbte</div> | ||||
|     </div> | ||||
|     <!--  --> | ||||
|     <div class="flex flex-wrap -mx-1 overflow-hidden"> | ||||
|       {#if allpermissions.length > 0} | ||||
|         <div class="my-1 px-1 w-full overflow-hidden sm:w-1/3"> | ||||
|           <div | ||||
|             class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||
|             {#each allpermissions as p} | ||||
|               {#if !grantedPermissions.includes(p)} | ||||
|                 <p | ||||
|                   class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||
|                   {p.target + ':' + p.action} | ||||
|                   <button | ||||
|                     on:click={() => { | ||||
|                       grantedPermissions = grantedPermissions.concat([p]); | ||||
|                       if (to_delete.some((o) => o === p.id)) { | ||||
|                         to_delete = to_delete.filter((o) => o !== p.id); | ||||
|                       } else { | ||||
|                         to_add = to_add.concat([ | ||||
|                           { | ||||
|                             action: p.action, | ||||
|                             target: p.target, | ||||
|                             principal: original_data.id, | ||||
|                           }, | ||||
|                         ]); | ||||
|                       } | ||||
|                     }} | ||||
|                     type="button" | ||||
|                     class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-200 text-base font-medium text-black hover:bg-green-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm">+</button> | ||||
|                 </p> | ||||
|               {/if} | ||||
|             {/each} | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="my-1 px-1 w-full overflow-hidden sm:w-1/3"> | ||||
|           <div | ||||
|             class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||
|             {#each grantedPermissions as p} | ||||
|               <p | ||||
|                 class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||
|                 {p.target + ':' + p.action} | ||||
|                 <button | ||||
|                   on:click={() => { | ||||
|                     grantedPermissions = grantedPermissions.filter((o) => o.target + ':' + o.action !== p.target + ':' + p.action); | ||||
|                     if (to_add.some((o) => o.target + ':' + o.action === p.target + ':' + p.action)) { | ||||
|                       to_add = to_add.filter((o) => o.target + ':' + o.action !== p.target + ':' + p.action); | ||||
|                     } else { | ||||
|                       to_delete = to_delete.concat([p.id]); | ||||
|                     } | ||||
|                   }} | ||||
|                   type="button" | ||||
|                   class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-300 text-base font-medium text-black hover:bg-red-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">-</button> | ||||
|               </p> | ||||
|             {/each} | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="my-1 px-1 w-full overflow-hidden sm:w-1/3"> | ||||
|           <div | ||||
|             class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||
|             {#each inheritedPermissions as p} | ||||
|               <p | ||||
|                 class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||
|                 {p.target + ':' + p.action} | ||||
|               </p> | ||||
|             {/each} | ||||
|           </div> | ||||
|         </div> | ||||
|       {/if} | ||||
|     </div> | ||||
|   </section> | ||||
| {:catch error} | ||||
|   <PromiseError {error} /> | ||||
| {/await} | ||||
| @@ -1,23 +1,31 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import store from "../store"; | ||||
|   import AddUserModal from "./AddUserModal.svelte"; | ||||
|   export let modal_open = false; | ||||
|   import UsersOverview from "./UsersOverview.svelte"; | ||||
|   console.log(store.state.jwtinfo.userdetails.permissions); | ||||
|   let current_users=[]; | ||||
| </script> | ||||
|  | ||||
| <section class="container p-5"> | ||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||
|     {$_('users')} | ||||
|     <button | ||||
|       on:click={() => { | ||||
|         modal_open = true; | ||||
|       }} | ||||
|       type="button" | ||||
|       class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|       {$_('create-user')} | ||||
|     </button> | ||||
|     {#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')} | ||||
|       <button | ||||
|         on:click={() => { | ||||
|           modal_open = true; | ||||
|         }} | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|         {$_('create-user')} | ||||
|       </button> | ||||
|     {/if} | ||||
|   </span> | ||||
|   <p class="mb-8 text-lg text-gray-500">{$_('manage-admin-users')}</p> | ||||
|   <UsersOverview /> | ||||
|   <UsersOverview bind:current_users /> | ||||
| </section> | ||||
| <AddUserModal bind:modal_open /> | ||||
|  | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')} | ||||
|   <AddUserModal bind:current_users bind:modal_open /> | ||||
| {/if} | ||||
|   | ||||
| @@ -1,11 +1,13 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import AddUserModal from "./AddUserModal.svelte"; | ||||
|   import users_empty from "./users_empty.svg"; | ||||
|   let modal_open = false; | ||||
| </script> | ||||
|  | ||||
| <div class="text-center items-center justify-center"> | ||||
|   <p class="mb-16 text-lg text-gray-500"> | ||||
|     <img class="w-full h-44" src={users_empty} alt="" /> | ||||
|     <span class="font-bold">There are no users added yet.</span><br /> | ||||
|     <span>Add your first user</span> | ||||
|   </p> | ||||
|   | ||||
| @@ -1,181 +1,178 @@ | ||||
| <script> | ||||
|   import { _ } from "svelte-i18n"; | ||||
|   import Toastify from "toastify-js"; | ||||
|   import { UserService } from "@odit/lfk-client-js"; | ||||
|   const users_promise = UserService.userControllerGetAll(); | ||||
|   import "gridjs/dist/theme/mermaid.css"; | ||||
|   import { users as usersstore } from "../store.js"; | ||||
|   import store from "../store"; | ||||
|   import UsersEmptyState from "./UsersEmptyState.svelte"; | ||||
|   $: searchvalue = ""; | ||||
|   $: active_deletes = []; | ||||
|   $: userscache = []; | ||||
|   export let current_users=[]; | ||||
|   $: advanced_search = false; | ||||
|   usersstore.subscribe((val) => { | ||||
|     userscache = val; | ||||
|     current_users=val; | ||||
|   }); | ||||
|   users_promise.then((data) => { | ||||
|     console.log(data); | ||||
|     usersstore.set(data); | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| {#await users_promise} | ||||
|   <div | ||||
|     class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|     role="alert"> | ||||
|     <p class="font-bold">users are being loaded...</p> | ||||
|     <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|   </div> | ||||
| {:then users} | ||||
|   {#if userscache.length === 0} | ||||
|     <UsersEmptyState /> | ||||
|   {:else} | ||||
|     {#if advanced_search} | ||||
|       advanced search | ||||
|     {:else} | ||||
|       <input | ||||
|         type="search" | ||||
|         bind:value={searchvalue} | ||||
|         placeholder={$_('datatable.search')} | ||||
|         aria-label={$_('datatable.search')} | ||||
|         class="gridjs-input gridjs-search-input mb-4" /> | ||||
|     {/if} | ||||
|     <button | ||||
|       on:click={() => { | ||||
|         advanced_search = !advanced_search; | ||||
|       }} | ||||
|       type="button" | ||||
|       class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|       {#if advanced_search} | ||||
|         toggle simple search | ||||
|       {:else}toggle advanced search{/if} | ||||
|     </button> | ||||
| {#if store.state.jwtinfo.userdetails.permissions.includes('USER:GET')} | ||||
|   {#await users_promise} | ||||
|     <div | ||||
|       class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> | ||||
|       <table class="divide-y divide-gray-200 w-full"> | ||||
|         <thead class="bg-gray-50"> | ||||
|           <tr> | ||||
|             <th | ||||
|               scope="col" | ||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|               Name | ||||
|             </th> | ||||
|             <th | ||||
|               scope="col" | ||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|               Status | ||||
|             </th> | ||||
|             <th | ||||
|               scope="col" | ||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|               Groups | ||||
|             </th> | ||||
|             <th scope="col" class="relative px-6 py-3"> | ||||
|               <span class="sr-only">Action</span> | ||||
|             </th> | ||||
|           </tr> | ||||
|         </thead> | ||||
|         <tbody class="divide-y divide-gray-200"> | ||||
|           {#each users as u} | ||||
|             {#if Object.values(u) | ||||
|               .toString() | ||||
|               .toLowerCase() | ||||
|               .includes(searchvalue)} | ||||
|               <tr> | ||||
|                 <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                   <div class="flex items-center"> | ||||
|                     {#if u.profilePic} | ||||
|                       <div class="flex-shrink-0 h-10 w-10"> | ||||
|                         <img | ||||
|                           class="h-10 w-10 rounded-full" | ||||
|                           src={u.profilePic} | ||||
|                           alt="" /> | ||||
|                       </div> | ||||
|                     {/if} | ||||
|                     <div class="ml-4"> | ||||
|                       <div | ||||
|                         class="text-sm font-medium text-gray-900 dark:text-gray-100"> | ||||
|                         {u.firstname} | ||||
|                         {u.middlename || ''} | ||||
|                         {u.lastname} | ||||
|                       </div> | ||||
|                       <div class="text-sm text-gray-500"> | ||||
|                         {u.email || u.username} | ||||
|       class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||
|       role="alert"> | ||||
|       <p class="font-bold">users are being loaded...</p> | ||||
|       <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||
|     </div> | ||||
|   {:then} | ||||
|     {#if current_users.length === 0} | ||||
|       <UsersEmptyState /> | ||||
|     {:else} | ||||
|       <!-- {#if advanced_search} | ||||
|         advanced search | ||||
|       {:else} --> | ||||
|         <input | ||||
|           type="search" | ||||
|           bind:value={searchvalue} | ||||
|           placeholder={$_('datatable.search')} | ||||
|           aria-label={$_('datatable.search')} | ||||
|           class="gridjs-input gridjs-search-input mb-4" /> | ||||
|       <!-- {/if} --> | ||||
|       <!-- <button | ||||
|         on:click={() => { | ||||
|           advanced_search = !advanced_search; | ||||
|         }} | ||||
|         type="button" | ||||
|         class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm"> | ||||
|         {#if advanced_search} | ||||
|           toggle simple search | ||||
|         {:else}toggle advanced search{/if} | ||||
|       </button> --> | ||||
|       <div | ||||
|         class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> | ||||
|         <table class="divide-y divide-gray-200 w-full"> | ||||
|           <thead class="bg-gray-50"> | ||||
|             <tr> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Name | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Status | ||||
|               </th> | ||||
|               <th | ||||
|                 scope="col" | ||||
|                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||
|                 Groups | ||||
|               </th> | ||||
|               <th scope="col" class="relative px-6 py-3"> | ||||
|                 <span class="sr-only">Action</span> | ||||
|               </th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody class="divide-y divide-gray-200"> | ||||
|             {#each current_users as u} | ||||
|               {#if Object.values(u) | ||||
|                 .toString() | ||||
|                 .toLowerCase() | ||||
|                 .includes(searchvalue)} | ||||
|                 <tr data-rowid="user_{u.id}"> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     <div class="flex items-center"> | ||||
|                       {#if u.profilePic} | ||||
|                         <div class="flex-shrink-0 h-10 w-10"> | ||||
|                           <img | ||||
|                             class="h-10 w-10 rounded-full" | ||||
|                             src={u.profilePic} | ||||
|                             alt="" /> | ||||
|                         </div> | ||||
|                       {/if} | ||||
|                       <div class="ml-4"> | ||||
|                         <div | ||||
|                           class="text-sm font-medium text-gray-900"> | ||||
|                           {u.firstname} | ||||
|                           {u.middlename || ''} | ||||
|                           {u.lastname} | ||||
|                         </div> | ||||
|                         <div class="text-sm text-gray-500"> | ||||
|                           {u.email || u.username} | ||||
|                         </div> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </div> | ||||
|                 </td> | ||||
|                 <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                   {#if u.enabled} | ||||
|                     <span | ||||
|                       class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span> | ||||
|                   {:else} | ||||
|                     <span | ||||
|                       class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Inactive</span> | ||||
|                   {/if} | ||||
|                 </td> | ||||
|                 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                   {#each u.groups as g} | ||||
|                     <a | ||||
|                       href="../groups/{g.id}" | ||||
|                       class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a> | ||||
|                   {/each} | ||||
|                 </td> | ||||
|                 {#if active_deletes[u.id] === true} | ||||
|                   <td | ||||
|                     class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                     <button | ||||
|                       on:click={() => { | ||||
|                         active_deletes[u.id] = false; | ||||
|                       }} | ||||
|                       tabindex="0" | ||||
|                       class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel | ||||
|                       Delete</button> | ||||
|                     <button | ||||
|                       on:click={() => { | ||||
|                         UserService.userControllerRemove(u.id, true) | ||||
|                           .then((resp) => { | ||||
|                             console.log(resp); | ||||
|                             // user deleted | ||||
|                             users_promise.then((data) => { | ||||
|                               console.log(data); | ||||
|                               usersstore.set(data); | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap"> | ||||
|                     {#if u.enabled} | ||||
|                       <span | ||||
|                         class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span> | ||||
|                     {:else} | ||||
|                       <span | ||||
|                         class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Inactive</span> | ||||
|                     {/if} | ||||
|                   </td> | ||||
|                   <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                     {#each u.groups as g} | ||||
|                       <a | ||||
|                         href="../groups/{g.id}" | ||||
|                         class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a> | ||||
|                     {/each} | ||||
|                   </td> | ||||
|                   {#if active_deletes[u.id] === true} | ||||
|                     <td | ||||
|                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           active_deletes[u.id] = false; | ||||
|                         }} | ||||
|                         tabindex="0" | ||||
|                         class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel | ||||
|                         Delete</button> | ||||
|                       <button | ||||
|                         on:click={() => { | ||||
|                           UserService.userControllerRemove(u.id, true) | ||||
|                             .then((resp) => { | ||||
|                               current_users=current_users.filter(obj=>obj.id!==u.id); | ||||
|                             }) | ||||
|                             .catch((err) => { | ||||
|                               // error deleting user | ||||
|                             }); | ||||
|                           }) | ||||
|                           .catch((err) => { | ||||
|                             // error deleting user | ||||
|                           }); | ||||
|                       }} | ||||
|                       tabindex="0" | ||||
|                       class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Confirm | ||||
|                       Delete</button> | ||||
|                   </td> | ||||
|                 {:else} | ||||
|                   <td | ||||
|                     class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||
|                     <a | ||||
|                       href="./{u.id}" | ||||
|                       class="text-indigo-600 hover:text-indigo-900">Edit</a> | ||||
|                     <button | ||||
|                       on:click={() => { | ||||
|                         active_deletes[u.id] = true; | ||||
|                       }} | ||||
|                       tabindex="0" | ||||
|                       class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</button> | ||||
|                   </td> | ||||
|                 {/if} | ||||
|               </tr> | ||||
|             {/if} | ||||
|           {/each} | ||||
|         </tbody> | ||||
|       </table> | ||||
|                         }} | ||||
|                         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="./{u.id}" | ||||
|                         class="text-indigo-600 hover:text-indigo-900">Edit</a> | ||||
|                       {#if store.state.jwtinfo.userdetails.permissions.includes('USER:DELETE')} | ||||
|                         <button | ||||
|                           on:click={() => { | ||||
|                             active_deletes[u.id] = true; | ||||
|                           }} | ||||
|                           tabindex="0" | ||||
|                           class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</button> | ||||
|                       {/if} | ||||
|                     </td> | ||||
|                   {/if} | ||||
|                 </tr> | ||||
|               {/if} | ||||
|             {/each} | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|     {/if} | ||||
|   {:catch error} | ||||
|     <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|       <span class="inline-block align-middle mr-8"> | ||||
|         <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|         {error} | ||||
|       </span> | ||||
|     </div> | ||||
|   {/if} | ||||
| {:catch error} | ||||
|   <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||
|     <span class="inline-block align-middle mr-8"> | ||||
|       <b class="capitalize">{$_('general_promise_error')}</b> | ||||
|       {error} | ||||
|     </span> | ||||
|   </div> | ||||
| {/await} | ||||
|   {/await} | ||||
| {/if} | ||||
|   | ||||
							
								
								
									
										1
									
								
								src/components/org_empty.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/components/org_empty.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 25 KiB | 
							
								
								
									
										1
									
								
								src/components/runners_empty.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/components/runners_empty.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 854.63 686"><path fill="#3f3d56" d="M0 600h821v9.053H0zM0 676.947h821V686H0z"/><path d="M750.178 608.328c-.49-.802-12.06-20.12-16.071-60.234-3.68-36.802-1.313-98.836 30.858-185.367 60.947-163.928-14.046-296.194-14.812-297.512l3.7-2.146c.194.334 19.545 34.057 30.977 87.755a382.846 382.846 0 01-15.856 213.394c-60.844 163.648-15.61 241.118-15.146 241.882z" fill="#3f3d56"/><circle cx="726.346" cy="27.795" r="27.795" fill="#3f3d56"/><circle cx="814.007" cy="130.422" r="27.795" fill="#3f3d56"/><circle cx="754.141" cy="198.841" r="27.795" fill="#dfe5ee"/><circle cx="826.835" cy="256.569" r="27.795" fill="#dfe5ee"/><circle cx="732.76" cy="346.368" r="27.795" fill="#3f3d56"/><path d="M766.97 609.35s-27.796-68.418 55.59-119.731zM732.786 608.11s-12.65-72.758-110.557-72.134z" fill="#3f3d56"/><circle cx="136.5" cy="387.5" r="41" fill="#a0616a"/><path d="M143.928 336.504c-7.84-1.925-16.272-2.247-23.868.49-7.824 2.817-14.263 8.708-19 15.542s-7.918 14.613-10.767 22.425a78.442 78.442 0 00-3.845 13.14 44.992 44.992 0 008.167 34.325c-1.2-3.166 1.822-6.617 5.138-7.303s6.693.474 9.963 1.353a61.559 61.559 0 0017.68 2.078c2.284-.065 4.679-.295 6.578-1.565 5.952-3.98 2.802-14.263 7.754-19.434 1.767-1.845 4.29-2.708 6.567-3.865 8.2-4.167 13.604-12.576 16.156-21.413 1.599-5.54 5.567-21.45 2.185-26.598-3.057-4.653-17.516-7.9-22.708-9.175z" fill="#2f2e41"/><path d="M248.066 342.291a24.396 24.396 0 00-18.677 1.041c-10.695 5.016-29.608 17.167-28.889 40.168 1 32 12 53 12 53l-2 65-10 118s-49 40-30 50 51-48 51-48l25-67 9-65s15-68 0-98c0 0 17.328-40.17-7.434-49.209zM367.5 445.5s-17 72 43 92 139 54 139 54l10 17 23-31-4-24-39-11s-73-56-105-60l4-37zM527.307 387.717L548.5 422.5s70 40 88 67 61 56 61 56l-19 45s-54-63-77-69-125-88-125-88z" fill="#a0616a"/><path d="M338.568 265.819c-7.805 1.226-15.617 2.454-23.33 4.174-17.917 3.995-35.147 10.616-51.966 17.974a608.405 608.405 0 00-68.447 35.267 132.255 132.255 0 00-16.97 11.523c-3.559 2.964-6.91 6.363-8.82 10.584a33.406 33.406 0 00-2.33 9.221c-1.55 11.12-1.477 22.595 1.535 33.41s9.124 20.955 18.119 27.674a29.88 29.88 0 007.84 4.348 45.675 45.675 0 0011.675 1.957c12.617 1.003 25.798 1.923 37.474-2.964 7.42-3.105 13.701-8.354 20.528-12.607 28.913-18.008 65.646-17.241 97.26-29.92 3.074-1.233 6.204-2.67 8.354-5.189a22.578 22.578 0 003.62-7.187l11.99-33.188c2.533-7.01 5.067-14.023 7.193-21.167 2.224-7.472 3.996-15.07 5.766-22.661 1.524-6.534 3.093-13.376 2.507-20.13-.864-9.962-3.166-10.367-12.316-8.925q-24.839 3.915-49.682 7.806z" fill="#dfe5ee"/><path d="M218.066 342.291a24.396 24.396 0 00-18.677 1.041c-10.695 5.016-29.608 17.167-28.889 40.168 1 32 12 53 12 53l-2 65-10 118s-49 40-30 50 51-48 51-48l25-67 9-65s15-68 0-98c0 0 17.328-40.17-7.434-49.209z" fill="#a0616a"/><path d="M391.66 257.544S480.5 249.5 476.5 304.5s-34 150-34 150l-77 4s-10-65 4-87z" fill="#2f2e41"/><path d="M453.5 275.5s22 14 34 42 48 83 48 83l-59 42-44-45zM559.5 587.5s-19 7-6 21a126.61 126.61 0 0120 29s-4 26 13 25 24-36 20-48-9-69-9-69-35 3-34 8 6 30-4 34zM678.5 567.5s-18 9-13 17 21 31 21 31-6 30 11 33 27-36 24-44-3-39-3-39 1-31-9-31-27 4-27 4 4 28-4 29z" fill="#2f2e41"/><path fill="#dfe5ee" d="M96 617.871h29v50.612H96z"/></svg> | ||||
| After Width: | Height: | Size: 3.2 KiB | 
							
								
								
									
										1
									
								
								src/components/team_empty.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/components/team_empty.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 52 KiB | 
							
								
								
									
										1
									
								
								src/components/users_empty.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/components/users_empty.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 7.6 KiB | 
| @@ -2,10 +2,17 @@ | ||||
|     "404message": "Die gesuchte Seite wurde leider nicht gefunden.", | ||||
|     "404title": "Fehler 404", | ||||
|     "about": "Über", | ||||
|     "application_name": "Lauf für Kaya! \n- Admin", | ||||
|     "application_name": "Lauf für Kaya! - Admin", | ||||
|     "bitte-bestaetige-diese-laeufer-fuer-den-import": "Bitte die Läufer für den import bestätigen.", | ||||
|     "by": "von", | ||||
|     "cancel": "Abbrechen", | ||||
|     "cannot-reset-your-password-directly": "Schade. \nWir können das Passwort leider nicht direkt zurücksetzen.\nBitte sende uns eine Mail in der du deine Identität bestätigst.", | ||||
|     "credits": "", | ||||
|     "csv_import__class": "Klasse", | ||||
|     "csv_import__firstname": "Vorname", | ||||
|     "csv_import__lastname": "Nachname", | ||||
|     "csv_import__middlename": "Mittelname", | ||||
|     "csv_import__team": "Team", | ||||
|     "dashboard-greeting": "Moin", | ||||
|     "datatable": { | ||||
|         "search": "🔍 Suche ...", | ||||
| @@ -26,6 +33,9 @@ | ||||
|     "general-stats": "Allgemeine Statistiken", | ||||
|     "goback": "Zur Startseite", | ||||
|     "hallo": "hallo", | ||||
|     "icon-image-credits": "Wir möchten uns außerdem für die verwendeten Icons und Bilder bedanken bei:", | ||||
|     "import-runners": "Läufer importieren", | ||||
|     "import__target-organization": "Ziel Organisation", | ||||
|     "invalid-mail-reset": "Das ist keine gültige E-Mail", | ||||
|     "lfk-is-os": "Das \"Lauf für Kaya!\" Frontend ist (wie alle anderen Projekte für den \"LfK!\" auch) ein OpenSource Projekt.", | ||||
|     "log_in": "Anmelden", | ||||
| @@ -33,8 +43,11 @@ | ||||
|     "login_is_checked": "Login wird überprüft", | ||||
|     "oss_credit_description": "Wir verwenden eine Menge Open Source-Software bei diesen Projekten und möchten uns bei den folgenden Projekten und Mitwirkenden bedanken, die dazu beitragen, Open Source großartig zu machen!", | ||||
|     "password": "Passwort", | ||||
|     "please-provide-the-required-csv-xlsx-file": "Bitte eine CSV oder XLSX Datei hochladen.", | ||||
|     "register": "Registrieren", | ||||
|     "reset-my-password": "Passwort zurücksetzen", | ||||
|     "runner-import": "Läufer Import", | ||||
|     "runnerimport_verify_runners_org": "Bitte die Läufer für den Import in die Organisation \"{org_name}\" bestätigen", | ||||
|     "runners": "Läufer", | ||||
|     "send-a-mail-to-lfk-odit-services": "Sende eine Mail an lfk@odit.services", | ||||
|     "settings": "Einstellungen", | ||||
|   | ||||
| @@ -4,8 +4,10 @@ | ||||
|     "about": "About", | ||||
|     "action": "Action", | ||||
|     "add-your-first-track": "Add your first track", | ||||
|     "address": "Address", | ||||
|     "application_name": "Lauf für Kaya! - Admin", | ||||
|     "author": "Author", | ||||
|     "bitte-bestaetige-diese-laeufer-fuer-den-import": "Please confirm these runners for import", | ||||
|     "browse": "Browse", | ||||
|     "by": "by", | ||||
|     "cancel": "Cancel", | ||||
| @@ -13,12 +15,22 @@ | ||||
|     "changelog": "Changelog", | ||||
|     "close": "Close", | ||||
|     "confirm-delete": "Confirm Delete", | ||||
|     "confirm-deletion": "Confirm Deletion", | ||||
|     "contact": "Contact", | ||||
|     "contact-information": "Contact Information", | ||||
|     "count_organizations": "# Organizations", | ||||
|     "count_teams": "# Teams", | ||||
|     "create": "Create", | ||||
|     "create-a-new-runner": "Create a new Runner", | ||||
|     "create-a-new-track": "Create a new Track", | ||||
|     "create-organization": "Create Organization", | ||||
|     "create-user": "Create User", | ||||
|     "credits": "Credits", | ||||
|     "csv_import__class": "Class", | ||||
|     "csv_import__firstname": "Firstname", | ||||
|     "csv_import__lastname": "Lastname", | ||||
|     "csv_import__middlename": "Middlename", | ||||
|     "csv_import__team": "Team", | ||||
|     "dashboard-greeting": "hello there", | ||||
|     "dashboard-title": "Dashboard", | ||||
|     "datatable": { | ||||
| @@ -36,12 +48,19 @@ | ||||
|         "no_matching_records_found": "No matching records found", | ||||
|         "an_error_happened_while_fetching_the_data": "An error happened while fetching the data" | ||||
|     }, | ||||
|     "delete": "Delete", | ||||
|     "delete-organization": "Delete Organization", | ||||
|     "delete-runner": "Delete Runner", | ||||
|     "delete-team": "Delete Team", | ||||
|     "delete-user": "Delete User", | ||||
|     "dependency_name": "Name", | ||||
|     "distance": "Distance", | ||||
|     "distance-in-km": "Distance in km", | ||||
|     "dont-have-your-email-connected": "Don't have your email connected?", | ||||
|     "dont-panic-were-resetting-it": "Don't panic, we're resetting it ✌", | ||||
|     "drag-and-drop-your-files-or": "Drag & Drop your files or", | ||||
|     "e-mail-adress": "E-Mail Adress", | ||||
|     "edit-permissions": "edit permissions", | ||||
|     "email_address_or_username": "Email / username", | ||||
|     "error_on_login": "Error on login", | ||||
|     "faq": "FAQ", | ||||
| @@ -71,8 +90,12 @@ | ||||
|     "general-stats": "General Stats", | ||||
|     "general_promise_error": "😢 Error", | ||||
|     "goback": "Go Home", | ||||
|     "group": "Group", | ||||
|     "groups": "Groups", | ||||
|     "hallo": "hello", | ||||
|     "icon-image-credits": "We also want to thank these projects for illustrations and icons:", | ||||
|     "import-runners": "Import runners", | ||||
|     "import__target-organization": "Target Organization", | ||||
|     "installed-version": "Installed version", | ||||
|     "invalid-mail-reset": "the provided email is invalid", | ||||
|     "last-name": "Last name", | ||||
| @@ -80,6 +103,7 @@ | ||||
|     "lfk-is-os": "The \"Lauf für Kaya!\" Frontend is (like all other projects for the \"LfK!\" Also) an open source project.", | ||||
|     "license": "License", | ||||
|     "licenses-are-being-loaded": "Licenses are being loaded...", | ||||
|     "loading-runners": "loading runners...", | ||||
|     "log_in": "Log in", | ||||
|     "log_in_to_your_account": "Log in to your account", | ||||
|     "login_is_checked": "Login is being checked...", | ||||
| @@ -88,25 +112,38 @@ | ||||
|     "manage-admin-users": "manage admin users", | ||||
|     "middle-name": "Middle name", | ||||
|     "minimum-lap-time-in-s": "minimum lap time in s", | ||||
|     "name": "Name", | ||||
|     "no-license-text-could-be-found": "No license text could be found 😢", | ||||
|     "no-tracks-added-yet": "there are no tracks added yet.", | ||||
|     "organization": "Organization", | ||||
|     "organizations": "Organizations", | ||||
|     "orgs": "Orgs", | ||||
|     "oss_credit_description": "We use a lot of open source software on these projects, and would like to thank the following projects and contributors who help make open source great!", | ||||
|     "password": "Password", | ||||
|     "password-is-required": "Password is required", | ||||
|     "permissions": "Permissions", | ||||
|     "phone": "Phone", | ||||
|     "please-provide-the-required-csv-xlsx-file": "Please provide the required csv/ xlsx file", | ||||
|     "please-provide-the-required-information-to-add-a-new-runner": "Please provide the required information to add a new runner.", | ||||
|     "please-provide-the-required-information-to-add-a-new-track": "Please provide the required information to add a new track.", | ||||
|     "profile-picture": "Profile Picture", | ||||
|     "read-license": "Read License", | ||||
|     "register": "Register", | ||||
|     "repo_link": "Link", | ||||
|     "reset-my-password": "Reset my password", | ||||
|     "runner-import": "Runner Import", | ||||
|     "runner-updated": "Runner updated!", | ||||
|     "runnerimport_verify_runners_org": "Please confirm these runners for import into the organization \"{org_name}\"", | ||||
|     "runners": "Runners", | ||||
|     "save-changes": "Save Changes", | ||||
|     "send-a-mail-to-lfk-odit-services": "send a mail to lfk@odit.services", | ||||
|     "settings": "Settings", | ||||
|     "signout": "Sign out", | ||||
|     "stats-are-being-loaded": "stats are being loaded...", | ||||
|     "team": "Team", | ||||
|     "team-name": "Team name", | ||||
|     "teams": "Teams", | ||||
|     "the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number": "the provided phone number is invalid.<br />please enter a valid international number...", | ||||
|     "this-might-take-a-moment": "This might take a moment 👀", | ||||
|     "total-distance": "total distance", | ||||
|     "total-donations": "total donations", | ||||
| @@ -117,6 +154,10 @@ | ||||
|     "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 👋", | ||||
|   | ||||
| @@ -27,6 +27,8 @@ const store = () => { | ||||
| 			AuthService.authControllerRefresh({ token: state.auth.refresh_token }).then((auth) => { | ||||
| 				console.log('got new auth'); | ||||
| 				OpenAPI.TOKEN = auth.access_token; | ||||
| 				const jwtinfo = JSON.parse(atob(auth.access_token.split('.')[1])); | ||||
| 				state.jwtinfo = jwtinfo; | ||||
| 				localForage.setItem('logindata', auth); | ||||
| 			}); | ||||
| 		}, | ||||
| @@ -39,8 +41,8 @@ const store = () => { | ||||
| 				// | ||||
| 				state.refreshInterval = setInterval(() => { | ||||
| 					this.refreshAuth(); | ||||
| 					// 4min | ||||
| 				}, 4 * 60000); | ||||
| 					// 2min | ||||
| 				}, 2 * 60000); | ||||
| 				// | ||||
| 				return state; | ||||
| 			}); | ||||
|   | ||||
| @@ -1,11 +0,0 @@ | ||||
| const sveltePreprocess = require('svelte-preprocess'); | ||||
|  | ||||
| const preprocess = sveltePreprocess({ | ||||
| 	postcss: { | ||||
| 		plugins: [ require('tailwindcss'), require('autoprefixer') ] | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| module.exports = { | ||||
| 	preprocess | ||||
| }; | ||||
| @@ -2,7 +2,7 @@ module.exports = { | ||||
| 	purge: { | ||||
| 		content: [ './src/**/*.svelte' ] | ||||
| 	}, | ||||
| 	darkMode: 'media', | ||||
| 	// darkMode: 'media', | ||||
| 	variants: {}, | ||||
| 	plugins: [], | ||||
| 	theme: { | ||||
|   | ||||
							
								
								
									
										19
									
								
								template-copy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								template-copy.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| const fs = require('fs'); | ||||
| let content_svelteconfig = fs.readFileSync('./s-config.template.js', { encoding: 'utf8' }); | ||||
| let content_html = fs.readFileSync('./index.template.html', { encoding: 'utf8' }); | ||||
| if (process.env.NODE_ENV_ODIT == 'development_fast') { | ||||
| 	content_html = content_html.replace( | ||||
| 		'__TAILWIND_INSERT__', | ||||
| 		'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.0.2/dist/tailwind.min.css">' | ||||
| 	); | ||||
| 	content_svelteconfig = content_svelteconfig.replace('__insert__', '{postcss:{}}'); | ||||
| } else { | ||||
| 	content_html = content_html.replace('__TAILWIND_INSERT__', ''); | ||||
| 	content_svelteconfig = content_svelteconfig.replace( | ||||
| 		'__insert__', | ||||
| 		"{postcss:{plugins:[require('tailwindcss'),require('autoprefixer')]}}" | ||||
| 	); | ||||
| } | ||||
| fs.writeFileSync('./public/index.html', content_html); | ||||
| fs.writeFileSync('./svelte.config.js', content_svelteconfig); | ||||
| console.info('dev setup script done'); | ||||
| @@ -1,5 +1,5 @@ | ||||
| const fs = require('fs'); | ||||
| const package = JSON.parse(fs.readFileSync(`./package.json`, { encoding: 'utf-8' })); | ||||
| const original = fs.readFileSync(`./public/index.html`, { encoding: 'utf-8' }); | ||||
| const original = fs.readFileSync(`./index.template.html`, { encoding: 'utf-8' }); | ||||
| let out = original.replace(/RELEASE_INFO-(\S)+-RELEASE_INFO/gi, 'RELEASE_INFO-' + package.version + '-RELEASE_INFO'); | ||||
| fs.writeFileSync(`./public/index.html`, out); | ||||
| fs.writeFileSync(`./index.template.html`, out); | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| module.exports = { | ||||
| 	globDirectory: 'public', | ||||
| 	globPatterns: [ '**/*.{js,ico,png,svg,html,webmanifest,txt}' ], | ||||
| 	globIgnores: [ 'env.js', 'env.sample.js', 'licenses.json' ], | ||||
| 	swDest: 'public/sw.js' | ||||
| 	globPatterns: [ '**/*.{js,ico,png,svg,html,webmanifest,txt,json}' ], | ||||
| 	globIgnores: [ 'env.js', 'env.sample.js' ], | ||||
| 	swDest: 'public/sw.js', | ||||
| 	cleanupOutdatedCaches: true, | ||||
| 	mode: 'production', | ||||
| 	sourcemap: false | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user