Skip to content

Commit 58b8940

Browse files
authored
Merge pull request #272 from rbt-mm/master-show-hierarchical-view-in-project-list
Show hierarchical view in project list
2 parents 3a64bbc + abfe53c commit 58b8940

5 files changed

Lines changed: 195 additions & 12 deletions

File tree

src/i18n/locales/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
"property_deleted": "Property deleted",
110110
"create_project": "Create Project",
111111
"show_inactive_projects": "Show inactive projects",
112+
"hierarchical_view": "Hierarchical view",
112113
"create_project_property": "Create Project Property",
113114
"group_name": "Group Name",
114115
"property_name": "Property Name",
@@ -242,6 +243,7 @@
242243
"snapshot_notification": "Snapshot Notification",
243244
"select_project": "Select Project",
244245
"select_tag": "Select Tag",
246+
"parent": "Parent",
245247
"select": "Select",
246248
"identity": "Identity",
247249
"extended": "Extended",
@@ -382,7 +384,8 @@
382384
"component_device": "Device",
383385
"component_firmware": "Firmware",
384386
"component_file": "File",
385-
"dates": "Dates"
387+
"dates": "Dates",
388+
"inactive_active_children": "The project cannot be set to inactive if it has active children"
386389
},
387390
"admin": {
388391
"configuration": "Configuration",

src/views/portfolio/projects/Project.vue

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
<project-policy-violations :key="this.uuid" :uuid="this.uuid" v-on:total="totalViolations = $event" />
126126
</b-tab>
127127
</b-tabs>
128-
<project-details-modal :project="cloneDeep(project)" v-on:projectUpdated="syncProjectFields"/>
128+
<project-details-modal :project="cloneDeep(project)" :uuid="this.uuid" v-on:projectUpdated="syncProjectFields"/>
129129
<project-properties-modal :uuid="this.uuid" />
130130
<project-create-property-modal :uuid="this.uuid" />
131131
<project-add-version-modal :uuid="this.uuid" />
@@ -244,8 +244,6 @@
244244
},
245245
beforeMount() {
246246
this.uuid = this.$route.params.uuid;
247-
},
248-
mounted() {
249247
this.initialize();
250248
},
251249
watch:{

src/views/portfolio/projects/ProjectCreateProjectModal.vue

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
<b-input-group-form-select id="v-classifier-input" required="true"
1515
v-model="project.classifier" :options="sortAvailableClassifiers"
1616
:label="$t('message.classifier')" :tooltip="$t('message.component_classifier_desc')" />
17+
<b-input-group-form-select id="project-parent-input" required="false"
18+
v-model="selectedParent" :options="availableParents"
19+
:label="$t('message.parent')" />
1720
<b-form-group
1821
id="project-description-form-group"
1922
:label="this.$t('message.description')"
@@ -111,6 +114,10 @@
111114
],
112115
selectableLicenses: [],
113116
selectedLicense: '',
117+
selectedParent: null,
118+
availableParents: [
119+
{ value: null, text: ''}
120+
],
114121
project: {},
115122
tag: '', // The contents of a tag as its being typed into the vue-tag-input
116123
tags: [], // An array of tags bound to the vue-tag-input
@@ -130,6 +137,7 @@
130137
},
131138
beforeMount() {
132139
this.retrieveLicenses();
140+
this.retrieveParents();
133141
},
134142
computed: {
135143
sortAvailableClassifiers: function() {
@@ -149,13 +157,18 @@
149157
createProject: function() {
150158
let url = `${this.$api.BASE_URL}/${this.$api.URL_PROJECT}`;
151159
let tagsNode = [];
160+
let parent = {uuid: this.selectedParent};
161+
if (this.selectedParent == null){
162+
parent = null;
163+
}
152164
this.tags.forEach((tag) => tagsNode.push({name: tag.text}));
153165
this.axios.put(url, {
154166
name: this.project.name,
155167
version: this.project.version,
156168
group: this.project.group,
157169
description: this.project.description,
158170
//license: this.selectedLicense,
171+
parent: parent,
159172
classifier: this.project.classifier,
160173
purl: this.project.purl,
161174
cpe: this.project.cpe,
@@ -186,10 +199,31 @@
186199
this.$toastr.w(this.$t('condition.unsuccessful_action'));
187200
});
188201
},
202+
retrieveParents: function() {
203+
let url = `${this.$api.BASE_URL}/${this.$api.URL_PROJECT}`;
204+
this.axios.get(url).then((response) => {
205+
for (let i = 0; i < response.data.length; i++) {
206+
let project = response.data[i];
207+
if (project.version) {
208+
this.availableParents.push({value: project.uuid, text: project.name + ' : ' + project.version});
209+
} else {
210+
this.availableParents.push({value: project.uuid, text: project.name});
211+
}
212+
if (this.project.parent && this.project.parent.uuid === project.uuid ) {
213+
this.selectedParent = project.uuid;
214+
}
215+
}
216+
}).catch((error) => {
217+
this.$toastr.w(this.$t('condition.unsuccessful_action'));
218+
});
219+
},
189220
resetValues: function () {
190221
this.project = {};
191222
this.tag = "";
192223
this.tags = [];
224+
this.selectedParent = null;
225+
this.availableParents = [{ value: null, text: ''}]
226+
this.retrieveParents();
193227
}
194228
}
195229
}

