mirror of https://github.com/rancher/dashboard.git
351 lines
9.4 KiB
Vue
351 lines
9.4 KiB
Vue
<script>
|
|
import AsyncButton from '@/components/AsyncButton';
|
|
import Loading from '@/components/Loading';
|
|
import Banner from '@/components/Banner';
|
|
import { CATALOG } from '@/config/types';
|
|
import {
|
|
REPO_TYPE, REPO, CHART, VERSION, SEARCH_QUERY, _FLAGGED, _UNFLAG
|
|
} from '@/config/query-params';
|
|
import { CATALOG as CATALOG_ANNOTATIONS } from '@/config/labels-annotations';
|
|
import { ensureRegex } from '@/utils/string';
|
|
import { sortBy } from '@/utils/sort';
|
|
import { mapGetters } from 'vuex';
|
|
import ButtonGroup from '@/components/ButtonGroup';
|
|
import Checkbox from '@/components/form/Checkbox';
|
|
import LazyImage from '@/components/LazyImage';
|
|
|
|
export default {
|
|
components: {
|
|
AsyncButton,
|
|
Banner,
|
|
Loading,
|
|
ButtonGroup,
|
|
Checkbox,
|
|
LazyImage,
|
|
|
|
},
|
|
|
|
async fetch() {
|
|
await this.$store.dispatch('catalog/load');
|
|
},
|
|
|
|
data() {
|
|
const query = this.$route.query;
|
|
|
|
let showRancher = query['rancher'] === _FLAGGED;
|
|
let showPartner = query['partner'] === _FLAGGED;
|
|
let showOther = query['other'] === _FLAGGED;
|
|
|
|
if ( !showRancher && !showPartner && !showOther ) {
|
|
showRancher = true;
|
|
showPartner = true;
|
|
showOther = true;
|
|
}
|
|
|
|
return {
|
|
searchQuery: query[SEARCH_QUERY] || '',
|
|
sortField: 'certifiedSort',
|
|
showDeprecated: false,
|
|
showRancher,
|
|
showPartner,
|
|
showOther,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
...mapGetters({ allCharts: 'catalog/charts', loadingErrors: 'catalog/errors' }),
|
|
|
|
filteredCharts() {
|
|
return (this.allCharts || []).filter((c) => {
|
|
if ( c.deprecated && !this.showDeprecated ) {
|
|
return false;
|
|
}
|
|
|
|
if ( ( c.certified === CATALOG_ANNOTATIONS._RANCHER && !this.showRancher ) ||
|
|
( c.certified === CATALOG_ANNOTATIONS._PARTNER && !this.showPartner ) ||
|
|
( c.certified !== CATALOG_ANNOTATIONS._RANCHER && c.certified !== CATALOG_ANNOTATIONS._PARTNER && !this.showOther )
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
if ( this.searchQuery ) {
|
|
const searchTokens = this.searchQuery.split(/\s*[, ]\s*/).map(x => ensureRegex(x, false));
|
|
|
|
for ( const token of searchTokens ) {
|
|
if ( !c.chartName.match(token) && !c.description.match(token) ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
},
|
|
|
|
arrangedCharts() {
|
|
return sortBy(this.filteredCharts, [this.sortField, 'chartName']);
|
|
},
|
|
},
|
|
|
|
watch: {
|
|
searchQuery(q) {
|
|
this.$router.applyQuery({ [SEARCH_QUERY]: q || undefined });
|
|
},
|
|
|
|
showRancher: 'updateShowFlags',
|
|
showPartner: 'updateShowFlags',
|
|
showOther: 'updateShowFlags',
|
|
},
|
|
|
|
methods: {
|
|
updateShowFlags() {
|
|
if ( (!this.showRancher && !this.showPartner && !this.showOther) ||
|
|
( this.showRancher && this.showPartner && this.showOther) ) {
|
|
this.$router.applyQuery({
|
|
rancher: _UNFLAG,
|
|
partner: _UNFLAG,
|
|
other: _UNFLAG,
|
|
});
|
|
} else {
|
|
this.$router.applyQuery({
|
|
rancher: this.showRancher ? _FLAGGED : _UNFLAG,
|
|
partner: this.showPartner ? _FLAGGED : _UNFLAG,
|
|
other: this.showOther ? _FLAGGED : _UNFLAG,
|
|
});
|
|
}
|
|
},
|
|
|
|
selectChart(chart) {
|
|
this.$router.push({
|
|
name: 'c-cluster-product-resource-create',
|
|
params: {
|
|
cluster: this.$route.params.cluster,
|
|
product: this.$store.getters['productId'],
|
|
resource: CATALOG.RELEASE,
|
|
},
|
|
query: {
|
|
[REPO_TYPE]: chart.repoType,
|
|
[REPO]: chart.repoName,
|
|
[CHART]: chart.chartName,
|
|
[VERSION]: chart.versions[0].version,
|
|
}
|
|
});
|
|
},
|
|
|
|
focusSearch() {
|
|
this.$refs.searchQuery.focus();
|
|
this.$refs.searchQuery.select();
|
|
},
|
|
|
|
async refresh(btnCb) {
|
|
try {
|
|
await this.$store.dispatch('catalog/refresh');
|
|
btnCb(true);
|
|
} catch (e) {
|
|
this.$store.dispatch('growl/fromError', e);
|
|
btnCb(false);
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Loading v-if="$fetchState.pending" />
|
|
<div v-else>
|
|
<Banner v-for="err in loadingErrors" :key="err" color="error" :label="err" />
|
|
|
|
<div v-if="allCharts.length">
|
|
<div class="clearfix">
|
|
<div class="pull-left">
|
|
<Checkbox v-model="showRancher" label="Rancher" class="check-rancher" />
|
|
<Checkbox v-model="showPartner" label="Partner" class="check-partner" />
|
|
<Checkbox v-model="showOther" label="Other" class="check-other" />
|
|
</div>
|
|
<div class="pull-right">
|
|
<input ref="searchQuery" v-model="searchQuery" type="search" class="input-sm" placeholder="Filter">
|
|
<button v-shortkey.once="['/']" class="hide" @shortkey="focusSearch()" />
|
|
</div>
|
|
<div class="pull-right pt-5 pr-10">
|
|
<AsyncButton mode="refresh" class="btn-sm mr-10" @click="refresh" />
|
|
<ButtonGroup v-if="false" v-model="sortField" :options="[{label: 'By Name', value: 'name'}, {label: 'By Kind', value: 'certifiedSort'}]" />
|
|
</div>
|
|
</div>
|
|
<div class="charts">
|
|
<div v-for="c in arrangedCharts" :key="c.key" class="chart" :class="{[c.certified]: true}" @click="selectChart(c)">
|
|
<div class="side-label">
|
|
<label v-if="c.sideLabel">{{ c.sideLabel }}</label>
|
|
</div>
|
|
<div class="logo">
|
|
<LazyImage :src="c.icon" />
|
|
</div>
|
|
<h4 class="name">
|
|
{{ c.chartName }}
|
|
</h4>
|
|
<div class="description">
|
|
{{ c.description }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-else class="m-50 text-center">
|
|
<h1>There are no charts available, have you added any repos?</h1>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
$chart: 110px;
|
|
$side: 15px;
|
|
$margin: 10px;
|
|
$logo: 60px;
|
|
|
|
.check-rancher, .check-partner, .check-other {
|
|
border-radius: var(--border-radius);
|
|
padding: 3px 0 3px 8px;
|
|
margin-right: 5px;
|
|
}
|
|
.check-rancher {
|
|
background: var(--app-rancher-bg);
|
|
border: 1px solid var(--app-rancher-accent);
|
|
}
|
|
.check-partner {
|
|
background: var(--app-partner-bg);
|
|
border: 1px solid var(--app-partner-accent);
|
|
}
|
|
.check-other {
|
|
background: var(--app-other-bg);
|
|
border: 1px solid var(--app-other-accent);
|
|
}
|
|
|
|
.charts {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
flex-wrap: wrap;
|
|
margin: 0 -1*$margin;
|
|
|
|
@media only screen and (min-width: map-get($breakpoints, '--viewport-4')) {
|
|
.chart {
|
|
width: 100%;
|
|
}
|
|
}
|
|
@media only screen and (min-width: map-get($breakpoints, '--viewport-7')) {
|
|
.chart {
|
|
width: calc(50% - 2 * #{$margin});
|
|
}
|
|
}
|
|
@media only screen and (min-width: map-get($breakpoints, '--viewport-9')) {
|
|
.chart {
|
|
width: calc(33.33333% - 2 * #{$margin});
|
|
}
|
|
}
|
|
@media only screen and (min-width: map-get($breakpoints, '--viewport-12')) {
|
|
.chart {
|
|
width: calc(25% - 2 * #{$margin});
|
|
}
|
|
}
|
|
|
|
.chart {
|
|
height: $chart;
|
|
margin: $margin;
|
|
padding: $margin;
|
|
position: relative;
|
|
border-radius: calc( 3 * var(--border-radius));
|
|
|
|
&:hover {
|
|
box-shadow: 0 0 0 2px var(--body-text);
|
|
transition: box-shadow 0.1s ease-in-out;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.side-label {
|
|
transform: rotate(180deg);
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
bottom: 0;
|
|
min-width: calc(3 * var(--border-radius));
|
|
width: $side;
|
|
border-top-right-radius: calc( 3 * var(--border-radius));
|
|
border-bottom-right-radius: calc( 3 * var(--border-radius));
|
|
|
|
label {
|
|
text-align: center;
|
|
writing-mode: tb;
|
|
height: 100%;
|
|
padding: 0 1px;
|
|
display: block;
|
|
white-space: no-wrap;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
|
|
.logo {
|
|
text-align: center;
|
|
position: absolute;
|
|
left: $side+$margin;
|
|
top: ($chart - $logo)/2;
|
|
width: $logo;
|
|
height: $logo;
|
|
border-radius: calc(5 * var(--border-radius));
|
|
overflow: hidden;
|
|
background-color: white;
|
|
|
|
img {
|
|
width: $logo - 4px;
|
|
height: $logo - 4px;
|
|
object-fit: contain;
|
|
position: relative;
|
|
top: 2px;
|
|
}
|
|
}
|
|
|
|
&.rancher {
|
|
background: var(--app-rancher-bg);
|
|
.side-label {
|
|
background-color: var(--app-rancher-accent);
|
|
color: var(--app-rancher-accent-text);
|
|
}
|
|
}
|
|
|
|
&.partner {
|
|
background: var(--app-partner-bg);
|
|
.side-label {
|
|
background-color: var(--app-partner-accent);
|
|
color: var(--app-partner-accent-text);
|
|
}
|
|
}
|
|
|
|
&.other {
|
|
background: var(--app-other-bg);
|
|
.side-label {
|
|
background-color: var(--app-other-accent);
|
|
color: var(--app-other-accent-text);
|
|
}
|
|
}
|
|
|
|
.name {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
margin-top: $margin;
|
|
margin-left: $side+$logo+$margin;
|
|
}
|
|
|
|
.description {
|
|
margin-top: $margin;
|
|
margin-left: $side+$logo+$margin;
|
|
margin-right: $margin;
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 3;
|
|
line-clamp: 3;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
color: var(--text-muted);
|
|
}
|
|
}
|
|
}
|
|
|
|
</style>
|