More logos, move logo logic to model, add logos to cluster switcher

This commit is contained in:
Westly Wright 2020-12-11 15:08:20 -07:00
parent 150a081348
commit 787a442522
No known key found for this signature in database
GPG Key ID: 4FAB3D8673DC54A3
6 changed files with 155 additions and 74 deletions

View File

@ -262,6 +262,8 @@ catalog:
partner: Partner
rancher: Rancher
header: Deploy Chart
noCharts: 'There are no charts available, have you added any repos?'
noWindows: Your catalogs do not contain any charts capable of being deployed on a Windows cluster.
search: Filter
install:
action:
@ -307,8 +309,8 @@ catalog:
operation:
tableHeaders:
action: Action
releaseNamespace: Release Namespace
releaseName: Release Name
releaseNamespace: Release Namespace
repo:
action:
refresh: Refresh

View File

@ -2,13 +2,15 @@
import { MANAGEMENT } from '@/config/types';
import { sortBy } from '@/utils/sort';
import { findBy } from '@/utils/array';
import { mapState } from 'vuex';
import { mapGetters, mapState } from 'vuex';
import Select from '@/components/form/Select';
export default {
components: { Select },
computed: {
...mapState(['isMultiCluster']),
...mapGetters(['currentCluster']),
value: {
get() {
@ -36,9 +38,10 @@ export default {
const out = all.map((x) => {
return {
id: x.id,
label: x.nameDisplay,
ready: x.isReady,
id: x.id,
label: x.nameDisplay,
ready: x.isReady,
osLogo: x.providerOSLogo,
};
});
@ -66,8 +69,12 @@ export default {
:options="options"
>
<template #selected-option="opt">
<i class="icon icon-copy icon-lg pr-5" />
{{ opt.label }}
<span class="cluster-label-container">
<img class="cluster-switcher-os-logo" :src="currentCluster.providerOSLogo" />
<span class="cluster-label">
{{ opt.label }}
</span>
</span>
</template>
<template #no-options="{ search, searching }">
@ -78,15 +85,22 @@ export default {
</template>
<template #option="opt">
<b v-if="opt === value">{{ opt.label }}</b>
<nuxt-link
v-else-if="opt.ready"
class="cluster"
:to="{ name: 'c-cluster', params: { cluster: opt.id } }"
>
{{ opt.label }}
</nuxt-link>
<span v-else class="text-muted">{{ opt.label }}</span>
<span class="dropdown-option">
<span class="logo">
<img v-if="opt.osLogo" class="cluster-switcher-os-logo" :src="opt.osLogo" />
</span>
<span class="content">
<b v-if="opt === value">{{ opt.label }}</b>
<nuxt-link
v-else-if="opt.ready"
class="cluster"
:to="{ name: 'c-cluster', params: { cluster: opt.id } }"
>
{{ opt.label }}
</nuxt-link>
<span v-else class="text-muted">{{ opt.label }}</span>
</span>
</span>
</template>
</Select>
<button v-shortkey.once="['c']" class="hide" @shortkey="focus()" />
@ -94,6 +108,18 @@ export default {
</template>
<style lang="scss" scoped>
.filter {
.cluster-label-container {
width: 100%;
display: grid;
grid-template-columns: 15% 85%;
}
.cluster-switcher-os-logo {
height: 16px;
}
}
.filter ::v-deep .unlabeled-select .v-select {
background: rgba(0, 0, 0, 0.05);
border-radius: var(--border-radius);
@ -101,6 +127,20 @@ export default {
display: inline-block;
max-width: 100%;
.vs__selected {
width: 100%;
}
// matches the padding a layout of the option (and logo/content) to the selected option so it doesn't look off
.vs__dropdown-option {
padding: 3px 20px 3px 8px;
.dropdown-option {
display: grid;
width: 100%;
grid-template-columns: 35% fit-content(100%);
}
}
&.vs--disabled .vs__actions {
display: none;
}

View File

@ -52,6 +52,8 @@ export const CATALOG = {
SOURCE_REPO_NAME: 'catalog.cattle.io/ui-source-repo',
COLOR: 'catalog.cattle.io/ui-color',
DISPLAY_NAME: 'catalog.cattle.io/display-name',
SUPPORTED_OS: 'catalog.cattle.io/os',
};
export const FLEET = {

View File

@ -1,12 +1,19 @@
import { CATALOG } from '@/config/labels-annotations';
import { FLEET } from '@/config/types';
import { insertAt } from '@/utils/array';
import { insertAt, findBy } from '@/utils/array';
export const OS_LOGOS = [
{
id: 'linux',
logo: require(`~/assets/images/logo-linux.svg`)
},
{
id: 'windows',
logo: require(`~/assets/images/logo-windows.svg`)
},
];
export default {
scope() {
return this.id === 'local' ? CATALOG._MANAGEMENT : CATALOG._DOWNSTREAM;
},
_availableActions() {
const out = this._standardActions;
@ -20,6 +27,33 @@ export default {
return out;
},
canDelete() {
return this.hasLink('remove') && !this?.spec?.internal;
},
configName() {
const allKeys = Object.keys(this.spec);
const configKey = allKeys.find( kee => kee.includes('Config'));
return configKey;
},
groupByLabel() {
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.notInAWorkspace');
},
isReady() {
return this.hasCondition('Ready');
},
kubernetesVersion() {
if ( this?.status?.version?.gitVersion ) {
return this.status.version.gitVersion;
} else {
return this.$rootGetters['i18n/t']('generic.unknown');
}
},
openShell() {
return () => {
this.$dispatch('wm/open', {
@ -35,31 +69,23 @@ export default {
};
},
isReady() {
return this.hasCondition('Ready');
},
providerOSLogo() {
const providerOSOptions = OS_LOGOS;
const provider = this.status.provider || '';
let match = findBy(providerOSOptions, 'id', 'linux');
let { logo } = match;
configName() {
const allKeys = Object.keys(this.spec);
const configKey = allKeys.find( kee => kee.includes('Config'));
if (provider === 'rke.windows') {
match = findBy(providerOSOptions, 'id', 'windows');
return configKey;
},
kubernetesVersion() {
if ( this?.status?.version?.gitVersion ) {
return this.status.version.gitVersion;
} else {
return this.$rootGetters['i18n/t']('generic.unknown');
logo = match.logo;
}
return logo;
},
canDelete() {
return this.hasLink('remove') && !this?.spec?.internal;
},
groupByLabel() {
return this.$rootGetters['i18n/t']('resourceTable.groupLabel.notInAWorkspace');
scope() {
return this.id === 'local' ? CATALOG._MANAGEMENT : CATALOG._DOWNSTREAM;
},
setClusterNameLabel() {

View File

@ -13,6 +13,7 @@ import Select from '@/components/form/Select';
import LazyImage from '@/components/LazyImage';
import { mapPref, HIDE_REPOS } from '@/store/prefs';
import { removeObject, addObject, findBy } from '@/utils/array';
import { CATALOG } from '@/config/labels-annotations';
export default {
components: {
@ -38,19 +39,33 @@ export default {
data() {
return {
searchQuery: null,
showDeprecated: null,
showHidden: null,
category: null,
allRepos: null,
allRepos: null,
catalogOSAnnotation: CATALOG.OS,
category: null,
searchQuery: null,
showDeprecated: null,
showHidden: null,
};
},
computed: {
...mapGetters(['currentCluster']),
...mapGetters({ allCharts: 'catalog/charts', loadingErrors: 'catalog/errors' }),
hideRepos: mapPref(HIDE_REPOS),
showWindowsClusterNoAppsSplash() {
const clusterProvider = this.currentCluster.status.provider || 'other';
const { filteredCharts } = this;
let showSplash = false;
if (clusterProvider === 'rke.windows' && filteredCharts.length === 0) {
showSplash = true;
}
return showSplash;
},
repoOptions() {
let nextColor = 1;
@ -97,7 +112,15 @@ export default {
},
filteredCharts() {
const clusterProvider = this.currentCluster.status.provider || 'other';
return (this.enabledCharts || []).filter((c) => {
const osAnnotation = c.annotations[this.catalogOSAnnotation];
if (clusterProvider === 'rke.windows' && ((osAnnotation && osAnnotation !== 'windows') || !osAnnotation)) {
return false;
}
if ( this.category && !c.categories.includes(this.category) ) {
return false;
}
@ -313,7 +336,19 @@ export default {
<div v-if="allCharts.length">
<div class="charts">
<div v-for="c in arrangedCharts" :key="c.key" class="chart" :class="{[colorForChart(c)]: true}" @click="selectChart(c)">
<div v-if="arrangedCharts.length === 0 && showWindowsClusterNoAppsSplash" style="width: 100%;">
<div class="m-50 text-center">
<h1>{{ t('catalog.charts.noWindows') }}</h1>
</div>
</div>
<div
v-for="c in arrangedCharts"
v-else
:key="c.key"
class="chart"
:class="{[colorForChart(c)]: true}"
@click="selectChart(c)"
>
<div class="side-label">
<label v-if="c.sideLabel">{{ c.sideLabel }}</label>
</div>
@ -330,7 +365,7 @@ export default {
</div>
</div>
<div v-else class="m-50 text-center">
<h1>There are no charts available, have you added any repos?</h1>
<h1>{{ t('catalog.charts.noCharts') }}</h1>
</div>
</div>
</template>

View File

@ -29,6 +29,7 @@ import CountGauge from '@/components/CountGauge';
import Glance from '@/components/Glance';
import LazyImage from '@/components/LazyImage';
import { findBy } from '@/utils/array';
import { OS_LOGOS } from '@/models/management.cattle.io.cluster';
import HardwareResourceGauge from './HardwareResourceGauge';
const PARSE_RULES = {
@ -50,16 +51,6 @@ const METRICS_POLL_RATE_MS = 30000;
const MAX_FAILURES = 2;
const RESOURCES = [NAMESPACE, INGRESS, PV, WORKLOAD_TYPES.DEPLOYMENT, WORKLOAD_TYPES.STATEFUL_SET, WORKLOAD_TYPES.JOB, WORKLOAD_TYPES.DAEMON_SET, SERVICE];
const PROVIDER_OS_OPTIONS = [
{
id: 'linux',
logo: require(`~/assets/images/logo-linux.svg`)
},
{
id: 'windows',
logo: require(`~/assets/images/logo-windows.svg`)
},
];
export default {
components: {
@ -141,7 +132,6 @@ export default {
nodePools: [],
nodeTemplates: [],
nodes: [],
providerOSOptions: PROVIDER_OS_OPTIONS,
};
},
@ -163,20 +153,6 @@ export default {
return this.t(`cluster.provider.${ provider }`);
},
providerOSLogo() {
const { currentCluster, providerOSOptions } = this;
const provider = currentCluster.status.provider || '';
let match = findBy(providerOSOptions, 'id', 'linux');
let logo = match.logo;
if (provider === 'rke.windows') {
match = findBy(providerOSOptions, 'id', 'windows');
logo = match.logo;
}
return logo;
},
accessibleResources() {
return RESOURCES.filter(resource => this.$store.getters['cluster/schemaFor'](resource));
},
@ -368,7 +344,7 @@ export default {
<label>{{ t('glance.provider') }}</label>
</div>
<div class="logo">
<LazyImage class="os-provider-logo" :initial-src="findBy(providerOSOptions, 'id', 'linux').logo" :src="providerOSLogo" />
<LazyImage class="os-provider-logo" :src="currentCluster.providerOSLogo" />
</div>
</template>
<template #kubernetesVersion>