Skip to content

Commit

Permalink
Added a wizard to create cluster from cluster class (#26)
Browse files Browse the repository at this point in the history
Added a wizard to create a capi cluster.

---------

Co-authored-by: Nancy Butler <42977925+mantis-toboggan-md@users.noreply.github.com>
  • Loading branch information
eva-vashkevich and mantis-toboggan-md committed Jan 27, 2024
1 parent 0c5c8ea commit 0e57bba
Show file tree
Hide file tree
Showing 17 changed files with 1,494 additions and 123 deletions.
4 changes: 2 additions & 2 deletions pkg/capi/components/CCVariables/Variable.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { defineComponent } from 'vue';
import Vue from 'vue';
import type { PropType } from 'vue';
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
Expand All @@ -12,7 +12,7 @@ import { mapGetters } from 'vuex';
import { Translation } from '@rancher/shell/types/t';
import type { ClusterClassVariable } from '../../types/clusterClass';
import { isDefined, openAPIV3SchemaValidators } from '../../util/validators';
export default defineComponent({
export default Vue.extend({
name: 'CCVariable',
props: {
Expand Down
5 changes: 3 additions & 2 deletions pkg/capi/components/CCVariables/index.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<script lang="ts">
import debounce from 'lodash/debounce';
import { defineComponent } from 'vue';
import Vue from 'vue';
import type { PropType } from 'vue';
import { randomStr } from '@shell/utils/string';
import { ClusterClassVariable } from '../../types/clusterClass';
import type { CapiClusterVariable } from '../../types/cluster.x-k8s.io.cluster';
import { isDefined } from '../../util/validators';
import Variable from './Variable.vue';
export default defineComponent({
export default Vue.extend({
name: 'ClusterClassVariables',
components: { Variable },
Expand Down
97 changes: 97 additions & 0 deletions pkg/capi/components/CardGrid.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script lang="ts">
import Vue from 'vue';
import type { PropType } from 'vue';
import { get } from '@shell/utils/object';
import capitalize from 'lodash/capitalize';
import ClusterClassCard from './../components/ClusterClassCard/index.vue';
interface Card {
id: String,
obj: Object,
selected: Boolean
}
export default Vue.extend({
components: { ClusterClassCard },
name: 'CardGrid',
props: {
rows: {
type: Array as PropType<Card[]>,
required: true,
},
keyField: {
type: String,
default: 'key',
},
noDataKey: {
type: String,
default: 'sortableTable.noRows',
},
/**
* Inherited global identifier prefix for tests
* Define a term based on the parent component to avoid conflicts on multiple components
*/
componentTestid: {
type: String,
default: 'select-card-grid'
}
},
methods: {
get,
select(row: Card[], idx: number) {
this.resetSelected();
this.rows[idx].selected = true;
this.$emit('clicked', row, idx);
},
resetSelected() {
this.rows.map((el) => {
el.selected = false;
});
},
capitalize
},
});
</script>

<template>
<div
v-if="rows.length"
class="container"
>
<div
v-for="(r, idx) in rows"
:key="get(r, keyField)"
:data-testid="componentTestid + '-' + idx"
@click="select(r, idx)"
>
<ClusterClassCard
:value="r.obj"
:selected="r.selected"
/>
</div>
</div>
<div
v-else
class="m-50 text-center"
>
<h1 v-t="noDataKey" />
</div>
</template>

<style lang="scss" scoped>
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
@media only screen and (max-width: map-get($breakpoints, '--viewport-9')) {
grid-template-columns: 1fr 1fr;
}
@media only screen and (max-width: map-get($breakpoints, '--viewport-12')) {
grid-template-columns: 1fr;
}
}
</style>
48 changes: 48 additions & 0 deletions pkg/capi/components/ClusterClassCard/ClusterCardField.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script>
export default {
props: {
name: {
type: String,
required: true
},
value: {
type: [Number, String, undefined],
default: ''
}
}
};
</script>

<template>
<div class="label">
<div class="text-label">
<slot name="name">
{{ name }}
</slot>
</div>

<div class="value">
<slot name="value">
{{ value }}
</slot>
</div>
</div>
</template>

<style lang="scss" scoped>
.label {
display: flex;
flex-direction: column;
margin-bottom: 10px;
.text-label {
font-size: 12px;
line-height: $input-line-height;
color: var(--darker);
}
.value {
line-height: $input-line-height;
}
}
</style>
186 changes: 186 additions & 0 deletions pkg/capi/components/ClusterClassCard/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<script lang='ts'>
import Vue from 'vue';
import { mapGetters } from 'vuex';
import ClusterCardField from './ClusterCardField.vue';
import { Worker } from './../../types/capi';
export default Vue.extend({
name: 'ClusterClassCard',
components: { ClusterCardField },
props: {
value: {
type: Object,
required: true,
},
selected: {
type: Boolean,
default: false
}
},
data() {
return {
cloneData: true,
errors: []
};
},
computed: {
...mapGetters({ t: 'i18n/t' }),
name() {
return this.value?.metadata?.name || '';
},
description() {
return this.value?.metadata?.annotations?.description || '';
},
controlPlaneName() {
return this.value?.spec?.controlPlane?.ref?.name;
},
controlPlaneKind() {
return this.value?.spec?.controlPlane?.ref?.kind;
},
controlPlaneNamespace() {
return this.value?.spec?.controlPlane?.ref?.namespace;
},
machineDeploymentsCount() {
return this.value?.spec?.workers?.machineDeployments?.length;
},
machineDeploymentsList() {
return this.value?.spec?.workers?.machineDeployments?.map((w: Worker) => w.class).join(', ') || '';
},
machinePoolsCount() {
return this.value?.spec?.workers?.machinePools?.length;
},
machinePoolsList() {
return this.value?.spec?.workers?.machinePools?.map((w: Worker) => w.class).join(', ') || '';
}
},
});
</script>

<template>
<div
class="cluster-card-container"
:class="{selected: !!selected}"
data-testid="cluster-class-card"
>
<div class="card-wrap">
<div class="name">
{{ name }}
</div>
<div class="description">
<p>{{ description }}</p>
</div>
<div class="container">
<div class="leftcol">
<ClusterCardField
:value="controlPlaneName"
:name="t('capi.clusterClassCard.controlPlaneName')"
/>
<ClusterCardField
:value="controlPlaneKind"
:name="t('capi.clusterClassCard.controlPlaneKind')"
/>
<ClusterCardField
:value="controlPlaneNamespace"
:name="t('capi.clusterClassCard.controlPlaneNamespace')"
/>
</div>
<div class="rightcol">
<ClusterCardField
v-if="machineDeploymentsCount > 0"
:value="machineDeploymentsList"
:name="t('capi.clusterClassCard.machineDeploymentsCount',{count: machineDeploymentsCount})"
/>
<ClusterCardField
v-if="machinePoolsCount > 0"
:value="machinePoolsList"
:name="t('capi.clusterClassCard.machinePoolsCount',{count: machinePoolsCount})"
/>
</div>
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
.cluster-card-container {
display: flex;
flex-basis: 40%;
margin: 10px;
min-height: 100px;
padding: 10px 20px;
height: 90%;
border: 1px solid var(--border);
border-radius: var(--border-radius);
&.selected {
border: 2px solid var(--app-rancher-accent );
}
&:hover {
box-shadow: 0 0 30px var(--shadow);
transition: box-shadow 0.1s ease-in-out;
cursor: pointer;
text-decoration: none !important;
}
&.rancher {
.side-label, .deploys-os-label {
background-color: var(--app-rancher-accent);
label {
color: var(--app-rancher-accent-text);
}
}
&:hover {
border-color: var(--app-rancher-accent);
}
}
&.partner {
.side-label, .deploys-os-label {
background-color: var(--app-partner-accent);
label {
color: var(--app-partner-accent-text);
}
}
&:hover {
border-color: var(--app-partner-accent);
}
}
.card-wrap {
width: 100%;
}
}
.name {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
}
.description {
font-size: 14px;
margin-bottom: 20px;
}
.container{
display: grid;
padding: 0px;
grid-template-columns: 1fr 1fr;
@media only screen and (max-width: map-get($breakpoints, '--viewport-12')) {
grid-template-columns: 1fr;
}
}
.leftcol {
justify-self: start;
margin-right: 10px;
}
.rightcol{
justify-self: end;
@media only screen and (max-width: map-get($breakpoints, '--viewport-12')) {
justify-self: start;
}
}
</style>
Loading

0 comments on commit 0e57bba

Please sign in to comment.