sub users webui feature
This commit is contained in:
@@ -3,10 +3,8 @@
|
||||
<nav class="navbar is-dark mb-4 is-unselectable">
|
||||
<div class="navbar-brand pl-5">
|
||||
<NuxtLink class="navbar-item" to="/" @click.native="(e) => changeRoute(e)">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-home"/></span>
|
||||
<span>Home</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-home"/></span>
|
||||
<span>Home</span>
|
||||
</NuxtLink>
|
||||
|
||||
<a class="navbar-item is-hidden-tablet" id="top" href="#bottom">
|
||||
@@ -23,63 +21,68 @@
|
||||
<div class="navbar-menu" :class="{ 'is-active': showMenu }">
|
||||
<div class="navbar-start" v-if="hasAPISettings && !showConnection">
|
||||
<NuxtLink class="navbar-item" to="/backends" @click.native="(e) => changeRoute(e)">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-server"/></span>
|
||||
<span>Backends</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-server"/></span>
|
||||
<span>Backends</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="navbar-item" to="/history"
|
||||
@click.native="(e) => changeRoute(e, () => dEvent('history_main_link_clicked', { 'clear': true }))">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-history"/></span>
|
||||
<span>History</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-history"/></span>
|
||||
<span>History</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="navbar-item" to="/tasks" @click.native="(e) => changeRoute(e)">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-tasks"/></span>
|
||||
<span>Tasks</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-tasks"/></span>
|
||||
<span>Tasks</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="navbar-item" to="/env" @click.native="(e) => changeRoute(e)">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-cogs"/></span>
|
||||
<span>Env</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-cogs"/></span>
|
||||
<span>Env</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="navbar-item" to="/logs" @click.native="(e) => changeRoute(e)">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-globe"/></span>
|
||||
<span>Logs</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-globe"/></span>
|
||||
<span>Logs</span>
|
||||
</NuxtLink>
|
||||
|
||||
<div class="navbar-item has-dropdown">
|
||||
<a class="navbar-link" @click="(e) => openMenu(e)">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-ellipsis-vertical"/></span>
|
||||
<span>More</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-tools"/></span>
|
||||
<span>Tools</span>
|
||||
</a>
|
||||
|
||||
<div class="navbar-dropdown">
|
||||
<NuxtLink class="navbar-item" to="/tools/plex_token" @click.native="(e) => changeRoute(e)"
|
||||
v-if="hasAPISettings">
|
||||
<span class="icon"><i class="fas fa-key"/></span>
|
||||
<span>Plex Token</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="navbar-item" to="/tools/sub_users" @click.native="(e) => changeRoute(e)"
|
||||
v-if="hasAPISettings">
|
||||
<span class="icon"><i class="fas fa-users"/></span>
|
||||
<span>Sub Users</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item has-dropdown">
|
||||
<a class="navbar-link" @click="(e) => openMenu(e)">
|
||||
<span class="icon"><i class="fas fa-ellipsis-vertical"/></span>
|
||||
<span>More</span>
|
||||
</a>
|
||||
<div class="navbar-dropdown">
|
||||
|
||||
<NuxtLink class="navbar-item" to="/console" @click.native="(e) => changeRoute(e)" v-if="hasAPISettings">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-terminal"/></span>
|
||||
<span>Console</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-terminal"/></span>
|
||||
<span>Console</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="navbar-item" to="/processes" @click.native="(e) => changeRoute(e)"
|
||||
v-if="hasAPISettings">
|
||||
<span class="icon-text">
|
||||
<span class="icon"><i class="fas fa-microchip"/></span>
|
||||
<span>Processes</span>
|
||||
</span>
|
||||
<span class="icon"><i class="fas fa-microchip"/></span>
|
||||
<span>Processes</span>
|
||||
</NuxtLink>
|
||||
|
||||
<hr class="navbar-divider">
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"plyr": "^3.7.8",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-toastification": "^2.0.0-rc.5"
|
||||
"vue-toastification": "^2.0.0-rc.5",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<h1 class="title is-4">Tools</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<NuxtLink :to="`/plex_token`" v-text="'Validate plex token'"/>
|
||||
<NuxtLink :to="`/tools/plex_token`" v-text="'Validate plex token'"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
200
frontend/pages/tools/sub_users.vue
Normal file
200
frontend/pages/tools/sub_users.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="columns is-multiline">
|
||||
<div class="column is-12 is-clearfix is-unselectable">
|
||||
<span id="env_page_title" class="title is-4">
|
||||
<span class="icon"><i class="fas fa-users"/></span>
|
||||
Create Sub-users
|
||||
</span>
|
||||
<div class="is-pulled-right">
|
||||
<div class="field is-grouped">
|
||||
<p class="control">
|
||||
<button class="button is-purple" v-tooltip.bottom="'Export Association.'" @click="exportMapping">
|
||||
<span class="icon"><i class="fas fa-file-export"/></span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-primary" v-tooltip.bottom="'Create new user assoication.'" @click="addNewUser">
|
||||
<span class="icon"><i class="fas fa-plus"></i></span>
|
||||
</button>
|
||||
</p>
|
||||
<p class="control">
|
||||
<button class="button is-info" @click="loadContent" :disabled="isLoading"
|
||||
:class="{'is-loading':isLoading}">
|
||||
<span class="icon"><i class="fas fa-sync"/></span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-hidden-mobile">
|
||||
<span class="subtitle">
|
||||
Drag & Drop the relevant users accounts to form association.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<h2 class="title is-4">Matched users</h2>
|
||||
</div>
|
||||
|
||||
<div class="column is-12" v-for="(group, index) in matchedUsers" :key="index">
|
||||
<div class="card">
|
||||
<header class="card-header is-block">
|
||||
<div class="control has-icons-left">
|
||||
<input type="text" class="input is-fullwidth" v-model="group.user" required>
|
||||
<span class="icon is-left"><i class="fas fa-user"/></span>
|
||||
</div>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<draggable v-model="group.matched" :group="{ name: 'shared', pull: true, put: true }" animation="150"
|
||||
item-key="id">
|
||||
<template #item="{ element }">
|
||||
<div class="draggable-item">
|
||||
<span>{{ element.backend }}@{{ element.username }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<h2 class="title is-4">Users with no association.</h2>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="card">
|
||||
<header class="card-header is-block">
|
||||
<p class="card-header-title is-text-overflow">Users with no association.</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<draggable v-model="unmatched" :group="{ name: 'shared', pull: true, put: true }" animation="150"
|
||||
item-key="id">
|
||||
<template #item="{ element }">
|
||||
<div class="draggable-item">
|
||||
<span>{{ element.backend }}@{{ element.username }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
<div v-if="unmatched?.length <1">
|
||||
<Message message_class="has-background-success-90 has-text-dark" icon="fas fa-check-circle">
|
||||
<p>
|
||||
<span class="icon"><i class="fas fa-check"/></span>
|
||||
<span>All users are associated.</span>
|
||||
</p>
|
||||
</Message>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const data = {
|
||||
"matched": [
|
||||
{
|
||||
"user": "user1",
|
||||
"matched": [
|
||||
{
|
||||
"id": "u1a", "backend": "backend_name1", "username": "user_u1a"
|
||||
},
|
||||
{
|
||||
"id": "u1b", "backend": "backend_name2", "username": "user_u1b"
|
||||
},
|
||||
{
|
||||
"id": "u1c", "backend": "backend_name3", "username": "user_u1c"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"user": "user2",
|
||||
"matched": [
|
||||
{
|
||||
"id": "u2a", "backend": "backend_name1", "username": "user_u2a"
|
||||
},
|
||||
{
|
||||
"id": "u2b", "backend": "backend_name2", "username": "user_u2b"
|
||||
},
|
||||
{
|
||||
"id": "u2c", "backend": "backend_name3", "username": "user_u2c"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"user": "user3",
|
||||
"matched": [
|
||||
{
|
||||
"id": "u3a", "backend": "backend_name1", "username": "user_u3a"
|
||||
},
|
||||
{
|
||||
"id": "u3b", "backend": "backend_name2", "username": "user_u3b"
|
||||
},
|
||||
{
|
||||
"id": "u3c", "backend": "backend_name3", "username": "user_u3c"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"unmatched": [
|
||||
{
|
||||
"id": "u4a", "backend": "backend_name1", "username": "user_u4a"
|
||||
},
|
||||
{
|
||||
"id": "u4b", "backend": "backend_name2", "username": "user_u4b"
|
||||
},
|
||||
{
|
||||
"id": "u4c", "backend": "backend_name3", "username": "user_u4c"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const matchedUsers = ref(data.matched)
|
||||
const unmatched = ref(data.unmatched)
|
||||
|
||||
// Function to add a new matched group with a default name
|
||||
const addNewUser = () => {
|
||||
const newUserName = 'user ' + (matchedUsers.value.length + 1)
|
||||
matchedUsers.value.push({
|
||||
user: newUserName,
|
||||
matched: []
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Make containers flex so items wrap side by side */
|
||||
.users-list,
|
||||
.unmatched-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border: 1px dashed #ccc;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
/* Let draggable items size to content */
|
||||
.draggable-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
background: #f1f1f1;
|
||||
cursor: move;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin: .25rem;
|
||||
}
|
||||
</style>
|
||||
5
frontend/plugins/vuedraggable.client.js
Normal file
5
frontend/plugins/vuedraggable.client.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
nuxtApp.vueApp.component('draggable', draggable)
|
||||
})
|
||||
@@ -49,6 +49,11 @@
|
||||
"name": "autoscroll",
|
||||
"description": "Automatically scroll to an element.",
|
||||
"doc-url": ""
|
||||
},
|
||||
{
|
||||
"name": "sortable",
|
||||
"description": "Automatically sort.",
|
||||
"doc-url": ""
|
||||
}
|
||||
],
|
||||
"vue-components": [
|
||||
|
||||
@@ -3958,6 +3958,11 @@ smob@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/smob/-/smob-1.5.0.tgz#85d79a1403abf128d24d3ebc1cdc5e1a9548d3ab"
|
||||
integrity sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==
|
||||
|
||||
sortablejs@1.14.0:
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8"
|
||||
integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==
|
||||
|
||||
source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
@@ -4539,6 +4544,13 @@ vue@^3.4.21, vue@^3.5.13:
|
||||
"@vue/server-renderer" "3.5.13"
|
||||
"@vue/shared" "3.5.13"
|
||||
|
||||
vuedraggable@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.1.0.tgz#edece68adb8a4d9e06accff9dfc9040e66852270"
|
||||
integrity sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==
|
||||
dependencies:
|
||||
sortablejs "1.14.0"
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
|
||||
Reference in New Issue
Block a user