mirror of https://github.com/rancher/dashboard.git
343 lines
8.0 KiB
Vue
343 lines
8.0 KiB
Vue
<script>
|
|
import { cleanForNew } from '@/plugins/steve/normalize';
|
|
import CreateEditView from '@/mixins/create-edit-view';
|
|
import ResourceYaml from '@/components/ResourceYaml';
|
|
import {
|
|
MODE, _VIEW, _EDIT, _CLONE, _STAGE,
|
|
EDIT_YAML, _FLAGGED, _CREATE
|
|
} from '@/config/query-params';
|
|
import { NAMESPACE } from '@/config/types';
|
|
|
|
// Components can't have asyncData, only pages.
|
|
// So you have to call this in the page and pass it in as a prop.
|
|
export async function asyncData(ctx) {
|
|
const { store, params, route } = ctx;
|
|
const { resource, namespace, id } = params;
|
|
|
|
const hasCustomDetail = store.getters['type-map/hasCustomDetail'](resource);
|
|
const hasCustomEdit = store.getters['type-map/hasCustomEdit'](resource);
|
|
|
|
// There are 5 "real" modes: view, create, edit, stage, clone
|
|
// which later map to 3 logical/page modes: view, create, edit (stage and clone are "create")
|
|
const realMode = route.query.mode || _VIEW;
|
|
const schema = store.getters['cluster/schemaFor'](resource);
|
|
|
|
let fqid = id;
|
|
|
|
if ( schema.attributes.namespaced && namespace ) {
|
|
fqid = `${ namespace }/${ fqid }`;
|
|
}
|
|
|
|
const obj = await store.dispatch('cluster/find', { type: resource, id: fqid });
|
|
|
|
const forNew = realMode === _CLONE || realMode === _STAGE;
|
|
const model = await store.dispatch('cluster/clone', { resource: obj });
|
|
|
|
if ( forNew ) {
|
|
cleanForNew(model);
|
|
}
|
|
|
|
if ( model.applyDefaults ) {
|
|
model.applyDefaults(ctx, realMode);
|
|
}
|
|
|
|
let mode = realMode;
|
|
|
|
if ( realMode === _STAGE || realMode === _CLONE ) {
|
|
mode = _CREATE;
|
|
}
|
|
|
|
const asYaml = (route.query[EDIT_YAML] === _FLAGGED) || (mode === _VIEW && !hasCustomDetail) || (mode !== _VIEW && !hasCustomEdit);
|
|
let yaml = null;
|
|
|
|
const link = obj.hasLink('rioview') ? 'rioview' : 'view';
|
|
|
|
yaml = (await obj.followLink(link, { headers: { accept: 'application/yaml' } })).data;
|
|
|
|
/*******
|
|
* Important: these need to be declared below as props too
|
|
*******/
|
|
const out = {
|
|
hasCustomDetail,
|
|
hasCustomEdit,
|
|
schema,
|
|
resource,
|
|
model,
|
|
asYaml,
|
|
yaml,
|
|
originalModel: obj,
|
|
mode,
|
|
realMode
|
|
};
|
|
/*******
|
|
* Important: these need to be declared below as props too
|
|
*******/
|
|
|
|
return out;
|
|
}
|
|
|
|
export const watchQuery = [MODE, EDIT_YAML];
|
|
|
|
export default {
|
|
components: { ResourceYaml },
|
|
mixins: { CreateEditView },
|
|
|
|
props: {
|
|
hasCustomDetail: {
|
|
type: Boolean,
|
|
default: null,
|
|
},
|
|
hasCustomEdit: {
|
|
type: Boolean,
|
|
default: null,
|
|
},
|
|
schema: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
resource: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
model: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
asYaml: {
|
|
type: Boolean,
|
|
default: null,
|
|
},
|
|
yaml: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
originalModel: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
mode: {
|
|
type: String,
|
|
default: null
|
|
},
|
|
realMode: {
|
|
type: String,
|
|
default: null
|
|
}
|
|
},
|
|
|
|
data() {
|
|
if (this.hasCustomDetail) {
|
|
this.$store.getters['type-map/importDetail'](this.resource)().then((component) => {
|
|
this.importedDetailComponent = component.default;
|
|
});
|
|
}
|
|
|
|
if (this.hasCustomEdit) {
|
|
this.$store.getters['type-map/importEdit'](this.resource)().then((component) => {
|
|
this.importedEditComponent = component.default;
|
|
});
|
|
}
|
|
|
|
return {
|
|
isCustomYamlEditor: false,
|
|
currentValue: this.value,
|
|
detailComponent: this.$store.getters['type-map/importDetail'](this.resource),
|
|
importedDetailComponent: null,
|
|
editComponent: this.$store.getters['type-map/importEdit'](this.resource),
|
|
importedEditComponent: null,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
isView() {
|
|
return this.mode === _VIEW;
|
|
},
|
|
|
|
isEdit() {
|
|
return this.mode === _EDIT;
|
|
},
|
|
|
|
showEditAsYaml() {
|
|
return this.isEdit && !this.importedEditComponent?.isYamlEditor;
|
|
},
|
|
|
|
doneRoute() {
|
|
const name = this.$route.name.replace(/(-namespace)?-id$/, '');
|
|
|
|
return name;
|
|
},
|
|
|
|
doneParams() {
|
|
return this.$route.params;
|
|
},
|
|
|
|
parentLink() {
|
|
const name = this.doneRoute;
|
|
const params = this.doneParams;
|
|
const out = this.$router.resolve({ name, params }).href;
|
|
|
|
return out;
|
|
},
|
|
|
|
showComponent() {
|
|
if ( this.isView && this.hasCustomDetail ) {
|
|
return this.detailComponent;
|
|
} else if ( !this.isView && this.hasCustomEdit ) {
|
|
return this.editComponent;
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
typeDisplay() {
|
|
return this.$store.getters['type-map/singularLabelFor'](this.schema);
|
|
},
|
|
|
|
namespaceSuffixOnCreate() {
|
|
return this.resource !== NAMESPACE;
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
showActions() {
|
|
this.$store.commit('action-menu/show', {
|
|
resources: this.originalModel,
|
|
elem: this.$refs.actions,
|
|
});
|
|
},
|
|
|
|
goBack() {
|
|
window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/');
|
|
},
|
|
|
|
navigateToEditAsYaml() {
|
|
this.$router.push({ query: { ...this.$route.query, [EDIT_YAML]: _FLAGGED } });
|
|
},
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<template v-if="asYaml">
|
|
<ResourceYaml
|
|
:obj="model"
|
|
:value="yaml"
|
|
:done-route="doneRoute"
|
|
:parent-route="doneRoute"
|
|
:parent-params="doneParams"
|
|
:has-edit-as-form="hasCustomEdit"
|
|
:done-override="doneOverride"
|
|
/>
|
|
</template>
|
|
<template v-else>
|
|
<header>
|
|
<h1 v-trim-whitespace>
|
|
<span v-if="realMode === 'edit'">Edit {{ typeDisplay }}: </span>
|
|
<span v-else-if="realMode === 'stage'">Stage from {{ typeDisplay }}: </span>
|
|
<span v-else-if="realMode === 'clone'">Clone from {{ typeDisplay }}: </span>
|
|
<nuxt-link
|
|
v-else
|
|
v-trim-whitespace
|
|
:to="parentLink"
|
|
>
|
|
{{ typeDisplay }}:
|
|
</nuxt-link>{{ originalModel.nameDisplay }}
|
|
</h1>
|
|
<div v-if="isView" class="actions">
|
|
<button ref="actions" type="button" class="btn btn-sm role-multi-action actions" @click="showActions">
|
|
<i class="icon icon-actions" />
|
|
</button>
|
|
</div>
|
|
<div v-if="showEditAsYaml" class="actions">
|
|
<button class="btn bg-primary" @click="navigateToEditAsYaml">
|
|
Edit as YAML
|
|
</button>
|
|
</div>
|
|
</header>
|
|
<component
|
|
:is="showComponent"
|
|
v-model="model"
|
|
:original-value="originalModel"
|
|
:done-route="doneRoute"
|
|
:done-params="doneParams"
|
|
:parent-route="doneRoute"
|
|
:parent-params="doneParams"
|
|
:namespace-suffix-on-create="namespaceSuffixOnCreate"
|
|
:type-label="typeDisplay"
|
|
:mode="mode"
|
|
:value="model"
|
|
:obj="model"
|
|
:yaml="yaml"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang='scss' scoped>
|
|
.flat {
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
& th{
|
|
padding-bottom: 1rem;
|
|
text-align: left;
|
|
font-weight: normal;
|
|
color: var(--secondary);
|
|
}
|
|
|
|
& :not(THEAD) tr{
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
& td {
|
|
padding: 10px 0 10px 0;
|
|
}
|
|
}
|
|
& tr td:last-child, th:last-child{
|
|
text-align: right;
|
|
}
|
|
|
|
& tr td:first-child, th:first-child{
|
|
text-align: left;
|
|
margin-left: 15px;
|
|
}
|
|
|
|
& .click-row a{
|
|
color: var(--input-text);
|
|
}
|
|
|
|
& .click-row:hover{
|
|
@extend .faded;
|
|
}
|
|
|
|
& .faded {
|
|
opacity: 0.5
|
|
}
|
|
}
|
|
.detail-top{
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
background: var(--box-bg);
|
|
border: solid thin var(--border);
|
|
border-radius: var(--border-radius);
|
|
|
|
& > * {
|
|
margin-right: 20px;
|
|
padding: 10px 0 10px 0;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
|
|
&:not(:last-child){
|
|
border-right: 1px solid var(--border);
|
|
}
|
|
|
|
& >:not(:first-child){
|
|
color: var(--input-label);
|
|
padding: 3px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|