Compare commits
	
		
			29 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2bbaa500f4 | |||
| 722a20e141 | |||
| 041c24a837 | |||
| 39a3baa00b | |||
| f7acbb1eaa | |||
| e6fbf7aa5b | |||
| 36a084eab6 | |||
| a9e319e0c0 | |||
| ea23b97231 | |||
| 7df76f9642 | |||
| f6db117a5e | |||
| 23c3cd605d | |||
| 77690702c0 | |||
| e0093480d9 | |||
| c7679b7a67 | |||
| e6ac34bde8 | |||
| 8c4b595c30 | |||
| be629e5c6b | |||
| 63569684a3 | |||
| 5937a0d7ce | |||
| 4512272c1c | |||
| ce1f3842e0 | |||
| ee01c3a059 | |||
| 81c1537bad | |||
| 98ecfab032 | |||
| b948b8c1a4 | |||
| f856c6ae37 | |||
| 2dd2580530 | |||
| f0c100aee4 | 
							
								
								
									
										41
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -2,9 +2,50 @@ | |||||||
|  |  | ||||||
| All notable changes to this project will be documented in this file. Dates are displayed in UTC. | All notable changes to this project will be documented in this file. Dates are displayed in UTC. | ||||||
|  |  | ||||||
|  | #### [0.2.1](https://git.odit.services/lfk/frontend/compare/0.2.0...0.2.1) | ||||||
|  |  | ||||||
|  | - 🔒 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) | ||||||
|  | - 💬 UserDetail - info Toasts [`ea23b97`](https://git.odit.services/lfk/frontend/commit/ea23b972315db39e1f800366d52e3bacf9eaf58b) | ||||||
|  | - 🔨 optimized release script [`23c3cd6`](https://git.odit.services/lfk/frontend/commit/23c3cd605db950a5620ea709748216f7ea034385) | ||||||
|  | - 🐞 fix package release script: locales directory [`722a20e`](https://git.odit.services/lfk/frontend/commit/722a20e141079302da8876be1ccc55026c588048) | ||||||
|  | - ⏫ bump @odit/lfk-client-js to 0.0.11 [`f6db117`](https://git.odit.services/lfk/frontend/commit/f6db117a5eca5c9692a4c5d83159917a712a0e63) | ||||||
|  | - 🐞 UserDetail - fix permission reactivity by assignments [`39a3baa`](https://git.odit.services/lfk/frontend/commit/39a3baa00b0575ee3150c7ae26f32995e2e857d9) | ||||||
|  |  | ||||||
|  | #### [0.2.0](https://git.odit.services/lfk/frontend/compare/0.1.6...0.2.0) | ||||||
|  |  | ||||||
|  | > 11 January 2021 | ||||||
|  |  | ||||||
|  | - 🔒 added rendering based on permission level [`5937a0d`](https://git.odit.services/lfk/frontend/commit/5937a0d7ce970d38ddb0e4c6a02d11f8c8b53e9b) | ||||||
|  | - ❌ UserDetail - delete [`4512272`](https://git.odit.services/lfk/frontend/commit/4512272c1c2a5c861d8ac4c9908244df125dc3d9) | ||||||
|  | - 🖊 UserDetail - reactivity on edit + update functionality [`ce1f384`](https://git.odit.services/lfk/frontend/commit/ce1f3842e0f581d9e94ea6079cf223d945f7465d) | ||||||
|  | - 🚀RELEASE v0.2.0 [`7769070`](https://git.odit.services/lfk/frontend/commit/77690702c0461b71ff1b65a44bfdc331d5a42c3e) | ||||||
|  | - [tmp] - disable darkmode + re-enable sw [`c7679b7`](https://git.odit.services/lfk/frontend/commit/c7679b7a67b362a7fbf796f612892bdfac50731c) | ||||||
|  | - 🕕 set manual refresh time to 2min [`be629e5`](https://git.odit.services/lfk/frontend/commit/be629e5c6b076bf9a28dcc953e97478d244d8413) | ||||||
|  | - 🐞 [tmp] - nginx.conf - disable .js file caching [`e6ac34b`](https://git.odit.services/lfk/frontend/commit/e6ac34bde8d288a45e83fc6723a2bbe952f2622a) | ||||||
|  | - ℹ update jwtinfo store on token refresh [`6356968`](https://git.odit.services/lfk/frontend/commit/63569684a392bf0c24c9c2efd7945114d1a230a5) | ||||||
|  |  | ||||||
|  | #### [0.1.6](https://git.odit.services/lfk/frontend/compare/0.1.5...0.1.6) | ||||||
|  |  | ||||||
|  | > 10 January 2021 | ||||||
|  |  | ||||||
|  | - ✨ UsersOverview - user delete [`f0c100a`](https://git.odit.services/lfk/frontend/commit/f0c100aee47d6a5aeb0995db79b268f33bf316e9) | ||||||
|  | - 🔒 UserDetail - added basic layout for permission change [`81c1537`](https://git.odit.services/lfk/frontend/commit/81c1537bada2c87127385d9110d48459cd1b505f) | ||||||
|  | - 🚀RELEASE v0.1.6 [`ee01c3a`](https://git.odit.services/lfk/frontend/commit/ee01c3a059c435add68938657955cbd2c5851c50) | ||||||
|  | - 📧 UserDetail - email input [`f856c6a`](https://git.odit.services/lfk/frontend/commit/f856c6ae3792c93aee148339d89e3978db6e9293) | ||||||
|  | - ✨ UserDetail multiselect layout for groups [`98ecfab`](https://git.odit.services/lfk/frontend/commit/98ecfab0325e35c5418775f7162049b56e5f332f) | ||||||
|  | - UserDetail - placeholder for permission picker 🔒 [`b948b8c`](https://git.odit.services/lfk/frontend/commit/b948b8c1a48e5b4bc6a083139d26100ef4499970) | ||||||
|  |  | ||||||
| #### [0.1.5](https://git.odit.services/lfk/frontend/compare/0.1.4...0.1.5) | #### [0.1.5](https://git.odit.services/lfk/frontend/compare/0.1.4...0.1.5) | ||||||
|  |  | ||||||
|  | > 10 January 2021 | ||||||
|  |  | ||||||
| - Merge commit '16f572480ad55425890061f9dad65fe85f2f39ad' into dev [`#30`](https://git.odit.services/lfk/frontend/issues/30) | - Merge commit '16f572480ad55425890061f9dad65fe85f2f39ad' into dev [`#30`](https://git.odit.services/lfk/frontend/issues/30) | ||||||
|  | - 🚀RELEASE v0.1.5 [`330755c`](https://git.odit.services/lfk/frontend/commit/330755c63e3405533a5da79c84ef4f379eb43e1c) | ||||||
| - ⤵ load dynamic build info in Footer component [`c089bb3`](https://git.odit.services/lfk/frontend/commit/c089bb39298fb1067093c2fa81101130c214947c) | - ⤵ load dynamic build info in Footer component [`c089bb3`](https://git.odit.services/lfk/frontend/commit/c089bb39298fb1067093c2fa81101130c214947c) | ||||||
| - 📅 dynamic copyright year in Footer component [`b8a9e4f`](https://git.odit.services/lfk/frontend/commit/b8a9e4f272f925999b9a032dd009f7498acbfae0) | - 📅 dynamic copyright year in Footer component [`b8a9e4f`](https://git.odit.services/lfk/frontend/commit/b8a9e4f272f925999b9a032dd009f7498acbfae0) | ||||||
| - 👀 improved Footer layout + display on Login component [`43b4065`](https://git.odit.services/lfk/frontend/commit/43b406592ebe115cea04a8dbf36874c0a5bdd7e9) | - 👀 improved Footer layout + display on Login component [`43b4065`](https://git.odit.services/lfk/frontend/commit/43b406592ebe115cea04a8dbf36874c0a5bdd7e9) | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ http { | |||||||
|         location / { |         location / { | ||||||
|             try_files $uri $uri/ /index.html; |             try_files $uri $uri/ /index.html; | ||||||
|         } |         } | ||||||
|         location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { |         location ~* \.(?:ico|css|gif|jpe?g|png)$ { | ||||||
|             expires 1y; |             expires 1y; | ||||||
|             add_header Pragma public; |             add_header Pragma public; | ||||||
|             add_header Cache-Control "public"; |             add_header Cache-Control "public"; | ||||||
|   | |||||||
| @@ -1,17 +1,16 @@ | |||||||
| { | { | ||||||
| 	"name": "@odit/lfk-frontend", | 	"name": "@odit/lfk-frontend", | ||||||
| 	"version": "0.1.5", | 	"version": "0.2.1", | ||||||
| 	"scripts": { | 	"scripts": { | ||||||
| 		"i18n-order": "node order.js", | 		"i18n-order": "node order.js", | ||||||
| 		"dev": "snowpack dev", | 		"dev": "snowpack dev", | ||||||
| 		"build": "snowpack build", | 		"build": "snowpack build", | ||||||
| 		"build:sw": "workbox generateSW workbox-config.js", | 		"build:sw": "workbox generateSW workbox-config.js", | ||||||
| 		"release": "release-it", | 		"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" | 		"licenses:export": "license-exporter --json -o public" | ||||||
| 	}, | 	}, | ||||||
| 	"dependencies": { | 	"dependencies": { | ||||||
| 		"@odit/lfk-client-js": "0.0.10", | 		"@odit/lfk-client-js": "0.0.11", | ||||||
| 		"filepond": "4.25.1", | 		"filepond": "4.25.1", | ||||||
| 		"gridjs": "3.2.1", | 		"gridjs": "3.2.1", | ||||||
| 		"localforage": "1.9.0", | 		"localforage": "1.9.0", | ||||||
| @@ -51,7 +50,7 @@ | |||||||
| 			"publish": false | 			"publish": false | ||||||
| 		}, | 		}, | ||||||
| 		"hooks": { | 		"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 public/index.html && node order.js  && git add src/locales" | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ | |||||||
| </head> | </head> | ||||||
|  |  | ||||||
| <body> | <body> | ||||||
|   <span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.1.5-RELEASE_INFO</span> |   <span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.2.1-RELEASE_INFO</span> | ||||||
|   <noscript>You need to enable JavaScript to run this app.</noscript> |   <noscript>You need to enable JavaScript to run this app.</noscript> | ||||||
|   <script src="/env.js"></script> |   <script src="/env.js"></script> | ||||||
|   <script defer type="module" src="/_dist_/index.js"></script> |   <script defer type="module" src="/_dist_/index.js"></script> | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ | |||||||
|   OpenAPI.BASE = config.baseurl; |   OpenAPI.BASE = config.baseurl; | ||||||
|   import { register as registerSW } from "./swmodule"; |   import { register as registerSW } from "./swmodule"; | ||||||
|   store.init(); |   store.init(); | ||||||
|   // registerSW(); |   registerSW(); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <Route> | <Route> | ||||||
|   | |||||||
| @@ -2,20 +2,21 @@ | |||||||
|   import { _ } from "svelte-i18n"; |   import { _ } from "svelte-i18n"; | ||||||
|   import { clickOutside } from "./outsideclick"; |   import { clickOutside } from "./outsideclick"; | ||||||
|   import { focusTrap } from "svelte-focus-trap"; |   import { focusTrap } from "svelte-focus-trap"; | ||||||
|   import { tracks as tracksstore } from "../store.js"; |  | ||||||
|   import { UserService } from "@odit/lfk-client-js"; |   import { UserService } from "@odit/lfk-client-js"; | ||||||
|   import isEmail from "validator/es/lib/isEmail"; |   import isEmail from "validator/es/lib/isEmail"; | ||||||
|   import Toastify from "toastify-js"; |   import Toastify from "toastify-js"; | ||||||
|   import "toastify-js/src/toastify.css"; |  | ||||||
|   export let modal_open; |   export let modal_open; | ||||||
|  |   export let current_users; | ||||||
|   let firstname_input; |   let firstname_input; | ||||||
|   let lastname_input; |   let lastname_input; | ||||||
|   let middlename_input; |   let middlename_input; | ||||||
|  |   let username_input; | ||||||
|   let password_input; |   let password_input; | ||||||
|   let email_input; |   let email_input; | ||||||
|   function focus(el) { |   function focus(el) { | ||||||
|     el.focus(); |     el.focus(); | ||||||
|   } |   } | ||||||
|  |   $: username_input_value = ""; | ||||||
|   $: middlename_input_value = ""; |   $: middlename_input_value = ""; | ||||||
|   $: password_input_value = ""; |   $: password_input_value = ""; | ||||||
|   $: email_input_value = ""; |   $: email_input_value = ""; | ||||||
| @@ -49,17 +50,25 @@ | |||||||
|         text: "User is being added...", |         text: "User is being added...", | ||||||
|         duration: -1, |         duration: -1, | ||||||
|       }).showToast(); |       }).showToast(); | ||||||
|       UserService.userControllerPost({ |       let postdata={ | ||||||
|         firstname: firstname_input_value, |         firstname: firstname_input_value, | ||||||
|         lastname: lastname_input_value, |         lastname: lastname_input_value, | ||||||
|         middlename: middlename_input_value, |         middlename: middlename_input_value, | ||||||
|         email: email_input_value, |         password: password_input_value | ||||||
|         password: password_input_value, |       }; | ||||||
|       }) |       if(email_input_value!==""){ | ||||||
|  |         postdata.email=email_input_value; | ||||||
|  |       } | ||||||
|  |       if(username_input_value!==""){ | ||||||
|  |         postdata.username=username_input_value; | ||||||
|  |       } | ||||||
|  |       UserService.userControllerPost(postdata) | ||||||
|         .then((result) => { |         .then((result) => { | ||||||
|           firstname_input_value = ""; |           firstname_input_value = ""; | ||||||
|           lastname_input_value = ""; |           lastname_input_value = ""; | ||||||
|           middlename_input_value = ""; |           middlename_input_value = ""; | ||||||
|  |           email_input_value = ""; | ||||||
|  |           username_input_value = ""; | ||||||
|           modal_open = false; |           modal_open = false; | ||||||
|           // |           // | ||||||
|           Toastify({ |           Toastify({ | ||||||
| @@ -67,12 +76,8 @@ | |||||||
|             duration: 500, |             duration: 500, | ||||||
|             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", |             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||||
|           }).showToast(); |           }).showToast(); | ||||||
|           let storeval = []; |           current_users.push(result); | ||||||
|           tracksstore.subscribe((val) => { |           current_users=current_users; | ||||||
|             storeval = val; |  | ||||||
|           }); |  | ||||||
|           storeval.push(result); |  | ||||||
|           tracksstore.set(storeval); |  | ||||||
|         }) |         }) | ||||||
|         .catch((err) => { |         .catch((err) => { | ||||||
|           // |           // | ||||||
| @@ -213,6 +218,19 @@ | |||||||
|                     </span> |                     </span> | ||||||
|                   {/if} |                   {/if} | ||||||
|                 </div> |                 </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"> |                 <div class="col-span-6"> | ||||||
|                   <label |                   <label | ||||||
|                     for="email" |                     for="email" | ||||||
|   | |||||||
| @@ -1,9 +1,12 @@ | |||||||
| <script> | <script> | ||||||
|   import { _ } from "svelte-i18n"; |   import { _ } from "svelte-i18n"; | ||||||
|   const releaseinfo = document |   $: releaseinfo = ""; | ||||||
|     .getElementById("buildinfo") |   setTimeout(() => { | ||||||
|     .textContent.replace("RELEASE_INFO-", "") |     releaseinfo = document | ||||||
|     .replace("-RELEASE_INFO", ""); |       .getElementById("buildinfo") | ||||||
|  |       .textContent.replace("RELEASE_INFO-", "") | ||||||
|  |       .replace("-RELEASE_INFO", ""); | ||||||
|  |   }, 1500); | ||||||
|   const year = new Date().getFullYear(); |   const year = new Date().getFullYear(); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,27 +1,92 @@ | |||||||
| <script> | <script> | ||||||
|   import { _ } from "svelte-i18n"; |   import { _ } from "svelte-i18n"; | ||||||
|   import lodashIsEqual from "lodash.isequal"; |   import lodashIsEqual from "lodash.isequal"; | ||||||
|   import { UserService } from "@odit/lfk-client-js"; |   import store from "../store"; | ||||||
|  |   import { | ||||||
|  |     UserService, | ||||||
|  |     UserGroupService, | ||||||
|  |     PermissionService, | ||||||
|  |   } from "@odit/lfk-client-js"; | ||||||
|   import "gridjs/dist/theme/mermaid.css"; |   import "gridjs/dist/theme/mermaid.css"; | ||||||
|  |   import Toastify from "toastify-js"; | ||||||
|   import PromiseError from "./PromiseError.svelte"; |   import PromiseError from "./PromiseError.svelte"; | ||||||
|   export let params; |   export let params; | ||||||
|   const user_promise = UserService.userControllerGetOne(params.userid); |   const user_promise = UserService.userControllerGetOne(params.userid); | ||||||
|   let data_loaded = false; |   let data_loaded = false; | ||||||
|   let original_data = undefined; |   $: delete_triggered = false; | ||||||
|   $: editable_userdata = undefined; |   $: original_data = {}; | ||||||
|  |   $: editable_userdata = {}; | ||||||
|  |   $: allpermissions = []; | ||||||
|  |   $: allgroups = []; | ||||||
|  |   $: allgroups_ids = []; | ||||||
|  |   $: usergroups_array_objects = []; | ||||||
|  |   $: usergroups_array = []; | ||||||
|  |   let usergroups_array_original = []; | ||||||
|   user_promise.then((data) => { |   user_promise.then((data) => { | ||||||
|     data_loaded = true; |     data_loaded = true; | ||||||
|     original_data = data; |     original_data = Object.assign(original_data, data); | ||||||
|     editable_userdata = 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); |   UserGroupService.userGroupControllerGetAll().then((data) => { | ||||||
|   $: changes_performed = !lodashIsEqual({ test: 1 }, { test: 1 }); |     allgroups = data; | ||||||
|  |   }); | ||||||
|  |   const permissions_promise = PermissionService.permissionControllerGetAll(); | ||||||
|  |   permissions_promise.then((data) => { | ||||||
|  |     data.forEach((p) => { | ||||||
|  |       allpermissions=allpermissions.concat([p.target + ":" + p.action]) | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |   $: changes_performed = !lodashIsEqual(original_data, editable_userdata); | ||||||
|  |   $: groups_changed = JSON.stringify(usergroups_array)===JSON.stringify(usergroups_array_original); | ||||||
|  |   $: save_enabled = changes_performed||!groups_changed; | ||||||
|   function submit() { |   function submit() { | ||||||
|     if (data_loaded === true && changes_performed === true) { |     if ( | ||||||
|       console.log("ok, submitting..."); |       !lodashIsEqual(original_data.permissions, editable_userdata.permissions) | ||||||
|     } else { |     ) { | ||||||
|       console.log("no changes performed"); |       // TODO: add+delete permissions | ||||||
|     } |     } | ||||||
|  |     if (data_loaded === true && save_enabled) { | ||||||
|  |       let tmp=[]; | ||||||
|  |       usergroups_array.forEach(g => { | ||||||
|  |         const group=allgroups.find(obj=>obj.id===g); | ||||||
|  |         tmp.push(group); | ||||||
|  |       }); | ||||||
|  |       editable_userdata.groups=tmp; | ||||||
|  |       Toastify({ | ||||||
|  |         text: $_("updating-user"), | ||||||
|  |         duration: 2500, | ||||||
|  |       }).showToast(); | ||||||
|  |       UserService.userControllerPut(original_data.id, editable_userdata) | ||||||
|  |         .then((resp) => { | ||||||
|  |           Object.assign(original_data, editable_userdata); | ||||||
|  |           original_data = editable_userdata; | ||||||
|  |           Object.assign(original_data, editable_userdata); | ||||||
|  |           // | ||||||
|  |           Toastify({ | ||||||
|  |             text: $_("user-updated"), | ||||||
|  |             duration: 2500, | ||||||
|  |             backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", | ||||||
|  |           }).showToast(); | ||||||
|  |         }) | ||||||
|  |         .catch((err) => { | ||||||
|  |         }); | ||||||
|  |     } else { | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   function deleteUser() { | ||||||
|  |     UserService.userControllerRemove(original_data.id, true) | ||||||
|  |       .then((resp) => { | ||||||
|  |         location.replace("./"); | ||||||
|  |       }) | ||||||
|  |       .catch((err) => { | ||||||
|  |       }); | ||||||
|   } |   } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| @@ -63,51 +128,54 @@ | |||||||
|                 <polyline points="12 5 19 12 12 19" /></svg> |                 <polyline points="12 5 19 12 12 19" /></svg> | ||||||
|             </li> |             </li> | ||||||
|             <li class="flex items-center"> |             <li class="flex items-center"> | ||||||
|               <span class="mr-2">{user.firstname} |               <span class="mr-2">{original_data.firstname} | ||||||
|                 {user.middlename || ''} |                 {original_data.middlename || ''} | ||||||
|                 {user.lastname}</span> |                 {original_data.lastname}</span> | ||||||
|             </li> |             </li> | ||||||
|           </ol> |           </ol> | ||||||
|         </nav> |         </nav> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     <div class="mb-8 text-3xl font-extrabold leading-tight"> |     <div class="mb-8 text-3xl font-extrabold leading-tight"> | ||||||
|       {user.firstname} |       {original_data.firstname} | ||||||
|       {user.middlename || ''} |       {original_data.middlename || ''} | ||||||
|       {user.lastname} |       {original_data.lastname} | ||||||
|       <span data-id="user_actions_${user.id}"> |       <span data-id="user_actions_${editable_userdata.id}"> | ||||||
|         <button |         {#if store.state.jwtinfo.userdetails.permissions.includes('USER:DELETE')} | ||||||
|           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" |           {#if delete_triggered} | ||||||
|           data-userid="${user.id}" |             <button | ||||||
|           onclick="user__delete_cancel()">{$_('cancel')}</button> |               on:click={deleteUser} | ||||||
|         <button |               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-delete')}</button> | ||||||
|           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" |             <button | ||||||
|           data-userid="${user.id}" |               on:click={() => { | ||||||
|           onclick="user__delete_confirm()">{$_('confirm-delete')}</button> |                 delete_triggered = !delete_triggered; | ||||||
|         <button |               }} | ||||||
|           type="button" |               class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button> | ||||||
|           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} | ||||||
|         <button |           {#if !delete_triggered} | ||||||
|           disabled={!changes_performed} |             <button | ||||||
|           class:opacity-50={!changes_performed} |               on:click={() => { | ||||||
|           type="button" |                 delete_triggered = true; | ||||||
|           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> |               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> |       </span> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <!--  --> |  | ||||||
|     <div class="mt-2 flex items-center"> |     <div class="mt-2 flex items-center"> | ||||||
|       <img |       <img | ||||||
|         alt={$_('profile-picture')} |         alt={$_('profile-picture')} | ||||||
|         class="inline-block h-20 w-20 rounded-full overflow-hidden bg-gray-100" |         class="inline-block h-20 w-20 rounded-full overflow-hidden bg-gray-100" | ||||||
|         src={user.profilePic} /> |         src={editable_userdata.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 |       <button | ||||||
|         type="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="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> | ||||||
| @@ -165,6 +233,102 @@ | |||||||
|         name="lastname" |         name="lastname" | ||||||
|         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> |         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> | ||||||
|  |     <div class="text-sm w-full"> | ||||||
|  |       <label | ||||||
|  |         for="email" | ||||||
|  |         class="font-medium text-gray-700">{$_('e-mail-adress')}</label> | ||||||
|  |       <input | ||||||
|  |         autocomplete="off" | ||||||
|  |         placeholder={$_('e-mail-adress')} | ||||||
|  |         type="email" | ||||||
|  |         bind:value={editable_userdata.email} | ||||||
|  |         name="email" | ||||||
|  |         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||||
|  |     </div> | ||||||
|  |     <div class="text-sm w-full"> | ||||||
|  |       <label | ||||||
|  |         for="username" | ||||||
|  |         class="font-medium text-gray-700">{$_('username')}</label> | ||||||
|  |       <input | ||||||
|  |         autocomplete="off" | ||||||
|  |         placeholder={$_('username')} | ||||||
|  |         type="text" | ||||||
|  |         bind:value={editable_userdata.username} | ||||||
|  |         name="username" | ||||||
|  |         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" /> | ||||||
|  |     </div> | ||||||
|  |     <div class="text-sm w-full"> | ||||||
|  |       <span class="font-medium">{$_('groups')}</span> | ||||||
|  |       <!-- svelte-ignore a11y-no-onchange --> | ||||||
|  |       <select | ||||||
|  |         bind:value={usergroups_array} | ||||||
|  |         class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" | ||||||
|  |         multiple> | ||||||
|  |         {#each allgroups as g} | ||||||
|  |           {#if usergroups_array.includes(g.id)} | ||||||
|  |             <option selected value={g.id}>{g.name}</option> | ||||||
|  |           {:else} | ||||||
|  |             <option value={g.id}>{g.name}</option> | ||||||
|  |           {/if} | ||||||
|  |         {/each} | ||||||
|  |       </select> | ||||||
|  |     </div> | ||||||
|  |     <div class="text-sm w-full"> | ||||||
|  |       <span class="font-medium">Permissions</span> | ||||||
|  |       <div class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||||
|  |         <!--  --> | ||||||
|  |         <div class="flex flex-wrap -mx-1 overflow-hidden"> | ||||||
|  |           <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> | ||||||
|  |             verfügbare | ||||||
|  |           </div> | ||||||
|  |           <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">erteilte</div> | ||||||
|  |         </div> | ||||||
|  |         <!--  --> | ||||||
|  |         <div class="flex flex-wrap -mx-1 overflow-hidden"> | ||||||
|  |           {#if allpermissions.length > 0} | ||||||
|  |             <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> | ||||||
|  |               <div | ||||||
|  |                 class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||||
|  |                 {#each allpermissions as p} | ||||||
|  |                   {#if !editable_userdata.permissions.includes(p)} | ||||||
|  |                     <p | ||||||
|  |                       class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||||
|  |                       {p} | ||||||
|  |                       <button | ||||||
|  |                         on:click={() => { | ||||||
|  |                           editable_userdata.permissions.push(p); | ||||||
|  |                           editable_userdata.permissions = editable_userdata.permissions; | ||||||
|  |                         }} | ||||||
|  |                         type="button" | ||||||
|  |                         class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-200 text-base font-medium text-black hover:bg-green-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm">+</button> | ||||||
|  |                     </p> | ||||||
|  |                   {/if} | ||||||
|  |                 {/each} | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="my-1 px-1 w-full overflow-hidden sm:w-1/2"> | ||||||
|  |               <div | ||||||
|  |                 class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center"> | ||||||
|  |                 {#each allpermissions as p} | ||||||
|  |                   {#if editable_userdata.permissions.includes(p)} | ||||||
|  |                     <p | ||||||
|  |                       class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input"> | ||||||
|  |                       {p} | ||||||
|  |                       <button | ||||||
|  |                         on:click={() => { | ||||||
|  |                           editable_userdata.permissions = editable_userdata.permissions.filter((obj) => obj !== p); | ||||||
|  |                         }} | ||||||
|  |                         type="button" | ||||||
|  |                         class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-300 text-base font-medium text-black hover:bg-red-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">-</button> | ||||||
|  |                     </p> | ||||||
|  |                   {/if} | ||||||
|  |                 {/each} | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           {/if} | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|   </section> |   </section> | ||||||
| {:catch error} | {:catch error} | ||||||
|   <PromiseError {error} /> |   <PromiseError {error} /> | ||||||
|   | |||||||
| @@ -1,23 +1,31 @@ | |||||||
| <script> | <script> | ||||||
|   import { _ } from "svelte-i18n"; |   import { _ } from "svelte-i18n"; | ||||||
|  |   import store from "../store"; | ||||||
|   import AddUserModal from "./AddUserModal.svelte"; |   import AddUserModal from "./AddUserModal.svelte"; | ||||||
|   export let modal_open = false; |   export let modal_open = false; | ||||||
|   import UsersOverview from "./UsersOverview.svelte"; |   import UsersOverview from "./UsersOverview.svelte"; | ||||||
|  |   console.log(store.state.jwtinfo.userdetails.permissions); | ||||||
|  |   let current_users=[]; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <section class="container p-5"> | <section class="container p-5"> | ||||||
|   <span class="mb-1 text-3xl font-extrabold leading-tight"> |   <span class="mb-1 text-3xl font-extrabold leading-tight"> | ||||||
|     {$_('users')} |     {$_('users')} | ||||||
|     <button |     {#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')} | ||||||
|       on:click={() => { |       <button | ||||||
|         modal_open = true; |         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"> |         type="button" | ||||||
|       {$_('create-user')} |         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"> | ||||||
|     </button> |         {$_('create-user')} | ||||||
|  |       </button> | ||||||
|  |     {/if} | ||||||
|   </span> |   </span> | ||||||
|   <p class="mb-8 text-lg text-gray-500">{$_('manage-admin-users')}</p> |   <p class="mb-8 text-lg text-gray-500">{$_('manage-admin-users')}</p> | ||||||
|   <UsersOverview /> |   <UsersOverview bind:current_users /> | ||||||
| </section> | </section> | ||||||
| <AddUserModal bind:modal_open /> |  | ||||||
|  | {#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')} | ||||||
|  |   <AddUserModal bind:current_users bind:modal_open /> | ||||||
|  | {/if} | ||||||
|   | |||||||
| @@ -5,143 +5,178 @@ | |||||||
|   const users_promise = UserService.userControllerGetAll(); |   const users_promise = UserService.userControllerGetAll(); | ||||||
|   import "gridjs/dist/theme/mermaid.css"; |   import "gridjs/dist/theme/mermaid.css"; | ||||||
|   import { users as usersstore } from "../store.js"; |   import { users as usersstore } from "../store.js"; | ||||||
|  |   import store from "../store"; | ||||||
|   import UsersEmptyState from "./UsersEmptyState.svelte"; |   import UsersEmptyState from "./UsersEmptyState.svelte"; | ||||||
|   $: searchvalue = ""; |   $: searchvalue = ""; | ||||||
|  |   $: active_deletes = []; | ||||||
|  |   export let current_users=[]; | ||||||
|   $: userscache = []; |   $: userscache = []; | ||||||
|   $: advanced_search = false; |   $: advanced_search = false; | ||||||
|   usersstore.subscribe((val) => { |   usersstore.subscribe((val) => { | ||||||
|     userscache = val; |     userscache = val; | ||||||
|  |     current_users=val; | ||||||
|   }); |   }); | ||||||
|   users_promise.then((data) => { |   users_promise.then((data) => { | ||||||
|     console.log(data); |  | ||||||
|     usersstore.set(data); |     usersstore.set(data); | ||||||
|   }); |   }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| {#await users_promise} | {#if store.state.jwtinfo.userdetails.permissions.includes('USER:GET')} | ||||||
|   <div |   {#await users_promise} | ||||||
|     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> |  | ||||||
|     <div |     <div | ||||||
|       class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> |       class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" | ||||||
|       <table class="divide-y divide-gray-200 w-full"> |       role="alert"> | ||||||
|         <thead class="bg-gray-50"> |       <p class="font-bold">users are being loaded...</p> | ||||||
|           <tr> |       <p class="text-sm">{$_('this-might-take-a-moment')}</p> | ||||||
|             <th |     </div> | ||||||
|               scope="col" |   {:then} | ||||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> |     {#if current_users.length === 0} | ||||||
|               Name |       <UsersEmptyState /> | ||||||
|             </th> |     {:else} | ||||||
|             <th |       {#if advanced_search} | ||||||
|               scope="col" |         advanced search | ||||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> |       {:else} | ||||||
|               Status |         <input | ||||||
|             </th> |           type="search" | ||||||
|             <th |           bind:value={searchvalue} | ||||||
|               scope="col" |           placeholder={$_('datatable.search')} | ||||||
|               class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> |           aria-label={$_('datatable.search')} | ||||||
|               Groups |           class="gridjs-input gridjs-search-input mb-4" /> | ||||||
|             </th> |       {/if} | ||||||
|             <th scope="col" class="relative px-6 py-3"> |       <button | ||||||
|               <span class="sr-only">Action</span> |         on:click={() => { | ||||||
|             </th> |           advanced_search = !advanced_search; | ||||||
|           </tr> |         }} | ||||||
|         </thead> |         type="button" | ||||||
|         <tbody class="divide-y divide-gray-200"> |         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"> | ||||||
|           {#each users as u} |         {#if advanced_search} | ||||||
|             {#if Object.values(u) |           toggle simple search | ||||||
|               .toString() |         {:else}toggle advanced search{/if} | ||||||
|               .toLowerCase() |       </button> | ||||||
|               .includes(searchvalue)} |       <div | ||||||
|               <tr> |         class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"> | ||||||
|                 <td class="px-6 py-4 whitespace-nowrap"> |         <table class="divide-y divide-gray-200 w-full"> | ||||||
|                   <div class="flex items-center"> |           <thead class="bg-gray-50"> | ||||||
|                     {#if u.profilePic} |             <tr> | ||||||
|                       <div class="flex-shrink-0 h-10 w-10"> |               <th | ||||||
|                         <img |                 scope="col" | ||||||
|                           class="h-10 w-10 rounded-full" |                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||||
|                           src={u.profilePic} |                 Name | ||||||
|                           alt="" /> |               </th> | ||||||
|                       </div> |               <th | ||||||
|                     {/if} |                 scope="col" | ||||||
|                     <div class="ml-4"> |                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||||
|                       <div |                 Status | ||||||
|                         class="text-sm font-medium text-gray-900 dark:text-gray-100"> |               </th> | ||||||
|                         {u.firstname} |               <th | ||||||
|                         {u.middlename || ''} |                 scope="col" | ||||||
|                         {u.lastname} |                 class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> | ||||||
|                       </div> |                 Groups | ||||||
|                       <div class="text-sm text-gray-500"> |               </th> | ||||||
|                         {u.email || u.username} |               <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 dark:text-gray-100"> | ||||||
|  |                           {u.firstname} | ||||||
|  |                           {u.middlename || ''} | ||||||
|  |                           {u.lastname} | ||||||
|  |                         </div> | ||||||
|  |                         <div class="text-sm text-gray-500"> | ||||||
|  |                           {u.email || u.username} | ||||||
|  |                         </div> | ||||||
|                       </div> |                       </div> | ||||||
|                     </div> |                     </div> | ||||||
|                   </div> |                   </td> | ||||||
|                 </td> |                   <td class="px-6 py-4 whitespace-nowrap"> | ||||||
|                 <td class="px-6 py-4 whitespace-nowrap"> |                     {#if u.enabled} | ||||||
|                   {#if u.enabled} |                       <span | ||||||
|                     <span |                         class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span> | ||||||
|                       class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span> |                     {:else} | ||||||
|  |                       <span | ||||||
|  |                         class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Inactive</span> | ||||||
|  |                     {/if} | ||||||
|  |                   </td> | ||||||
|  |                   <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||||
|  |                     {#each u.groups as g} | ||||||
|  |                       <a | ||||||
|  |                         href="../groups/{g.id}" | ||||||
|  |                         class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a> | ||||||
|  |                     {/each} | ||||||
|  |                   </td> | ||||||
|  |                   {#if active_deletes[u.id] === true} | ||||||
|  |                     <td | ||||||
|  |                       class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> | ||||||
|  |                       <button | ||||||
|  |                         on:click={() => { | ||||||
|  |                           active_deletes[u.id] = false; | ||||||
|  |                         }} | ||||||
|  |                         tabindex="0" | ||||||
|  |                         class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel | ||||||
|  |                         Delete</button> | ||||||
|  |                       <button | ||||||
|  |                         on:click={() => { | ||||||
|  |                           UserService.userControllerRemove(u.id, true) | ||||||
|  |                             .then((resp) => { | ||||||
|  |                               current_users=current_users.filter(obj=>obj.id!==u.id); | ||||||
|  |                             }) | ||||||
|  |                             .catch((err) => { | ||||||
|  |                               // error deleting user | ||||||
|  |                             }); | ||||||
|  |                         }} | ||||||
|  |                         tabindex="0" | ||||||
|  |                         class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Confirm | ||||||
|  |                         Delete</button> | ||||||
|  |                     </td> | ||||||
|                   {:else} |                   {:else} | ||||||
|                     <span |                     <td | ||||||
|                       class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Inactive</span> |                       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} |                   {/if} | ||||||
|                 </td> |                 </tr> | ||||||
|                 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> |               {/if} | ||||||
|                   {#each u.groups as g} |             {/each} | ||||||
|                     <a |           </tbody> | ||||||
|                       href="../groups/{g.id}" |         </table> | ||||||
|                       class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a> |       </div> | ||||||
|                   {/each} |     {/if} | ||||||
|                 </td> |   {:catch error} | ||||||
|                 <td |     <div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> | ||||||
|                   class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> |       <span class="inline-block align-middle mr-8"> | ||||||
|                   <a |         <b class="capitalize">{$_('general_promise_error')}</b> | ||||||
|                     href="./{u.id}" |         {error} | ||||||
|                     class="text-indigo-600 hover:text-indigo-900">Edit</a> |       </span> | ||||||
|                   <span |  | ||||||
|                     tabindex="0" |  | ||||||
|                     href="#" |  | ||||||
|                     class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</span> |  | ||||||
|                 </td> |  | ||||||
|               </tr> |  | ||||||
|             {/if} |  | ||||||
|           {/each} |  | ||||||
|         </tbody> |  | ||||||
|       </table> |  | ||||||
|     </div> |     </div> | ||||||
|   {/if} |   {/await} | ||||||
| {:catch error} | {/if} | ||||||
|   <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} |  | ||||||
|   | |||||||
| @@ -71,6 +71,7 @@ | |||||||
|     "general-stats": "General Stats", |     "general-stats": "General Stats", | ||||||
|     "general_promise_error": "😢 Error", |     "general_promise_error": "😢 Error", | ||||||
|     "goback": "Go Home", |     "goback": "Go Home", | ||||||
|  |     "groups": "Groups", | ||||||
|     "hallo": "hello", |     "hallo": "hello", | ||||||
|     "installed-version": "Installed version", |     "installed-version": "Installed version", | ||||||
|     "invalid-mail-reset": "the provided email is invalid", |     "invalid-mail-reset": "the provided email is invalid", | ||||||
| @@ -116,6 +117,9 @@ | |||||||
|     "track-length-in-m": "Track Length in m", |     "track-length-in-m": "Track Length in m", | ||||||
|     "track-name": "Track name", |     "track-name": "Track name", | ||||||
|     "tracks": "Tracks", |     "tracks": "Tracks", | ||||||
|  |     "updating-user": "updating user...", | ||||||
|  |     "user-updated": "User updated", | ||||||
|  |     "username": "Username", | ||||||
|     "users": "Users", |     "users": "Users", | ||||||
|     "valid-email-is-required": "valid email is required", |     "valid-email-is-required": "valid email is required", | ||||||
|     "welcome_wavinghand": "Welcome 👋", |     "welcome_wavinghand": "Welcome 👋", | ||||||
|   | |||||||
| @@ -27,6 +27,8 @@ const store = () => { | |||||||
| 			AuthService.authControllerRefresh({ token: state.auth.refresh_token }).then((auth) => { | 			AuthService.authControllerRefresh({ token: state.auth.refresh_token }).then((auth) => { | ||||||
| 				console.log('got new auth'); | 				console.log('got new auth'); | ||||||
| 				OpenAPI.TOKEN = auth.access_token; | 				OpenAPI.TOKEN = auth.access_token; | ||||||
|  | 				const jwtinfo = JSON.parse(atob(auth.access_token.split('.')[1])); | ||||||
|  | 				state.jwtinfo = jwtinfo; | ||||||
| 				localForage.setItem('logindata', auth); | 				localForage.setItem('logindata', auth); | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
| @@ -39,8 +41,8 @@ const store = () => { | |||||||
| 				// | 				// | ||||||
| 				state.refreshInterval = setInterval(() => { | 				state.refreshInterval = setInterval(() => { | ||||||
| 					this.refreshAuth(); | 					this.refreshAuth(); | ||||||
| 					// 4min | 					// 2min | ||||||
| 				}, 4 * 60000); | 				}, 2 * 60000); | ||||||
| 				// | 				// | ||||||
| 				return state; | 				return state; | ||||||
| 			}); | 			}); | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ module.exports = { | |||||||
| 	purge: { | 	purge: { | ||||||
| 		content: [ './src/**/*.svelte' ] | 		content: [ './src/**/*.svelte' ] | ||||||
| 	}, | 	}, | ||||||
| 	darkMode: 'media', | 	// darkMode: 'media', | ||||||
| 	variants: {}, | 	variants: {}, | ||||||
| 	plugins: [], | 	plugins: [], | ||||||
| 	theme: { | 	theme: { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user