Merge pull request 'feature/12-user-management' (#39) from feature/12-user-management into dev
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #39 close #12
This commit is contained in:
commit
d48b9b7f91
@ -48,7 +48,7 @@
|
|||||||
OpenAPI.BASE = config.baseurl;
|
OpenAPI.BASE = config.baseurl;
|
||||||
import { register as registerSW } from "./swmodule";
|
import { register as registerSW } from "./swmodule";
|
||||||
import TeamDetail from "./components/TeamDetail.svelte";
|
import TeamDetail from "./components/TeamDetail.svelte";
|
||||||
import RunnerDetail from "./components/RunnerDetail.svelte";
|
import UserPermissions from "./components/UserPermissions.svelte";
|
||||||
store.init();
|
store.init();
|
||||||
registerSW();
|
registerSW();
|
||||||
</script>
|
</script>
|
||||||
@ -72,9 +72,14 @@
|
|||||||
<Route path="/">
|
<Route path="/">
|
||||||
<Users />
|
<Users />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/:userid" let:params>
|
<Route path="/:userid/*" let:params>
|
||||||
|
<Route path="/">
|
||||||
<UserDetail {params} />
|
<UserDetail {params} />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/permissions/">
|
||||||
|
<UserPermissions {params} />
|
||||||
|
</Route>
|
||||||
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/tracks/*">
|
<Route path="/tracks/*">
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
@ -82,14 +87,9 @@
|
|||||||
</Route>
|
</Route>
|
||||||
<Route path="/:trackid" let:params />
|
<Route path="/:trackid" let:params />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/runners/*">
|
<Route path="/runners">
|
||||||
<Route path="/">
|
|
||||||
<Runners />
|
<Runners />
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/:runnerid" let:params>
|
|
||||||
<RunnerDetail {params} />
|
|
||||||
</Route>
|
|
||||||
</Route>
|
|
||||||
<Route path="/teams/*">
|
<Route path="/teams/*">
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
<Teams />
|
<Teams />
|
||||||
|
@ -28,7 +28,10 @@
|
|||||||
$: isLastnameValid = lastname_input_value.trim().length !== 0;
|
$: isLastnameValid = lastname_input_value.trim().length !== 0;
|
||||||
$: isFirstnameValid = firstname_input_value.trim().length !== 0;
|
$: isFirstnameValid = firstname_input_value.trim().length !== 0;
|
||||||
$: createbtnenabled =
|
$: createbtnenabled =
|
||||||
isFirstnameValid && isLastnameValid && isEmailValid && isPasswordValid;
|
isFirstnameValid &&
|
||||||
|
isLastnameValid &&
|
||||||
|
isPasswordValid &&
|
||||||
|
!(!isEmailValid && username_input_value.trim().length === 0);
|
||||||
(function () {
|
(function () {
|
||||||
document.onkeydown = function (e) {
|
document.onkeydown = function (e) {
|
||||||
e = e || window.event;
|
e = e || window.event;
|
||||||
@ -50,19 +53,14 @@
|
|||||||
text: "User is being added...",
|
text: "User is being added...",
|
||||||
duration: -1,
|
duration: -1,
|
||||||
}).showToast();
|
}).showToast();
|
||||||
let postdata={
|
UserService.userControllerPost({
|
||||||
firstname: firstname_input_value,
|
firstname: firstname_input_value,
|
||||||
lastname: lastname_input_value,
|
lastname: lastname_input_value,
|
||||||
middlename: middlename_input_value,
|
middlename: middlename_input_value,
|
||||||
password: password_input_value
|
password: password_input_value,
|
||||||
};
|
email: email_input_value,
|
||||||
if(email_input_value!==""){
|
username: username_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 = "";
|
||||||
@ -238,18 +236,21 @@
|
|||||||
<input
|
<input
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
placeholder={$_('e-mail-adress')}
|
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:value={email_input_value}
|
||||||
bind:this={email_input}
|
bind:this={email_input}
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
{#if !isEmailValid}
|
<!-- {#if !isEmailValid}
|
||||||
<span
|
<span
|
||||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
{$_('valid-email-is-required')}
|
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>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,26 +2,58 @@
|
|||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
import lodashIsEqual from "lodash.isequal";
|
import lodashIsEqual from "lodash.isequal";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
import {
|
import { UserService, UserGroupService } from "@odit/lfk-client-js";
|
||||||
UserService,
|
|
||||||
UserGroupService,
|
|
||||||
PermissionService,
|
|
||||||
} from "@odit/lfk-client-js";
|
|
||||||
import Toastify from "toastify-js";
|
import Toastify from "toastify-js";
|
||||||
import PromiseError from "./PromiseError.svelte";
|
import PromiseError from "./PromiseError.svelte";
|
||||||
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 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;
|
$: delete_triggered = false;
|
||||||
$: original_data = {};
|
$: original_data = {};
|
||||||
$: editable_userdata = {};
|
$: editable_userdata = {};
|
||||||
$: allpermissions = [];
|
|
||||||
$: allgroups = [];
|
$: allgroups = [];
|
||||||
$: allgroups_ids = [];
|
$: allgroups_ids = [];
|
||||||
$: usergroups_array_objects = [];
|
|
||||||
$: usergroups_array = [];
|
$: usergroups_array = [];
|
||||||
let usergroups_array_original = [];
|
$: search_permission = "";
|
||||||
user_promise.then((data) => {
|
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;
|
data_loaded = true;
|
||||||
original_data = Object.assign(original_data, data);
|
original_data = Object.assign(original_data, data);
|
||||||
editable_userdata = data;
|
editable_userdata = data;
|
||||||
@ -36,37 +68,28 @@
|
|||||||
UserGroupService.userGroupControllerGetAll().then((data) => {
|
UserGroupService.userGroupControllerGetAll().then((data) => {
|
||||||
allgroups = data;
|
allgroups = data;
|
||||||
});
|
});
|
||||||
const permissions_promise = PermissionService.permissionControllerGetAll();
|
|
||||||
permissions_promise.then((data) => {
|
|
||||||
data.forEach((p) => {
|
|
||||||
allpermissions=allpermissions.concat([p.target + ":" + p.action])
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$: changes_performed = !lodashIsEqual(original_data, editable_userdata);
|
$: changes_performed = !lodashIsEqual(original_data, editable_userdata);
|
||||||
$: groups_changed = JSON.stringify(usergroups_array)===JSON.stringify(usergroups_array_original);
|
$: groups_changed =
|
||||||
|
JSON.stringify(usergroups_array) ===
|
||||||
|
JSON.stringify(usergroups_array_original);
|
||||||
$: save_enabled = changes_performed || !groups_changed;
|
$: save_enabled = changes_performed || !groups_changed;
|
||||||
function submit() {
|
function submit() {
|
||||||
if (
|
|
||||||
!lodashIsEqual(original_data.permissions, editable_userdata.permissions)
|
|
||||||
) {
|
|
||||||
// TODO: add+delete permissions
|
|
||||||
}
|
|
||||||
if (data_loaded === true && save_enabled) {
|
if (data_loaded === true && save_enabled) {
|
||||||
let tmp=[];
|
editable_userdata.groups = usergroups_array;
|
||||||
usergroups_array.forEach(g => {
|
|
||||||
const group=allgroups.find(obj=>obj.id===g);
|
|
||||||
tmp.push(group);
|
|
||||||
});
|
|
||||||
editable_userdata.groups=tmp;
|
|
||||||
Toastify({
|
Toastify({
|
||||||
text: $_("updating-user"),
|
text: $_("updating-user"),
|
||||||
duration: 2500,
|
duration: 2500,
|
||||||
}).showToast();
|
}).showToast();
|
||||||
UserService.userControllerPut(original_data.id, editable_userdata)
|
UserService.userControllerPut(original_data.id, editable_userdata)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
Object.assign(original_data, editable_userdata);
|
Object.assign(original_data, resp);
|
||||||
original_data = editable_userdata;
|
Object.assign(editable_userdata, resp);
|
||||||
Object.assign(original_data, editable_userdata);
|
original_data.permissions = resp.permissions;
|
||||||
|
usergroups_array = [];
|
||||||
|
resp.groups.forEach((g) => {
|
||||||
|
usergroups_array = usergroups_array.concat([g.id]);
|
||||||
|
});
|
||||||
|
usergroups_array_original = usergroups_array;
|
||||||
//
|
//
|
||||||
Toastify({
|
Toastify({
|
||||||
text: $_("user-updated"),
|
text: $_("user-updated"),
|
||||||
@ -74,9 +97,7 @@
|
|||||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||||
}).showToast();
|
}).showToast();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {});
|
||||||
});
|
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function deleteUser() {
|
function deleteUser() {
|
||||||
@ -84,8 +105,7 @@
|
|||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
location.replace("./");
|
location.replace("./");
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -135,7 +155,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-8 text-3xl font-extrabold leading-tight">
|
<div class="mb-8 text-3xl font-extrabold">
|
||||||
{original_data.firstname}
|
{original_data.firstname}
|
||||||
{original_data.middlename || ''}
|
{original_data.middlename || ''}
|
||||||
{original_data.lastname}
|
{original_data.lastname}
|
||||||
@ -170,31 +190,30 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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
|
<img
|
||||||
alt={$_('profile-picture')}
|
alt={$_('profile-picture')}
|
||||||
class="inline-block h-20 w-20 rounded-full overflow-hidden bg-gray-100"
|
class="h-20 w-20 rounded-full overflow-hidden bg-gray-100"
|
||||||
src={editable_userdata.profilePic} />
|
src={editable_userdata.profilePic} />
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Change</button>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- -->
|
|
||||||
<div class="mt-3 text-sm w-full">
|
<div class="mt-3 text-sm w-full">
|
||||||
|
<label
|
||||||
|
for="enabled"
|
||||||
|
class="ml-1 font-medium text-gray-700">Active?</label>
|
||||||
|
<br />
|
||||||
|
<p class="text-gray-500">
|
||||||
<input
|
<input
|
||||||
id="enabled"
|
id="enabled"
|
||||||
on:change={() => {
|
on:change={() => {
|
||||||
editable_userdata.enabled = !editable_userdata.enabled;
|
editable_userdata.enabled = !editable_userdata.enabled;
|
||||||
// TODO: this reactive set does not work?
|
|
||||||
}}
|
}}
|
||||||
name="enabled"
|
name="enabled"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={editable_userdata.enabled}
|
checked={editable_userdata.enabled}
|
||||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
||||||
<label
|
set the user active/ inactive
|
||||||
for="enabled"
|
</p>
|
||||||
class="ml-1 font-medium text-gray-700">Active?</label>
|
|
||||||
<p class="text-gray-500">set the user active/ inactive</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm w-full">
|
<div class="text-sm w-full">
|
||||||
<label
|
<label
|
||||||
@ -206,7 +225,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={editable_userdata.firstname}
|
bind:value={editable_userdata.firstname}
|
||||||
name="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" />
|
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">
|
<div class="text-sm w-full">
|
||||||
<label
|
<label
|
||||||
@ -218,7 +237,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={editable_userdata.middlename}
|
bind:value={editable_userdata.middlename}
|
||||||
name="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" />
|
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">
|
<div class="text-sm w-full">
|
||||||
<label
|
<label
|
||||||
@ -230,7 +249,7 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={editable_userdata.lastname}
|
bind:value={editable_userdata.lastname}
|
||||||
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 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">
|
<div class="text-sm w-full">
|
||||||
<label
|
<label
|
||||||
@ -242,7 +261,7 @@
|
|||||||
type="email"
|
type="email"
|
||||||
bind:value={editable_userdata.email}
|
bind:value={editable_userdata.email}
|
||||||
name="email"
|
name="email"
|
||||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" />
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm w-full">
|
<div class="text-sm w-full">
|
||||||
<label
|
<label
|
||||||
@ -254,14 +273,14 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={editable_userdata.username}
|
bind:value={editable_userdata.username}
|
||||||
name="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 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">
|
<div class="text-sm w-full">
|
||||||
<span class="font-medium">{$_('groups')}</span>
|
<span class="font-medium">{$_('groups')}</span>
|
||||||
<!-- svelte-ignore a11y-no-onchange -->
|
<!-- svelte-ignore a11y-no-onchange -->
|
||||||
<select
|
<select
|
||||||
bind:value={usergroups_array}
|
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 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"
|
||||||
multiple>
|
multiple>
|
||||||
{#each allgroups as g}
|
{#each allgroups as g}
|
||||||
{#if usergroups_array.includes(g.id)}
|
{#if usergroups_array.includes(g.id)}
|
||||||
@ -272,62 +291,30 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm w-full">
|
<div class="text-sm w-full mt-8">
|
||||||
<span class="font-medium">Permissions</span>
|
<p class="font-medium mb-4">
|
||||||
<div class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center">
|
{$_('permissions')}
|
||||||
<!-- -->
|
<a
|
||||||
<div class="flex flex-wrap -mx-1 overflow-hidden">
|
class="px-4 py-2 bg-gray-500 rounded-md text-white"
|
||||||
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
|
href="/users/{params.userid}/permissions/">{$_('edit-permissions')}</a>
|
||||||
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 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple 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>
|
</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}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</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 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple 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} />
|
||||||
|
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,11 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
import AddUserModal from "./AddUserModal.svelte";
|
import AddUserModal from "./AddUserModal.svelte";
|
||||||
|
import users_empty from "./users_empty.svg";
|
||||||
let modal_open = false;
|
let modal_open = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="text-center items-center justify-center">
|
<div class="text-center items-center justify-center">
|
||||||
<p class="mb-16 text-lg text-gray-500">
|
<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 class="font-bold">There are no users added yet.</span><br />
|
||||||
<span>Add your first user</span>
|
<span>Add your first user</span>
|
||||||
</p>
|
</p>
|
||||||
|
@ -29,17 +29,17 @@
|
|||||||
{#if current_users.length === 0}
|
{#if current_users.length === 0}
|
||||||
<UsersEmptyState />
|
<UsersEmptyState />
|
||||||
{:else}
|
{:else}
|
||||||
{#if advanced_search}
|
<!-- {#if advanced_search}
|
||||||
advanced search
|
advanced search
|
||||||
{:else}
|
{:else} -->
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
bind:value={searchvalue}
|
bind:value={searchvalue}
|
||||||
placeholder={$_('datatable.search')}
|
placeholder={$_('datatable.search')}
|
||||||
aria-label={$_('datatable.search')}
|
aria-label={$_('datatable.search')}
|
||||||
class="gridjs-input gridjs-search-input mb-4" />
|
class="gridjs-input gridjs-search-input mb-4" />
|
||||||
{/if}
|
<!-- {/if} -->
|
||||||
<button
|
<!-- <button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
advanced_search = !advanced_search;
|
advanced_search = !advanced_search;
|
||||||
}}
|
}}
|
||||||
@ -48,7 +48,7 @@
|
|||||||
{#if advanced_search}
|
{#if advanced_search}
|
||||||
toggle simple search
|
toggle simple search
|
||||||
{:else}toggle advanced search{/if}
|
{:else}toggle advanced search{/if}
|
||||||
</button>
|
</button> -->
|
||||||
<div
|
<div
|
||||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||||
<table class="divide-y divide-gray-200 w-full">
|
<table class="divide-y divide-gray-200 w-full">
|
||||||
|
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 |
@ -60,6 +60,7 @@
|
|||||||
"dont-panic-were-resetting-it": "Don't panic, we're resetting it ✌",
|
"dont-panic-were-resetting-it": "Don't panic, we're resetting it ✌",
|
||||||
"drag-and-drop-your-files-or": "Drag & Drop your files or",
|
"drag-and-drop-your-files-or": "Drag & Drop your files or",
|
||||||
"e-mail-adress": "E-Mail Adress",
|
"e-mail-adress": "E-Mail Adress",
|
||||||
|
"edit-permissions": "edit permissions",
|
||||||
"email_address_or_username": "Email / username",
|
"email_address_or_username": "Email / username",
|
||||||
"error_on_login": "Error on login",
|
"error_on_login": "Error on login",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
@ -119,6 +120,7 @@
|
|||||||
"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!",
|
"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": "Password",
|
||||||
"password-is-required": "Password is required",
|
"password-is-required": "Password is required",
|
||||||
|
"permissions": "Permissions",
|
||||||
"phone": "Phone",
|
"phone": "Phone",
|
||||||
"please-provide-the-required-csv-xlsx-file": "Please provide the required csv/ xlsx file",
|
"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-runner": "Please provide the required information to add a new runner.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user