Commit 1544861b authored by gj4210's avatar gj4210 👽
Browse files

ADD: Global Search

warning: broken mess atm
+ also changed ''API-Tokens" to "Accounts & Tokens"
parent 36a7a677
Pipeline #105455 passed with stages
in 7 minutes and 32 seconds
<template>
<b-form inline @submit="searchAction" class="mr-2">
<transition name="slide">
<b-form-group v-if="show_search || ($refs.navdropdown && $refs.navdropdown.show)">
<b-form-input
size="sm" placeholder="Suche" id="search" ref="search"
v-model="search_input" class="mr-2 search"
@focus="search_has_focus = true"
@blur="search_has_focus = false"
/>
<b-button class="search-clear bg-transparent" size="sm" v-if="search_input !== ''"
@click="search_input = ''">
<font-awesome-icon :icon="['fas','times']" class="text-secondary"/>
</b-button>
</b-form-group>
</transition>
<b-popover custom-class="popover-wide suggestions-popover shadow"
:show="search_input !== '' && search_has_focus"
target="search" placement="bottom">
<div class="suggestions-wrapper">
<div v-for="(suggestion, index) in suggestions"
:key="'search-suggestion-' + index"
class="suggestion p-3"
@click="suggestionClicked(suggestion)">
<font-awesome-icon class="text-secondary mr-2 suggestion-icon"
:icon="suggestion.type === 'page' ? 'link': 'question'"/>
<font-awesome-icon class="text-primary mr-2 suggestion-arrow" :icon="['fas', 'arrow-right']"/>
<b>{{ suggestion.name[0] }}</b>{{ suggestion.name[1] }}<b>{{ suggestion.name[2] }}</b>
</div>
<div v-if="suggestions && suggestions.length === 0"
class="font-italic text-center p-3">
Keine Ergebnisse
</div>
</div>
</b-popover>
<b-button class="search-button" size="sm" @click="searchAction"
:variant="search_input === '' ? 'secondary' : 'success'">
<font-awesome-icon :icon="['fas','search']"/>
</b-button>
<div class="search-cover-wrapper">
<div class="search-cover"/>
</div>
</b-form>
</template>
<script>
export default {
name: "GlobalSearch",
data() {
return {
show_search: false,
search_input: '',
search_has_focus: false,
pages: [
{
'name': 'DNSVS',
'url': '/dnsvs'
},
{
'name': 'BCDs',
'url': '/dnsvs/bcds'
},
{
'name': 'Accounts & Tokens',
'url': '/user/tokens'
},
{
'name': 'API (Swagger)',
'url': '/swagger'
},
{
'name': 'MACAuth',
'url': '/macauth'
},
],
test_search_terms: [
'Easter Egg',
'00:DE:AD:BE:EF:00',
'127.0.0.1',
'100.124.99.212:25565',
'Internet ist kaputt :(',
'Wörter ausdenken ist mein Job.',
'Sinn des Lebens',
'Funktioniert die Suche überhaupt?',
'Irgendwann wird Alles hier ersetzt.',
'Lorem Ipsum',
'What is love?',
'Minecraft XOR Gate Redstone Tutorial',
'Fortnite Epic Gamer Moments',
'Where can I buy Gamer Girl Bathwater?',
'How to fix ID-10-T error?',
'12345678?',
'Is password a good password?',
'Why my pc hacked?',
'VIRUS HELP',
'Why does my Windows support guy sound like his name is not George Smith?',
'scc.fail',
'Why internet not work?',
'Help',
'hellooooo?'
],
suggestion_hover: []
}
},
computed: {
suggestions() {
let suggestions = []
this.pages.forEach(page => {
let index = page.name.toLowerCase().indexOf(this.search_input.toLowerCase())
if (index !== -1) {
suggestions.push({
'name':
[page.name.substring(0, index),
page
.name.substring(index, index + this.search_input.length),
page
.name.substring(index + this.search_input.length)
],
'type': 'page',
'url': page.url
})
}
}
)
this.test_search_terms.forEach(term => {
let index = term.toLowerCase().indexOf(this.search_input.toLowerCase())
if (index !== -1) {
suggestions.push({
'name':
[term.substring(0, index),
term
.substring(index, index + this.search_input.length),
term
.substring(index + this.search_input.length)
],
'type': 'test'
})
}
})
return suggestions
}
},
methods: {
searchAction(e) {
e.preventDefault()
if (this.search_input === '') {
this.show_search = !this.show_search
if (this.show_search) {
this.$nextTick(() => {
this.$refs.search.focus()
})
}
} else {
// TODO: actually search stuff
window.console.log('Search: ' + this.search_input)
this.search_input = ''
}
},
suggestionClicked(suggestion) {
if (suggestion.type === 'page' && this.$router.currentRoute !== suggestion.url) {
this.$router.push(suggestion.url)
}
if (suggestion.type === 'test') {
window.console.log('Clicked test suggestion: ' + suggestion.name[0] + suggestion.name[1] + suggestion.name[2])
}
}
}
}
</script>
<style scoped>
.slide-enter-active,
.slide-leave-active {
transition: transform 250ms;
z-index: -10;
}
.slide-enter,
.slide-leave-to {
transform: translateX(120%);
}
.search-cover-wrapper {
position: relative;
width: 0;
height: 0;
}
.search-cover {
z-index: -1;
position: absolute;
left: -30px;
top: -25px;
width: 300px;
height: 50px;
background: #343A40;
}
.search {
max-width: 250px;
}
.search-button {
z-index: 1;
}
.suggestions-popover {
width: 300px;
max-height: 75vh;
overflow-y: scroll;
}
.suggestions-popover::-webkit-scrollbar {
display: none;
}
.suggestions-wrapper {
margin: -0.5rem -0.75rem;
}
.suggestion {
cursor: pointer;
}
.suggestion:nth-child(even) {
background: #f1f1f1;
}
.suggestion-arrow {
display: none;
width: 16px;
}
.suggestion-icon {
width: 16px;
}
.suggestion:hover {
background: #d7d7d7;
}
.suggestion:hover > .suggestion-arrow {
display: initial;
}
.suggestion:hover > .suggestion-icon {
display: none;
}
.search-clear,
.search-clear:focus,
.search-clear:hover,
.search-clear:active {
position: relative;
left: -35px;
margin-right: -35px;
border: none;
}
</style>
<template> <template>
<b-navbar toggleable="lg" fixed="top" :type="$sysinfo.host_oper_mode.is_prod?'light':'dark'" <b-navbar toggleable="lg" fixed="top" :type="$sysinfo.host_oper_mode.is_prod?'light':'dark'"
:variant="$sysinfo.host_oper_mode.is_prod?'light':'dark'" :variant="$sysinfo.host_oper_mode.is_prod?'light':'dark'"
:class="`bg-${$sysinfo.host_oper_mode.mode}` + ' shadow'"> :class="`bg-${$sysinfo.host_oper_mode.mode}` + ' shadow'">
<!-- Brand and toggle get grouped for better mobile display --> <!-- Brand and toggle get grouped for better mobile display -->
<b-navbar-brand to="/"> <b-navbar-brand to="/">
<img class="d-inline-block align-top scc-img" alt="SCC" src="@/assets/img/scc_logo_small.png"/> <img class="d-inline-block align-top scc-img" alt="SCC" src="@/assets/img/scc_logo_small.png"/>
<span v-if="!$sysinfo.host_oper_mode.is_prod"> Instanz: {{$sysinfo.host_oper_mode.mode}}</span> <span v-if="!$sysinfo.host_oper_mode.is_prod"> Instanz: {{ $sysinfo.host_oper_mode.mode }}</span>
</b-navbar-brand> </b-navbar-brand>
<b-navbar-toggle target="navbarNavDropdown"> <b-navbar-toggle target="navbarNavDropdown">
<font-awesome-icon icon="bars"/> <font-awesome-icon icon="bars"/>
</b-navbar-toggle> </b-navbar-toggle>
<!-- Collect the nav links, forms, and other content for toggling --> <!-- Collect the nav links, forms, and other content for toggling -->
<b-collapse is-nav id="navbarNavDropdown" ref="navdropdown"> <b-collapse is-nav id="navbarNavDropdown" ref="navdropdown">
<b-navbar-nav> <b-navbar-nav>
<b-nav-item v-if="$sysinfo_mods_by_name['dnsvs']" to="/dnsvs/" <b-nav-item v-if="$sysinfo_mods_by_name['dnsvs']" to="/dnsvs/"
:active="$route.path.startsWith('/dnsvs/')">DNSVS :active="$route.path.startsWith('/dnsvs/')">DNSVS
</b-nav-item> </b-nav-item>
<b-nav-item v-if="$sysinfo_mods_by_name['dhcp_leases']" to="/dhcp-leases/" <b-nav-item v-if="$sysinfo_mods_by_name['dhcp_leases']" to="/dhcp-leases/"
:active="$route.path.startsWith('/dhcp-leases/')">DHCP-Leases :active="$route.path.startsWith('/dhcp-leases/')">DHCP-Leases
</b-nav-item> </b-nav-item>
<b-nav-item href="https://www-net.scc.kit.edu/~netadmin/natvs/user/wrapper.cgi/" target="_blank"> <b-nav-item href="https://www-net.scc.kit.edu/~netadmin/natvs/user/wrapper.cgi/" target="_blank">
NATVS+ NATVS+
</b-nav-item> </b-nav-item>
<b-nav-item href="https://www-net.scc.kit.edu/~netadmin/netdoc/user/wrapper.cgi/" target="_blank"> <b-nav-item href="https://www-net.scc.kit.edu/~netadmin/netdoc/user/wrapper.cgi/" target="_blank">
NETDOC NETDOC
</b-nav-item> </b-nav-item>
</b-navbar-nav> </b-navbar-nav>
<b-navbar-nav class="ml-auto"> <b-navbar-nav class="ml-auto">
<b-form inline @submit="searchAction" class="mr-2"> <GlobalSearch/>
<transition name="slide"> <b-nav-item :active="$store.state.show_sidebar"
<b-form-input v-if="show_search || ($refs.navdropdown && $refs.navdropdown.show)" ref="search" @click.stop="$store.commit('showSidebar', !$store.state.show_sidebar)">
size="sm" placeholder="Suche" <font-awesome-icon :icon="['fas', 'tasks']"/>
v-model="search_input" class="mr-2 search"/> Geplante Aktionen
</transition> <b-badge variant="primary">{{ $store.state.ta_list ? $store.state.ta_list.length : 0 }}</b-badge>
<b-button class="search-button" size="sm" @click="searchAction" </b-nav-item>
:variant="search_input === '' ? 'secondary' : 'success'"> <b-nav-item-dropdown v-if="$store.state.user" right
<font-awesome-icon :icon="['fas','search']"/> :toggle-class="$route.path.startsWith('/user/')?'active':''">
</b-button> <template slot="button-content">
<div class="search-cover-wrapper"> <font-awesome-icon :icon="['fas', 'user']"/>
<div class="search-cover"/> <span v-if="$store.state.user"> {{ $store.state.user.login_name }}</span>
</div> </template>
</b-form> <template v-if="$store.state.user">
<b-nav-item :active="$store.state.show_sidebar" <b-dropdown-item to="/user/tokens" :active="$route.path === '/user/tokens'">
@click.stop="$store.commit('showSidebar', !$store.state.show_sidebar)"> <font-awesome-icon :icon="['fas', 'code']"/>
<font-awesome-icon :icon="['fas', 'tasks']"/> Accounts & Tokens
Geplante Aktionen </b-dropdown-item>
<b-badge variant="primary">{{$store.state.ta_list ? $store.state.ta_list.length : 0}}</b-badge> <b-dropdown-divider/>
</b-nav-item> <b-dropdown-item @click="logout">
<b-nav-item-dropdown v-if="$store.state.user" right <font-awesome-icon :icon="['fas', 'sign-out-alt']"/>
:toggle-class="$route.path.startsWith('/user/')?'active':''"> Abmelden
<template slot="button-content"> </b-dropdown-item>
<font-awesome-icon :icon="['fas', 'user']"/> </template>
<span v-if="$store.state.user"> {{$store.state.user.login_name}}</span> </b-nav-item-dropdown>
</template> <b-nav-item v-else to="/login">
<template v-if="$store.state.user"> <font-awesome-icon :icon="['fas', 'sign-in-alt']"/>
<b-dropdown-item to="/user/tokens" :active="$route.path === '/user/tokens'"> <span>Anmelden</span>
<font-awesome-icon :icon="['fas', 'code']"/> </b-nav-item>
API-Tokens </b-navbar-nav>
</b-dropdown-item> </b-collapse><!-- /.navbar-collapse -->
<b-dropdown-divider/> </b-navbar>
<b-dropdown-item @click="logout">
<font-awesome-icon :icon="['fas', 'sign-out-alt']"/>
Abmelden
</b-dropdown-item>
</template>
</b-nav-item-dropdown>
<b-nav-item v-else to="/login">
<font-awesome-icon :icon="['fas', 'sign-in-alt']"/>
<span>Anmelden</span>
</b-nav-item>
</b-navbar-nav>
</b-collapse><!-- /.navbar-collapse -->
</b-navbar>
</template> </template>
<script> <script>
import LoginService from '@/api-services/login.service' import LoginService from '@/api-services/login.service'
import GlobalSearch from "@/components/GlobalSearch";
export default { export default {
name: "Navbar", name: "Navbar",
data() { components: {GlobalSearch},
return { data() {
show_search: false, return {}
search_input: '' },
} methods: {
}, async logout() {
methods: { if (this.$store.token == null) {
async logout() { await LoginService.logout(null)
if(this.$store.token == null) { } else {
await LoginService.logout(null) await LoginService.logout(this.$store.state.token.pk || null)
} }
else { this.$store.commit('logout')
await LoginService.logout(this.$store.state.token.pk || null) await this.$router.push('/login')
}
this.$store.commit('logout')
await this.$router.push('/login')
},
searchAction(e) {
e.preventDefault()
if (this.search_input === '') {
this.show_search = !this.show_search
if (this.show_search) {
this.$nextTick(() => {
this.$refs.search.focus()
})
}
} else {
// TODO: actually search stuff
window.console.log('Search: ' + this.search_input)
this.search_input = ''
}
}
}
} }
</script> }
}
<style scoped> </script>
.slide-enter-active, \ No newline at end of file
.slide-leave-active {
transition: transform 250ms;
z-index: -10;
}
.slide-enter,
.slide-leave-to {
transform: translateX(120%);
}
.search-cover-wrapper {
position: relative;
width: 0;
height: 0;
}
.search-cover {
z-index: -1;
position: absolute;
left: -30px;
top: -25px;
width: 300px;
height: 50px;
background: #343A40;
}
.search {
max-width: 250px;
}
.search-button {
z-index: 1;
}
</style>
...@@ -33,6 +33,9 @@ import { ...@@ -33,6 +33,9 @@ import {
faSignOutAlt, faSignOutAlt,
faUser, faUser,
faEye, faEye,
faLink,
faQuestion,
faTimes
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { import {
...@@ -81,4 +84,7 @@ library.add( ...@@ -81,4 +84,7 @@ library.add(
faPaperPlane, faPaperPlane,
faOpenid, faOpenid,
faEye, faEye,
faLink,
faQuestion,
faTimes
) )
...@@ -46,7 +46,7 @@ export default new Router({ ...@@ -46,7 +46,7 @@ export default new Router({
component: () => import('./views/netdb/Tokens.vue'), component: () => import('./views/netdb/Tokens.vue'),
meta: { meta: {
resolveName: function () { resolveName: function () {
return "API-Tokens" return "Accounts & Tokens"
} }
} }
}, },
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment