mirror of https://github.com/rancher/dashboard.git
865 lines
27 KiB
Vue
865 lines
27 KiB
Vue
<script lang="ts">
|
|
import { defineComponent } from 'vue';
|
|
import { mapPref, AFTER_LOGIN_ROUTE, READ_WHATS_NEW, HIDE_HOME_PAGE_CARDS } from '@shell/store/prefs';
|
|
import { Banner } from '@components/Banner';
|
|
import BannerGraphic from '@shell/components/BannerGraphic.vue';
|
|
import IndentedPanel from '@shell/components/IndentedPanel.vue';
|
|
import PaginatedResourceTable from '@shell/components/PaginatedResourceTable.vue';
|
|
import { BadgeState } from '@components/BadgeState';
|
|
import CommunityLinks from '@shell/components/CommunityLinks.vue';
|
|
import SingleClusterInfo from '@shell/components/SingleClusterInfo.vue';
|
|
import { mapGetters, mapState } from 'vuex';
|
|
import { MANAGEMENT, CAPI } from '@shell/config/types';
|
|
import { NAME as MANAGER } from '@shell/config/product/manager';
|
|
import { STATE } from '@shell/config/table-headers';
|
|
import { MODE, _IMPORT } from '@shell/config/query-params';
|
|
import { createMemoryFormat, formatSi, parseSi, createMemoryValues } from '@shell/utils/units';
|
|
import { getVersionInfo, readReleaseNotes, markReadReleaseNotes, markSeenReleaseNotes } from '@shell/utils/version';
|
|
import PageHeaderActions from '@shell/mixins/page-actions';
|
|
import { getVendor } from '@shell/config/private-label';
|
|
import { mapFeature, MULTI_CLUSTER } from '@shell/store/features';
|
|
import { BLANK_CLUSTER } from '@shell/store/store-types.js';
|
|
import { filterHiddenLocalCluster, filterOnlyKubernetesClusters, paginationFilterClusters } from '@shell/utils/cluster';
|
|
import TabTitle from '@shell/components/TabTitle.vue';
|
|
import { ActionFindPageArgs } from '@shell/types/store/dashboard-store.types';
|
|
|
|
import { RESET_CARDS_ACTION, SET_LOGIN_ACTION, SHOW_HIDE_BANNER_ACTION } from '@shell/config/page-actions';
|
|
import { STEVE_NAME_COL, STEVE_STATE_COL } from '@shell/config/pagination-table-headers';
|
|
import { PaginationParamFilter, FilterArgs, PaginationFilterField, PaginationArgs } from '@shell/types/store/pagination.types';
|
|
import ProvCluster from '@shell/models/provisioning.cattle.io.cluster';
|
|
import { sameContents } from '@shell/utils/array';
|
|
import { PagTableFetchPageSecondaryResourcesOpts, PagTableFetchSecondaryResourcesOpts, PagTableFetchSecondaryResourcesReturns } from '@shell/types/components/paginatedResourceTable';
|
|
import { CURRENT_RANCHER_VERSION } from '@shell/config/version';
|
|
import { CAPI as CAPI_LAB_AND_ANO } from '@shell/config/labels-annotations';
|
|
|
|
export default defineComponent({
|
|
name: 'Home',
|
|
layout: 'home',
|
|
components: {
|
|
Banner,
|
|
BannerGraphic,
|
|
IndentedPanel,
|
|
PaginatedResourceTable,
|
|
BadgeState,
|
|
CommunityLinks,
|
|
SingleClusterInfo,
|
|
TabTitle,
|
|
},
|
|
|
|
mixins: [PageHeaderActions],
|
|
|
|
data() {
|
|
const options = this.$store.getters[`type-map/optionsFor`](CAPI.RANCHER_CLUSTER)?.custom || {};
|
|
const params = {
|
|
product: MANAGER,
|
|
cluster: BLANK_CLUSTER,
|
|
resource: CAPI.RANCHER_CLUSTER
|
|
};
|
|
const defaultCreateLocation = {
|
|
name: 'c-cluster-product-resource-create',
|
|
params,
|
|
};
|
|
const defaultImportLocation = {
|
|
...defaultCreateLocation,
|
|
query: { [MODE]: _IMPORT }
|
|
};
|
|
|
|
return {
|
|
HIDE_HOME_PAGE_CARDS,
|
|
fullVersion: getVersionInfo(this.$store).fullVersion,
|
|
// Page actions don't change on the Home Page
|
|
pageActions: [
|
|
{
|
|
label: this.t('nav.header.setLoginPage'),
|
|
action: SET_LOGIN_ACTION
|
|
},
|
|
{ divider: true },
|
|
{
|
|
label: this.t('nav.header.showHideBanner'),
|
|
action: SHOW_HIDE_BANNER_ACTION
|
|
},
|
|
{
|
|
label: this.t('nav.header.restoreCards'),
|
|
action: RESET_CARDS_ACTION
|
|
},
|
|
],
|
|
vendor: getVendor(),
|
|
|
|
provClusterSchema: this.$store.getters['management/schemaFor'](CAPI.RANCHER_CLUSTER),
|
|
|
|
canViewMgmtClusters: !!this.$store.getters['management/schemaFor'](MANAGEMENT.CLUSTER),
|
|
canViewMachine: !!this.$store.getters['management/canList'](CAPI.MACHINE),
|
|
canViewMgmtNodes: !!this.$store.getters['management/canList'](MANAGEMENT.NODE),
|
|
canViewMgmtPools: !!this.$store.getters['management/canList'](MANAGEMENT.NODE_POOL),
|
|
canViewMgmtTemplates: !!this.$store.getters['management/canList'](MANAGEMENT.NODE_TEMPLATE),
|
|
|
|
manageLocation: {
|
|
name: 'c-cluster-product-resource',
|
|
params: {
|
|
product: MANAGER,
|
|
cluster: BLANK_CLUSTER,
|
|
resource: CAPI.RANCHER_CLUSTER
|
|
},
|
|
},
|
|
|
|
createLocation: options.createLocation ? options.createLocation(params) : defaultCreateLocation,
|
|
|
|
importLocation: options.importLocation ? options.importLocation(params) : defaultImportLocation,
|
|
|
|
headers: [
|
|
STATE,
|
|
{
|
|
name: 'name',
|
|
labelKey: 'tableHeaders.name',
|
|
value: 'nameDisplay',
|
|
sort: ['nameSort'],
|
|
canBeVariable: true,
|
|
getValue: (row: ProvCluster) => row.mgmt?.nameDisplay
|
|
},
|
|
{
|
|
label: this.t('landing.clusters.provider'),
|
|
subLabel: this.t('landing.clusters.distro'),
|
|
value: 'mgmt.status.provider',
|
|
name: 'Provider',
|
|
sort: ['mgmt.status.provider'],
|
|
formatter: 'ClusterProvider'
|
|
},
|
|
{
|
|
label: this.t('landing.clusters.kubernetesVersion'),
|
|
subLabel: this.t('landing.clusters.architecture'),
|
|
name: 'kubernetesVersion',
|
|
},
|
|
{
|
|
label: this.t('tableHeaders.cpu'),
|
|
value: '',
|
|
name: 'cpu',
|
|
sort: ['status.allocatable.cpu', 'status.available.cpu']
|
|
},
|
|
{
|
|
label: this.t('tableHeaders.memory'),
|
|
value: '',
|
|
name: 'memory',
|
|
sort: ['status.allocatable.memory', 'status.available.memory']
|
|
},
|
|
{
|
|
label: this.t('tableHeaders.pods'),
|
|
name: 'pods',
|
|
value: '',
|
|
sort: ['status.allocatable.pods', 'status.requested.pods'],
|
|
formatter: 'PodsUsage',
|
|
delayLoading: true
|
|
},
|
|
// {
|
|
// name: 'explorer',
|
|
// label: this.t('landing.clusters.explorer')
|
|
// }
|
|
],
|
|
|
|
paginationHeaders: [
|
|
STEVE_STATE_COL,
|
|
{
|
|
...STEVE_NAME_COL,
|
|
canBeVariable: true,
|
|
value: `metadata.annotations[${ CAPI_LAB_AND_ANO.HUMAN_NAME }]`,
|
|
sort: [`metadata.annotations[${ CAPI_LAB_AND_ANO.HUMAN_NAME }]`],
|
|
search: `metadata.annotations[${ CAPI_LAB_AND_ANO.HUMAN_NAME }]`,
|
|
},
|
|
{
|
|
label: this.t('landing.clusters.provider'),
|
|
subLabel: this.t('landing.clusters.distro'),
|
|
value: 'mgmt.status.provider',
|
|
name: 'Provider',
|
|
sort: false,
|
|
search: false,
|
|
formatter: 'ClusterProvider'
|
|
},
|
|
{
|
|
label: this.t('landing.clusters.kubernetesVersion'),
|
|
subLabel: this.t('landing.clusters.architecture'),
|
|
name: 'kubernetesVersion',
|
|
sort: false,
|
|
search: false,
|
|
},
|
|
{
|
|
label: this.t('tableHeaders.cpu'),
|
|
value: '',
|
|
name: 'cpu',
|
|
sort: false,
|
|
search: false,
|
|
},
|
|
{
|
|
label: this.t('tableHeaders.memory'),
|
|
value: '',
|
|
name: 'memory',
|
|
sort: false,
|
|
search: false,
|
|
},
|
|
{
|
|
label: this.t('tableHeaders.pods'),
|
|
name: 'pods',
|
|
value: '',
|
|
sort: false,
|
|
search: false,
|
|
formatter: 'PodsUsage',
|
|
delayLoading: true
|
|
},
|
|
],
|
|
|
|
clusterCount: 0,
|
|
|
|
CURRENT_RANCHER_VERSION
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapState(['managementReady']),
|
|
...mapGetters(['currentCluster', 'defaultClusterId', 'releaseNotesUrl']),
|
|
mcm: mapFeature(MULTI_CLUSTER),
|
|
|
|
canCreateCluster() {
|
|
return !!this.provClusterSchema?.collectionMethods.find((x: string) => x.toLowerCase() === 'post');
|
|
},
|
|
|
|
afterLoginRoute: mapPref(AFTER_LOGIN_ROUTE),
|
|
homePageCards: mapPref(HIDE_HOME_PAGE_CARDS),
|
|
|
|
readWhatsNewAlready() {
|
|
return readReleaseNotes(this.$store);
|
|
},
|
|
|
|
showSetLoginBanner() {
|
|
return this.homePageCards?.setLoginPage;
|
|
},
|
|
},
|
|
|
|
async created() {
|
|
// Update last visited on load
|
|
await this.$store.dispatch('prefs/setLastVisited', { name: 'home' });
|
|
markSeenReleaseNotes(this.$store);
|
|
},
|
|
|
|
// Forget the types when we leave the page
|
|
beforeUnmount() {
|
|
this.$store.dispatch('management/forgetType', CAPI.MACHINE);
|
|
this.$store.dispatch('management/forgetType', MANAGEMENT.NODE);
|
|
this.$store.dispatch('management/forgetType', MANAGEMENT.NODE_POOL);
|
|
this.$store.dispatch('management/forgetType', MANAGEMENT.NODE_TEMPLATE);
|
|
},
|
|
|
|
methods: {
|
|
/**
|
|
* Of type PagTableFetchSecondaryResources
|
|
*/
|
|
fetchSecondaryResources(opts: PagTableFetchSecondaryResourcesOpts): PagTableFetchSecondaryResourcesReturns {
|
|
if (opts.canPaginate) {
|
|
return Promise.resolve({});
|
|
}
|
|
|
|
if ( this.canViewMgmtClusters ) {
|
|
this.$store.dispatch('management/findAll', { type: MANAGEMENT.CLUSTER });
|
|
}
|
|
|
|
if ( this.canViewMachine ) {
|
|
this.$store.dispatch('management/findAll', { type: CAPI.MACHINE });
|
|
}
|
|
|
|
if ( this.canViewMgmtNodes ) {
|
|
this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE });
|
|
}
|
|
|
|
// We need to fetch node pools and node templates in order to correctly show the provider for RKE1 clusters
|
|
if ( this.canViewMgmtPools ) {
|
|
this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_POOL });
|
|
}
|
|
|
|
if ( this.canViewMgmtTemplates ) {
|
|
this.$store.dispatch('management/findAll', { type: MANAGEMENT.NODE_TEMPLATE });
|
|
}
|
|
|
|
return Promise.resolve({});
|
|
},
|
|
|
|
async fetchPageSecondaryResources({
|
|
canPaginate, force, page, pagResult
|
|
}: PagTableFetchPageSecondaryResourcesOpts) {
|
|
if (!canPaginate || !page?.length) {
|
|
this.clusterCount = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
this.clusterCount = pagResult.count;
|
|
|
|
if ( this.canViewMgmtClusters ) {
|
|
const opt: ActionFindPageArgs = {
|
|
force,
|
|
pagination: new FilterArgs({
|
|
filters: PaginationParamFilter.createMultipleFields(page.map((r: any) => new PaginationFilterField({
|
|
field: 'id',
|
|
value: r.mgmtClusterId
|
|
}))),
|
|
})
|
|
};
|
|
|
|
this.$store.dispatch(`management/findPage`, { type: MANAGEMENT.CLUSTER, opt });
|
|
}
|
|
|
|
if ( this.canViewMachine ) {
|
|
const opt: ActionFindPageArgs = {
|
|
force,
|
|
pagination: new FilterArgs({
|
|
filters: PaginationParamFilter.createMultipleFields(page.map((r: any) => new PaginationFilterField({
|
|
field: 'spec.clusterName',
|
|
value: r.metadata.name
|
|
}))),
|
|
})
|
|
};
|
|
|
|
await this.$store.dispatch(`management/findPage`, { type: CAPI.MACHINE, opt });
|
|
}
|
|
|
|
if ( this.canViewMgmtNodes ) {
|
|
const opt: ActionFindPageArgs = {
|
|
force,
|
|
pagination: new FilterArgs({
|
|
filters: PaginationParamFilter.createMultipleFields(page.map((r: any) => new PaginationFilterField({
|
|
field: 'id',
|
|
value: r.mgmtClusterId,
|
|
exact: false,
|
|
}))),
|
|
})
|
|
};
|
|
|
|
this.$store.dispatch(`management/findPage`, { type: MANAGEMENT.NODE, opt });
|
|
}
|
|
|
|
// We need to fetch node pools and node templates in order to correctly show the provider for RKE1 clusters
|
|
if ( this.canViewMgmtPools && this.canViewMgmtTemplates ) {
|
|
const nodePoolFilters = PaginationParamFilter.createMultipleFields(page
|
|
.filter((p: any) => p.status?.clusterName)
|
|
.map((r: any) => new PaginationFilterField({
|
|
field: 'spec.clusterName',
|
|
value: r.status?.clusterName
|
|
})));
|
|
const nodePools = await this.$store.dispatch(`management/findPage`, {
|
|
type: MANAGEMENT.NODE_POOL,
|
|
opt: {
|
|
force,
|
|
pagination: new FilterArgs({ filters: nodePoolFilters })
|
|
}
|
|
});
|
|
|
|
const templateOpt = PaginationParamFilter.createMultipleFields(nodePools
|
|
.filter((np: any) => !!np.nodeTemplateId)
|
|
.map((np: any) => new PaginationFilterField({
|
|
field: 'id',
|
|
value: np.nodeTemplateId,
|
|
exact: true,
|
|
})));
|
|
|
|
this.$store.dispatch(`management/findPage`, {
|
|
type: MANAGEMENT.NODE_TEMPLATE,
|
|
opt: {
|
|
force,
|
|
pagination: new FilterArgs({ filters: templateOpt })
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Define actions for each navigation link
|
|
* @param {*} action
|
|
*/
|
|
handlePageAction(action: any) {
|
|
switch (action.action) {
|
|
case RESET_CARDS_ACTION:
|
|
this.resetCards();
|
|
break;
|
|
|
|
case SHOW_HIDE_BANNER_ACTION:
|
|
this.toggleBanner();
|
|
break;
|
|
|
|
case SET_LOGIN_ACTION:
|
|
this.afterLoginRoute = 'home';
|
|
break;
|
|
|
|
// no default
|
|
}
|
|
},
|
|
|
|
cpuUsed(cluster: any) {
|
|
return parseSi(cluster.status.requested?.cpu);
|
|
},
|
|
|
|
cpuAllocatable(cluster: any) {
|
|
return parseSi(cluster.status.allocatable?.cpu);
|
|
},
|
|
|
|
memoryAllocatable(cluster: any) {
|
|
const parsedAllocatable = (parseSi(cluster.status.allocatable?.memory) || 0).toString();
|
|
const format = createMemoryFormat(parsedAllocatable);
|
|
|
|
return formatSi(parsedAllocatable, format);
|
|
},
|
|
|
|
memoryReserved(cluster: any) {
|
|
const memValues = createMemoryValues(cluster?.status?.allocatable?.memory, cluster?.status?.requested?.memory);
|
|
|
|
return `${ memValues.useful }/${ memValues.total } ${ memValues.units }`;
|
|
},
|
|
|
|
showWhatsNew() {
|
|
// Update the value, so that the message goes away
|
|
markReadReleaseNotes(this.$store);
|
|
},
|
|
|
|
showUserPrefs() {
|
|
this.$router.push({ name: 'prefs' });
|
|
},
|
|
|
|
async resetCards() {
|
|
const value = this.$store.getters['prefs/get'](HIDE_HOME_PAGE_CARDS) || {};
|
|
|
|
delete value.setLoginPage;
|
|
|
|
await this.$store.dispatch('prefs/set', { key: HIDE_HOME_PAGE_CARDS, value });
|
|
|
|
await this.$store.dispatch('prefs/set', { key: READ_WHATS_NEW, value: '' });
|
|
},
|
|
|
|
async toggleBanner() {
|
|
const value = this.$store.getters['prefs/get'](HIDE_HOME_PAGE_CARDS) || {};
|
|
|
|
if (value.welcomeBanner) {
|
|
delete value.welcomeBanner;
|
|
} else {
|
|
value.welcomeBanner = true;
|
|
}
|
|
|
|
await this.$store.dispatch('prefs/set', { key: HIDE_HOME_PAGE_CARDS, value });
|
|
},
|
|
|
|
async closeSetLoginBanner(retry = 0) {
|
|
let value = this.$store.getters['prefs/get'](HIDE_HOME_PAGE_CARDS);
|
|
|
|
if (value === true || value === false || value.length > 0) {
|
|
value = {};
|
|
}
|
|
value.setLoginPage = true;
|
|
|
|
const res = await this.$store.dispatch('prefs/set', { key: HIDE_HOME_PAGE_CARDS, value });
|
|
|
|
if (retry === 0 && res?.type === 'error' && res?.status === 500) {
|
|
await this.closeSetLoginBanner(retry + 1);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Filter out hidden clusters from list of all clusters
|
|
*/
|
|
filterRowsLocal(rows: any[]) {
|
|
return filterHiddenLocalCluster(filterOnlyKubernetesClusters(rows || [], this.$store), this.$store);
|
|
},
|
|
|
|
/**
|
|
* Filter out hidden clusters via api
|
|
*/
|
|
filterRowsApi(pagination: PaginationArgs): PaginationArgs {
|
|
if (!pagination.filters) {
|
|
pagination.filters = [];
|
|
}
|
|
|
|
const existingFilters = pagination.filters;
|
|
const requiredFilters = paginationFilterClusters(this.$store, false);
|
|
|
|
for (let i = 0; i < requiredFilters.length; i++) {
|
|
let found = false;
|
|
const required = requiredFilters[i];
|
|
|
|
for (let j = 0; j < existingFilters.length; j++) {
|
|
const existing = existingFilters[j];
|
|
|
|
if (
|
|
required.fields.length === existing.fields.length &&
|
|
sameContents(required.fields.map((e) => e.field), existing.fields.map((e) => e.field))
|
|
) {
|
|
Object.assign(existing, required);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
pagination.filters.push(required);
|
|
}
|
|
}
|
|
|
|
return pagination;
|
|
}
|
|
}
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
v-if="managementReady"
|
|
class="home-page"
|
|
>
|
|
<TabTitle
|
|
:show-child="false"
|
|
:breadcrumb="false"
|
|
>
|
|
{{ `${vendor} - ${t('landing.homepage')}` }}
|
|
</TabTitle>
|
|
<BannerGraphic
|
|
:small="true"
|
|
:title="t('landing.welcomeToRancher', {vendor})"
|
|
:pref="HIDE_HOME_PAGE_CARDS"
|
|
pref-key="welcomeBanner"
|
|
data-testid="home-banner-graphic"
|
|
/>
|
|
<IndentedPanel class="mt-20 mb-20">
|
|
<div
|
|
v-if="!readWhatsNewAlready"
|
|
class="row"
|
|
>
|
|
<div class="col span-12">
|
|
<Banner
|
|
data-testid="changelog-banner"
|
|
color="info whats-new"
|
|
>
|
|
<div>
|
|
{{ t('landing.seeWhatsNew', { version: CURRENT_RANCHER_VERSION }) }}
|
|
</div>
|
|
<a
|
|
class="hand banner-link"
|
|
:href="releaseNotesUrl"
|
|
role="link"
|
|
target="_blank"
|
|
rel="noopener noreferrer nofollow"
|
|
:aria-label="t('landing.whatsNewLink', { version: CURRENT_RANCHER_VERSION })"
|
|
@click.stop="showWhatsNew"
|
|
@keyup.stop.enter="showWhatsNew"
|
|
><span v-clean-html="t('landing.whatsNewLink', { version: CURRENT_RANCHER_VERSION })" /></a>
|
|
</Banner>
|
|
</div>
|
|
</div>
|
|
<div class="row home-panels">
|
|
<div class="col main-panel">
|
|
<div
|
|
v-if="!showSetLoginBanner"
|
|
class="mb-10 row"
|
|
>
|
|
<div class="col span-12">
|
|
<Banner
|
|
color="set-login-page mt-0"
|
|
data-testid="set-login-page-banner"
|
|
:closable="true"
|
|
@close="closeSetLoginBanner()"
|
|
>
|
|
<div>
|
|
{{ t('landing.landingPrefs.title') }}
|
|
</div>
|
|
<a
|
|
class="hand mr-20"
|
|
tabindex="0"
|
|
:aria-label="t('landing.landingPrefs.userPrefs')"
|
|
@click.prevent.stop="showUserPrefs"
|
|
@keyup.prevent.stop.enter="showUserPrefs"
|
|
@keyup.prevent.stop.space="showUserPrefs"
|
|
><span v-clean-html="t('landing.landingPrefs.userPrefs')" /></a>
|
|
</Banner>
|
|
</div>
|
|
</div>
|
|
<div class="row panel">
|
|
<div
|
|
v-if="mcm"
|
|
class="col span-12"
|
|
>
|
|
<PaginatedResourceTable
|
|
v-if="provClusterSchema"
|
|
:schema="provClusterSchema"
|
|
:table-actions="false"
|
|
:row-actions="false"
|
|
key-field="id"
|
|
:headers="headers"
|
|
:pagination-headers="paginationHeaders"
|
|
context="home"
|
|
|
|
:local-filter="filterRowsLocal"
|
|
:api-filter="filterRowsApi"
|
|
|
|
:namespaced="false"
|
|
:groupable="false"
|
|
manualRefreshButtonSize="sm"
|
|
:fetchSecondaryResources="fetchSecondaryResources"
|
|
:fetchPageSecondaryResources="fetchPageSecondaryResources"
|
|
>
|
|
<template #header-left>
|
|
<div class="row table-heading">
|
|
<h2 class="mb-0">
|
|
{{ t('landing.clusters.title') }}
|
|
</h2>
|
|
<BadgeState
|
|
v-if="clusterCount"
|
|
:label="clusterCount.toString()"
|
|
color="role-tertiary ml-20 mr-20"
|
|
/>
|
|
</div>
|
|
</template>
|
|
<template
|
|
v-if="canCreateCluster || !!provClusterSchema"
|
|
#header-middle
|
|
>
|
|
<div class="table-heading">
|
|
<router-link
|
|
v-if="!!provClusterSchema"
|
|
:to="manageLocation"
|
|
class="btn btn-sm role-secondary"
|
|
data-testid="cluster-management-manage-button"
|
|
role="button"
|
|
:aria-label="t('cluster.manageAction')"
|
|
@keyup.space="$router.push(manageLocation)"
|
|
>
|
|
{{ t('cluster.manageAction') }}
|
|
</router-link>
|
|
<router-link
|
|
v-if="canCreateCluster"
|
|
:to="importLocation"
|
|
class="btn btn-sm role-primary"
|
|
data-testid="cluster-create-import-button"
|
|
role="button"
|
|
:aria-label="t('cluster.importAction')"
|
|
@keyup.space="$router.push(importLocation)"
|
|
>
|
|
{{ t('cluster.importAction') }}
|
|
</router-link>
|
|
<router-link
|
|
v-if="canCreateCluster"
|
|
:to="createLocation"
|
|
class="btn btn-sm role-primary"
|
|
data-testid="cluster-create-button"
|
|
role="button"
|
|
:aria-label="t('generic.create')"
|
|
@keyup.space="$router.push(createLocation)"
|
|
>
|
|
{{ t('generic.create') }}
|
|
</router-link>
|
|
</div>
|
|
</template>
|
|
<template #col:name="{row}">
|
|
<td class="col-name">
|
|
<div class="list-cluster-name">
|
|
<p
|
|
v-if="row.mgmt"
|
|
class="cluster-name"
|
|
>
|
|
<router-link
|
|
v-if="row.mgmt.isReady && !row.hasError"
|
|
:to="{ name: 'c-cluster-explorer', params: { cluster: row.mgmt.id }}"
|
|
role="link"
|
|
:aria-label="row.nameDisplay"
|
|
>
|
|
{{ row.nameDisplay }}
|
|
</router-link>
|
|
<span v-else>{{ row.nameDisplay }}</span>
|
|
<i
|
|
v-if="row.unavailableMachines"
|
|
v-clean-tooltip="row.unavailableMachines"
|
|
class="conditions-alert-icon icon-alert icon"
|
|
/>
|
|
<i
|
|
v-if="row.isRke1"
|
|
v-clean-tooltip="t('cluster.rke1Unsupported')"
|
|
class="rke1-unsupported-icon icon-warning icon"
|
|
/>
|
|
</p>
|
|
<p
|
|
v-if="row.description"
|
|
class="cluster-description"
|
|
>
|
|
{{ row.description }}
|
|
</p>
|
|
</div>
|
|
</td>
|
|
</template>
|
|
<template #col:kubernetesVersion="{row}">
|
|
<td class="col-name">
|
|
<span>
|
|
{{ row.kubernetesVersion }}
|
|
</span>
|
|
<div
|
|
v-clean-tooltip="{content: row.architecture.tooltip, placement: 'left'}"
|
|
class="text-muted"
|
|
>
|
|
{{ row.architecture.label }}
|
|
</div>
|
|
</td>
|
|
</template>
|
|
<template #col:cpu="{row}">
|
|
<td v-if="row.mgmt && cpuAllocatable(row.mgmt)">
|
|
{{ `${cpuAllocatable(row.mgmt)} ${t('landing.clusters.cores', {count:cpuAllocatable(row.mgmt) })}` }}
|
|
</td>
|
|
<td v-else>
|
|
—
|
|
</td>
|
|
</template>
|
|
<template #col:memory="{row}">
|
|
<td v-if="row.mgmt && memoryAllocatable(row.mgmt) && !memoryAllocatable(row.mgmt).match(/^0 [a-zA-z]/)">
|
|
{{ memoryAllocatable(row.mgmt) }}
|
|
</td>
|
|
<td v-else>
|
|
—
|
|
</td>
|
|
</template>
|
|
<!-- <template #cell:explorer="{row}">
|
|
<router-link v-if="row && row.isReady" class="btn btn-sm role-primary" :to="{name: 'c-cluster', params: {cluster: row.id}}">
|
|
{{ t('landing.clusters.explore') }}
|
|
</router-link>
|
|
<button v-else :disabled="true" class="btn btn-sm role-primary">
|
|
{{ t('landing.clusters.explore') }}
|
|
</button>
|
|
</template> -->
|
|
</PaginatedResourceTable>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="col span-12"
|
|
>
|
|
<SingleClusterInfo />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<CommunityLinks class="col span-3 side-panel" />
|
|
</div>
|
|
</IndentedPanel>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang='scss' scoped>
|
|
.banner-link:focus-visible {
|
|
@include focus-outline;
|
|
}
|
|
|
|
.home-panels {
|
|
display: flex;
|
|
align-items: stretch;
|
|
.col {
|
|
margin: 0
|
|
}
|
|
.main-panel {
|
|
flex: auto;
|
|
}
|
|
|
|
.side-panel {
|
|
margin-left: 1.75%;
|
|
}
|
|
}
|
|
|
|
.set-login-page, .whats-new {
|
|
> :deep() .banner__content {
|
|
display: flex;
|
|
|
|
> div {
|
|
flex: 1;
|
|
}
|
|
> a {
|
|
align-self: flex-end;
|
|
}
|
|
}
|
|
}
|
|
|
|
.banner.set-login-page {
|
|
border: 1px solid var(--border);
|
|
}
|
|
.table-heading {
|
|
align-items: center;
|
|
display: flex;
|
|
height: 39px;
|
|
|
|
& > a {
|
|
margin-left: 10px;
|
|
}
|
|
}
|
|
.panel:not(:first-child) {
|
|
margin-top: 20px;
|
|
}
|
|
.getting-started {
|
|
align-items: flex-end;
|
|
display: flex;
|
|
|
|
> span {
|
|
flex: 1;
|
|
margin-right: 20px;
|
|
}
|
|
}
|
|
.getting-started-btn {
|
|
display: contents;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.col-name {
|
|
max-width: 280px;
|
|
}
|
|
|
|
.list-cluster-name {
|
|
|
|
.cluster-name {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
// Ensure long cluster names truncate with ellipsis
|
|
> A {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
|
|
.cluster-description {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
color: var(--muted);
|
|
}
|
|
|
|
.conditions-alert-icon {
|
|
color: var(--error);
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.rke1-unsupported-icon {
|
|
color: var(--warning);
|
|
margin-left: 4px;
|
|
}
|
|
}
|
|
|
|
// Hide the side-panel showing links when the screen is small
|
|
@media screen and (max-width: 996px) {
|
|
.side-panel {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style lang="scss">
|
|
.home-page {
|
|
.search {
|
|
align-items: center;
|
|
display: flex;
|
|
|
|
> INPUT {
|
|
background-color: transparent;
|
|
height: 30px;
|
|
padding: 8px;
|
|
}
|
|
}
|
|
|
|
h2 {
|
|
font-size: 16px;
|
|
}
|
|
}
|
|
</style>
|