dashboard/pages/c/_cluster/_product/projectsnamespaces.vue

258 lines
7.5 KiB
Vue

<script>
import ResourceTable from '@/components/ResourceTable';
import { STATE, AGE, SIMPLE_NAME } from '@/config/table-headers';
import { uniq } from '@/utils/array';
import { MANAGEMENT, NAMESPACE, VIRTUAL_TYPES } from '@/config/types';
import Loading from '@/components/Loading';
import { PROJECT_ID } from '@/config/query-params';
import Masthead from '@/components/ResourceList/Masthead';
import { mapPref, GROUP_RESOURCES, DEV } from '@/store/prefs';
import MoveModal from '@/components/MoveModal';
export default {
name: 'ListNamespace',
components: {
Loading, Masthead, MoveModal, ResourceTable
},
props: {},
async fetch() {
const inStore = this.$store.getters['currentStore'](NAMESPACE);
this.schema = this.$store.getters[`${ inStore }/schemaFor`](NAMESPACE);
this.projectSchema = this.$store.getters[`management/schemaFor`](MANAGEMENT.PROJECT);
if ( !this.schema ) {
this.$store.dispatch('loadingError', `Type ${ NAMESPACE } not found`);
return;
}
this.namespaces = await this.$store.dispatch(`${ inStore }/findAll`, { type: NAMESPACE });
this.projects = await this.$store.dispatch('management/findAll', { type: MANAGEMENT.PROJECT, opt: { force: true } });
},
data() {
return {
schema: null,
namespaces: [],
projects: [],
projectSchema: null,
MANAGEMENT,
VIRTUAL_TYPES
};
},
computed: {
headers() {
const project = {
name: 'project',
label: 'Project',
value: 'project.nameDisplay',
sort: ['projectNameSort', 'nameSort'],
};
return [
STATE,
SIMPLE_NAME,
this.groupPreference === 'none' ? project : null,
AGE
].filter(h => h);
},
projectIdsWithNamespaces() {
const ids = this.rows
.map(row => row.projectId)
.filter(id => id);
return uniq(ids);
},
clusterProjects() {
const clusterId = this.$store.getters['currentCluster'].id;
return this.projects.filter(project => project.spec.clusterName === clusterId);
},
projectsWithoutNamespaces() {
return this.clusterProjects
.filter(project => !this.projectIdsWithNamespaces.includes(project.name));
},
// We're using this because we need to show projects as groups even if the project doesn't have any namespaces.
rowsWithFakeNamespaces() {
const fakeRows = this.projectsWithoutNamespaces.map((project) => {
return {
groupByLabel: `${ ('resourceTable.groupLabel.notInAProject') }-${ project }`,
isFake: true,
mainRowKey: project.id,
project,
availableActions: []
};
});
return [...this.rows, ...fakeRows];
},
createProjectLocation() {
return {
name: 'c-cluster-product-resource-create',
params: {
product: this.$store.getters['currentProduct'].name,
resource: MANAGEMENT.PROJECT
},
};
},
groupPreference: mapPref(GROUP_RESOURCES),
filteredRows() {
return this.groupPreference === 'none' ? this.rows : this.rowsWithFakeNamespaces;
},
rows() {
if (this.$store.getters['prefs/get'](DEV)) {
return this.namespaces;
}
return this.namespaces.filter(namespace => !namespace.isObscure);
}
},
methods: {
slotName(project) {
return `main-row:${ project.id }`;
},
createNamespaceLocation(group) {
const project = group.rows[0].project;
return {
name: 'c-cluster-product-resource-create',
params: {
product: this.$store.getters['currentProduct'].name,
resource: NAMESPACE
},
query: { [PROJECT_ID]: project?.metadata.name }
};
},
showProjectAction(event, group) {
const project = group.rows[0].project;
this.$store.commit(`action-menu/show`, {
resources: [project],
elem: event.target
});
},
showProjectActionButton(group) {
const project = group.rows[0].project;
return !!project;
},
projectLabel(group) {
const row = group.rows[0];
if (row.isFake) {
return this.t('resourceTable.groupLabel.project', { name: row.project?.nameDisplay }, true);
}
return row.groupByLabel;
},
projectDescription(group) {
const project = group.rows[0].project;
return project?.description;
},
clearSelection() {
this.$refs.table.clearSelection();
}
}
};
</script>
<template>
<Loading v-if="$fetchState.pending" />
<div v-else class="project-namespaces">
<Masthead
:schema="projectSchema"
:type-display="t('projectNamespaces.label')"
:resource="MANAGEMENT.PROJECT"
:favorite-resource="VIRTUAL_TYPES.PROJECT_NAMESPACES"
:create-location="createProjectLocation"
:create-button-label="t('projectNamespaces.createProject')"
/>
<ResourceTable
ref="table"
class="table"
v-bind="$attrs"
:schema="schema"
:headers="headers"
:rows="filteredRows"
:groupable="true"
group-tooltip="resourceTable.groupBy.project"
key-field="_key"
v-on="$listeners"
>
<template #group-by="group">
<div class="project-bar" :class="{'has-description': projectDescription(group.group)}">
<div v-trim-whitespace class="group-tab">
<div class="project-name" v-html="projectLabel(group.group)" />
<div v-if="projectDescription(group.group)" class="description text-muted text-small">
{{ projectDescription(group.group) }}
</div>
</div>
<div class="right">
<n-link
class="create-namespace btn btn-sm role-secondary"
:to="createNamespaceLocation(group.group)"
>
{{ t('projectNamespaces.createNamespace') }}
</n-link>
<button type="button" class="project-action btn btn-sm role-multi-action actions mr-5" :class="{invisible: !showProjectActionButton(group.group)}" @click="showProjectAction($event, group.group)">
<i class="icon icon-actions" />
</button>
</div>
</div>
</template>
<template #cell:project="{row}">
<span v-if="row.project">{{ row.project.nameDisplay }}</span>
<span v-else class="text-muted">&ndash;</span>
</template>
<template v-for="project in projectsWithoutNamespaces" v-slot:[slotName(project)]>
<tr :key="project.id" class="main-row">
<td class="empty text-center" colspan="5">
{{ t('projectNamespaces.noNamespaces') }}
</td>
</tr>
</template>
</ResourceTable>
<MoveModal @moving="clearSelection" />
</div>
</template>
<style lang="scss" scoped>
.project-namespaces {
& ::v-deep {
.project-name {
line-height: 30px;
}
.project-bar {
display: flex;
flex-direction: row;
justify-content: space-between;
&.has-description {
.right {
margin-top: 5px;
}
.group-tab {
&, &::after {
height: 50px;
}
&::after {
right: -20px;
}
.description {
margin-top: -20px;
}
}
}
}
}
}
</style>