src/views/portfolio/projects/ProjectDetailsModal.vue

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
v-model="project.classifier" :options="availableClassifiers"
1818
:label="$t('message.classifier')" :tooltip="$t('message.component_classifier_desc')"
1919
:readonly="this.isNotPermitted(PERMISSIONS.PORTFOLIO_MANAGEMENT)" />
20+
<b-input-group-form-select id="project-parent-input" required="false"
21+
v-model="selectedParent" :options="availableParents"
22+
:label="$t('message.parent')" :readonly="this.isNotPermitted(PERMISSIONS.PORTFOLIO_MANAGEMENT)" />
2023
<b-form-group
2124
id="project-description-form-group"
2225
:label="this.$t('message.description')"
@@ -33,7 +36,9 @@
3336
style="max-width:none; background-color:transparent;"
3437
:readonly="this.isNotPermitted(PERMISSIONS.PORTFOLIO_MANAGEMENT)" />
3538
</b-form-group>
36-
<c-switch id="input-5" class="mx-1" color="primary" v-model="project.active" label :disabled="this.isNotPermitted(PERMISSIONS.PORTFOLIO_MANAGEMENT)" v-bind="labelIcon" /> {{$t('message.active')}}
39+
<c-switch id="input-5" class="mx-1" color="primary" v-model="project.active" label
40+
:disabled="this.isNotPermitted(PERMISSIONS.PORTFOLIO_MANAGEMENT) || (project.active && this.hasActiveChild(project))" v-bind="labelIcon"
41+
v-b-tooltip.hover :title="$t('message.inactive_active_children')"/> {{$t('message.active')}}
3742
<p></p>
3843
<b-input-group-form-input id="project-uuid" input-group-size="mb-3" type="text" v-model="project.uuid"
3944
lazy="false" required="false" feedback="false" autofocus="false" disabled="true"
@@ -99,7 +104,8 @@
99104
cSwitch
100105
},
101106
props: {
102-
project: Object
107+
project: Object,
108+
uuid: String
103109
},
104110
data() {
105111
return {
@@ -115,6 +121,10 @@
115121
{ value: 'FIRMWARE', text: this.$i18n.t('message.component_firmware') },
116122
{ value: 'FILE', text: this.$i18n.t('message.component_file') }
117123
],
124+
selectedParent: null,
125+
availableParents: [
126+
{ value: null, text: ''}
127+
],
118128
tag: '', // The contents of a tag as its being typed into the vue-tag-input
119129
tags: [], // An array of tags bound to the vue-tag-input
120130
addOnKeys: [9, 13, 32, ':', ';', ','], // Separators used when typing tags into the vue-tag-input
@@ -128,6 +138,9 @@
128138
this.readOnlyProjectName = this.project.name;
129139
this.readOnlyProjectVersion = this.project.version;
130140
},
141+
mounted() {
142+
this.retrieveParents();
143+
},
131144
methods: {
132145
initializeTags: function() {
133146
this.tags = (this.project.tags || []).map(tag => ({ text: tag.name }));
@@ -151,6 +164,7 @@
151164
version: this.project.version,
152165
description: this.project.description,
153166
classifier: this.project.classifier,
167+
parent: {uuid: this.selectedParent},
154168
cpe: this.project.cpe,
155169
purl: this.project.purl,
156170
swidTagId: this.project.swidTagId,
@@ -174,6 +188,39 @@
174188
}).catch((error) => {
175189
this.$toastr.w(this.$t('condition.unsuccessful_action'));
176190
});
191+
},
192+
retrieveParents: function() {
193+
let url = `${this.$api.BASE_URL}/${this.$api.URL_PROJECT}/withoutDescendantsOf/${this.$props.uuid}`;
194+
this.axios.get(url).then((response) => {
195+
for (let i = 0; i < response.data.length; i++) {
196+
let project = response.data[i];
197+
if (project.uuid !== this.project.uuid){
198+
if (project.version){
199+
this.availableParents.push({value: project.uuid, text: project.name + ' : ' + project.version});
200+
} else {
201+
this.availableParents.push({value: project.uuid, text: project.name});
202+
}
203+
}
204+
if (this.project.parent && this.project.parent.uuid === project.uuid ) {
205+
this.selectedParent = project.uuid;
206+
}
207+
}
208+
}).catch((error) => {
209+
this.$toastr.w(this.$t('condition.unsuccessful_action'));
210+
});
211+
},
212+
hasActiveChild: function (project) {
213+
let bool = false;
214+
if (project.children){
215+
for (const child of project.children){
216+
if (child.active || bool){
217+
return true;
218+
} else {
219+
bool = this.hasActiveChild(child);
220+
}
221+
}
222+
}
223+
return bool;
177224
}
178225
}
179226
}

0 commit comments

Comments
 (0)