mirror of https://github.com/rancher/dashboard.git
350 lines
7.8 KiB
Vue
350 lines
7.8 KiB
Vue
<script>
|
|
import debounce from 'lodash/debounce';
|
|
import { mapState, mapGetters } from 'vuex';
|
|
import { mapPref, DEV, EXPANDED_GROUPS, FAVORITE_TYPES } from '@/store/prefs';
|
|
import ActionMenu from '@/components/ActionMenu';
|
|
import Jump from '@/components/nav/Jump';
|
|
import WindowManager from '@/components/nav/WindowManager';
|
|
import PromptRemove from '@/components/PromptRemove';
|
|
import Group from '@/components/nav/Group';
|
|
import Header from '@/components/nav/Header';
|
|
import Footer from '@/components/nav/Footer';
|
|
import { COUNT, SCHEMA, MANAGEMENT } from '@/config/types';
|
|
import { BASIC, FAVORITE, USED } from '@/store/type-map';
|
|
import { addObjects, replaceWith, clear } from '@/utils/array';
|
|
import { NAME as EXPLORER } from '@/config/product/explorer';
|
|
import isEqual from 'lodash/isEqual';
|
|
|
|
export default {
|
|
|
|
components: {
|
|
Jump,
|
|
PromptRemove,
|
|
Header,
|
|
Footer,
|
|
ActionMenu,
|
|
Group,
|
|
WindowManager
|
|
},
|
|
|
|
data() {
|
|
return { groups: [] };
|
|
},
|
|
|
|
middleware: ['authenticated'],
|
|
|
|
computed: {
|
|
...mapState(['managementReady', 'clusterReady']),
|
|
...mapGetters(['productId']),
|
|
...mapGetters({ locale: 'i18n/selectedLocaleLabel' }),
|
|
|
|
namespaces() {
|
|
return this.$store.getters['namespaces']();
|
|
},
|
|
|
|
dev: mapPref(DEV),
|
|
expandedGroups: mapPref(EXPANDED_GROUPS),
|
|
favoriteTypes: mapPref(FAVORITE_TYPES),
|
|
|
|
allSchemas() {
|
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
|
|
return this.$store.getters[`${ inStore }/all`](SCHEMA);
|
|
},
|
|
|
|
counts() {
|
|
const inStore = this.$store.getters['currentProduct'].inStore;
|
|
|
|
// So that there's something to watch for updates
|
|
if ( this.$store.getters[`${ inStore }/haveAll`](COUNT) ) {
|
|
const counts = this.$store.getters[`${ inStore }/all`](COUNT)[0].counts;
|
|
|
|
return counts;
|
|
}
|
|
|
|
return {};
|
|
},
|
|
|
|
showJump() {
|
|
return this.productId === EXPLORER;
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
counts() {
|
|
this.queueUpdate();
|
|
},
|
|
|
|
allSchemas() {
|
|
this.queueUpdate();
|
|
},
|
|
|
|
favoriteTypes() {
|
|
this.queueUpdate();
|
|
},
|
|
|
|
locale(a, b) {
|
|
if ( !isEqual(a, b) ) {
|
|
this.getGroups();
|
|
}
|
|
},
|
|
|
|
productId(a, b) {
|
|
if ( !isEqual(a, b) ) {
|
|
// Immediately update because you'll see it come in later
|
|
this.getGroups();
|
|
}
|
|
},
|
|
|
|
namespaces(a, b) {
|
|
if ( !isEqual(a, b) ) {
|
|
// Immediately update because you'll see it come in later
|
|
this.getGroups();
|
|
}
|
|
},
|
|
|
|
clusterReady(a, b) {
|
|
if ( !isEqual(a, b) ) {
|
|
// Immediately update because you'll see it come in later
|
|
this.getGroups();
|
|
}
|
|
},
|
|
|
|
product(a, b) {
|
|
if ( !isEqual(a, b) ) {
|
|
// Immediately update because you'll see it come in later
|
|
this.getGroups();
|
|
}
|
|
},
|
|
},
|
|
|
|
created() {
|
|
this.queueUpdate = debounce(this.getGroups, 500);
|
|
|
|
this.getGroups();
|
|
},
|
|
|
|
methods: {
|
|
getGroups() {
|
|
if ( !this.clusterReady ) {
|
|
clear(this.groups);
|
|
|
|
return;
|
|
}
|
|
|
|
const clusterId = this.$store.getters['clusterId'];
|
|
const productId = this.$store.getters['productId'];
|
|
const currentType = this.$route.params.resource || '';
|
|
let namespaces = null;
|
|
|
|
if ( !this.$store.getters['isAllNamespaces'] ) {
|
|
namespaces = Object.keys(this.namespaces);
|
|
}
|
|
|
|
const namespaceMode = this.$store.getters['namespaceMode'];
|
|
const out = [];
|
|
const modes = [BASIC];
|
|
|
|
if ( productId === EXPLORER ) {
|
|
modes.push(FAVORITE);
|
|
modes.push(USED);
|
|
}
|
|
for ( const mode of modes ) {
|
|
const types = this.$store.getters['type-map/allTypes'](productId, mode) || {};
|
|
const more = this.$store.getters['type-map/getTree'](productId, mode, types, clusterId, namespaceMode, namespaces, currentType);
|
|
|
|
addObjects(out, more);
|
|
}
|
|
|
|
replaceWith(this.groups, ...out);
|
|
},
|
|
|
|
expanded(name) {
|
|
const currentType = this.$route.params.resource || '';
|
|
|
|
return this.expandedGroups.includes(name) || name === currentType;
|
|
},
|
|
|
|
toggleNoneLocale() {
|
|
this.$store.dispatch('i18n/toggleNone');
|
|
},
|
|
|
|
toggleTheme() {
|
|
this.$store.dispatch('prefs/toggleTheme');
|
|
},
|
|
|
|
wheresMyDebugger() {
|
|
// vue-shortkey is preventing F8 from passing through to the browser... this works for now.
|
|
// eslint-disable-next-line no-debugger
|
|
debugger;
|
|
},
|
|
|
|
async toggleShell() {
|
|
const clusterId = this.$route.params.cluster;
|
|
|
|
if ( !clusterId || !this.$store.getters['isMultiCluster'] ) {
|
|
return;
|
|
}
|
|
|
|
const cluster = await this.$store.dispatch('management/find', {
|
|
type: MANAGEMENT.CLUSTER,
|
|
id: clusterId,
|
|
});
|
|
|
|
if (!cluster ) {
|
|
return;
|
|
}
|
|
|
|
cluster.openShell();
|
|
}
|
|
},
|
|
|
|
head() {
|
|
const theme = this.$store.getters['prefs/theme'];
|
|
|
|
return {
|
|
bodyAttrs: { class: `theme-${ theme } overflow-hidden dashboard-body` },
|
|
title: this.$store.getters['i18n/t']('nav.title'),
|
|
};
|
|
},
|
|
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="managementReady" class="dashboard-root">
|
|
<Header />
|
|
|
|
<nav v-if="clusterReady">
|
|
<Jump v-if="showJump" class="m-10" />
|
|
<div v-else class="mb-20" />
|
|
<template v-for="(g, idx) in groups">
|
|
<Group
|
|
:key="idx"
|
|
id-prefix=""
|
|
class="package"
|
|
:expanded="expanded"
|
|
:group="g"
|
|
:can-collapse="!g.isRoot"
|
|
:show-header="!g.isRoot"
|
|
>
|
|
<template #header>
|
|
<h6>{{ g.label }}</h6>
|
|
</template>
|
|
</Group>
|
|
</template>
|
|
</nav>
|
|
|
|
<main v-if="clusterReady">
|
|
<nuxt class="outlet" />
|
|
<Footer />
|
|
|
|
<ActionMenu />
|
|
<PromptRemove />
|
|
<button v-if="dev" v-shortkey.once="['shift','l']" class="hide" @shortkey="toggleNoneLocale()" />
|
|
<button v-if="dev" v-shortkey.once="['shift','t']" class="hide" @shortkey="toggleTheme()" />
|
|
<button v-shortkey.once="['f8']" class="hide" @shortkey="wheresMyDebugger()" />
|
|
<button v-shortkey.once="['`']" class="hide" @shortkey="toggleShell" />
|
|
</main>
|
|
|
|
<div class="wm">
|
|
<WindowManager />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.dashboard-root {
|
|
display: grid;
|
|
height: 100vh;
|
|
|
|
grid-template-areas:
|
|
"header header"
|
|
"nav main"
|
|
"wm wm";
|
|
|
|
grid-template-columns: var(--nav-width) auto;
|
|
grid-template-rows: var(--header-height) auto var(--wm-height, 0px);
|
|
|
|
> HEADER {
|
|
grid-area: header;
|
|
}
|
|
|
|
NAV {
|
|
grid-area: nav;
|
|
position: relative;
|
|
background-color: var(--nav-bg);
|
|
overflow-y: auto;
|
|
|
|
.package.depth-0 {
|
|
.root .label {
|
|
}
|
|
|
|
&.expanded > .body {
|
|
margin-bottom: 5px;
|
|
}
|
|
}
|
|
|
|
.header {
|
|
background: transparent;
|
|
padding-left: 10px;
|
|
}
|
|
|
|
H6, .root.child .label {
|
|
margin: 0;
|
|
letter-spacing: 0.1em;
|
|
line-height: initial;
|
|
|
|
A { padding-left: 0; }
|
|
}
|
|
}
|
|
}
|
|
|
|
MAIN {
|
|
grid-area: main;
|
|
overflow: auto;
|
|
|
|
.outlet {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 20px 20px 70px 20px;
|
|
min-height: 100%;
|
|
margin-bottom: calc(-1 * var(--footer-height) - 1px);
|
|
}
|
|
|
|
FOOTER {
|
|
background-color: var(--nav-bg);
|
|
height: var(--footer-height);
|
|
}
|
|
|
|
HEADER {
|
|
display: grid;
|
|
grid-template-areas: "title actions"
|
|
"state-banner state-banner";
|
|
grid-template-columns: "auto min-content";
|
|
margin-bottom: 20px;
|
|
align-items: center;
|
|
|
|
H1 {
|
|
grid-area: title;
|
|
margin: 0;
|
|
}
|
|
|
|
.state-banner {
|
|
grid-area: state-banner;
|
|
}
|
|
|
|
.actions {
|
|
grid-area: actions;
|
|
text-align: right;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
.wm {
|
|
grid-area: wm;
|
|
overflow-y: hidden;
|
|
}
|
|
</style>
|