-
-
+ :class="{ 'is-invalid': teamsState === false }"
+ selectLabel=""
+ deselectLabel=""
+ >
+
+ {{ $t('message.required_team') }}
-
-
-
-
- (this.tags = newTags)"
- class="mw-100 bg-transparent text-lowercase"
- />
-
-
-
-
- {{ $t('message.identity') }}
+
+
+
+
+
-
-
-
+
+ (this.tags = newTags)"
+ class="mw-100 bg-transparent text-lowercase"
/>
+
+
+
+
-
-
-
-
+
+
+
+
- {{
- $t('message.close')
- }}
- {{
- $t('message.create')
- }}
+ {{ $t('message.cancel') }}
+
+
+ {{ $t('message.create') }}
+
@@ -257,13 +278,17 @@
import BInputGroupFormInput from '../../../forms/BInputGroupFormInput';
import BInputGroupFormSelect from '../../../forms/BInputGroupFormSelect';
import VueTagsInput from '@johmun/vue-tags-input';
-import { Switch as cSwitch } from '@coreui/vue';
import permissionsMixin from '../../../mixins/permissionsMixin';
import Multiselect from 'vue-multiselect';
import BInputGroupFormSwitch from '@/forms/BInputGroupFormSwitch.vue';
import common from '../../../shared/common';
import availableClassifiersMixin from '@/mixins/availableClassifiersMixin';
import availableCollectionLogicsMixin from '@/mixins/availableCollectionLogicsMixin';
+import projectFormMixin, {
+ COLLECTION_LOGIC_AGGREGATE_TAG,
+ DEFAULT_CLASSIFIER,
+ FORM_FIELD_DEFAULTS,
+} from './projectFormMixin';
export default {
name: 'ProjectCreateProjectModal',
@@ -271,70 +296,65 @@ export default {
permissionsMixin,
availableClassifiersMixin,
availableCollectionLogicsMixin,
+ projectFormMixin,
],
components: {
BInputGroupFormSwitch,
BInputGroupFormInput,
BInputGroupFormSelect,
VueTagsInput,
- cSwitch,
Multiselect,
},
data() {
return {
requiresTeam: true,
isDisabled: false,
- readOnlyProjectName: '',
- readOnlyProjectVersion: '',
availableTeams: [],
- selectableLicenses: [],
- selectedLicense: '',
- selectedParent: null,
- availableParents: [],
- project: { team: [] },
+ selectedTeams: [],
+ project: { ...FORM_FIELD_DEFAULTS, classifier: DEFAULT_CLASSIFIER },
teams: [],
- tag: '', // The contents of a tag as its being typed into the vue-tag-input
- tags: [], // An array of tags bound to the vue-tag-input
- tagsAutoCompleteItems: [],
- tagsAutoCompleteDebounce: null,
- collectionTagTyping: '', // The contents of a collection tag as its being typed into the vue-tag-input
- collectionTags: [], // An array of tags bound to the vue-tag-input for collection tag
- isCollection: false,
- addOnKeys: [9, 13, 32, ':', ';', ','], // Separators used when typing tags into the vue-tag-input
- isLoading: false,
+ showIdentity: false,
+ isCreating: false,
};
},
- created() {
- this.getACLEnabled().then(() => {
- this.getAvailableTeams();
- });
- },
- beforeUpdate() {
- if (this.tags.length === 0 && this.project && this.project.tags) {
- // Prevents line from being executed when entering new tags
- this.project.tags.forEach((tag) => this.tags.push({ text: tag.name }));
- }
- this.readOnlyProjectName = this.project.name;
- this.readOnlyProjectVersion = this.project.version;
- },
beforeMount() {
this.$root.$on('initializeProjectCreateProjectModal', async () => {
this.resetValues();
await this.getACLEnabled();
await this.getAvailableTeams();
- await this.retrieveLicenses();
+ this.fetchDefaultParents();
this.$root.$emit('bv::show::modal', 'projectCreateProjectModal');
});
},
- watch: {
- tag(input) {
- this.searchTags(input);
+ beforeDestroy() {
+ this.$root.$off('initializeProjectCreateProjectModal');
+ },
+ computed: {
+ teamsState() {
+ if (!this.requiresTeam || !this.submitted) return undefined;
+ return this.selectedTeams.length > 0;
},
- collectionTagTyping(input) {
- this.searchTags(input);
+ identityHint() {
+ const fields = [
+ this.project.group,
+ this.project.purl,
+ this.project.cpe,
+ this.project.swidTagId,
+ ].filter(Boolean);
+ if (fields.length > 0) {
+ return this.$t('message.identity_fields_set', {
+ count: fields.length,
+ });
+ }
+ return this.$t('message.optional');
},
},
methods: {
+ validate() {
+ if (!projectFormMixin.methods.validate.call(this)) return false;
+ if (this.requiresTeam && this.selectedTeams.length === 0) return false;
+ return true;
+ },
async getACLEnabled() {
let url = `${this.$api.BASE_URL}/${this.$api.URL_CONFIG_PROPERTY}/public/access-management/acl.enabled`;
let response = await this.axios.get(url);
@@ -345,13 +365,12 @@ export default {
async getAvailableTeams() {
let url = `${this.$api.BASE_URL}/${this.$api.URL_TEAM}/visible`;
let response = await this.axios.get(url);
- let convertedTeams = response.data.map((team) => {
+ this.availableTeams = response.data.map((team) => {
return { text: team.name, value: team.uuid };
});
- this.availableTeams = convertedTeams;
this.teams = response.data;
- if (this.requiresTeam && this.availableTeams.length == 1) {
- this.project.team = this.availableTeams[0].value;
+ if (this.requiresTeam && this.availableTeams.length === 1) {
+ this.selectedTeams = [this.availableTeams[0]];
this.isDisabled = true;
} else {
this.isDisabled = false;
@@ -360,48 +379,50 @@ export default {
return a.text.localeCompare(b.text);
});
},
- onCollectionToggle: function (value) {
+ onCollectionToggle(value) {
if (value) {
- this.project.classifier = undefined;
+ this.project.classifier = null;
} else {
+ this.project.classifier = DEFAULT_CLASSIFIER;
this.project.collectionLogic = null;
this.collectionTagTyping = '';
this.collectionTags = [];
}
},
- createProject: function () {
- let url = `${this.$api.BASE_URL}/${this.$api.URL_PROJECT}`;
- let tagsNode = [];
- let choosenTeams = this.teams.filter((team) => {
- return (this.project.team || []).includes(team.uuid);
- });
- let choosenTeamswithoutAPIKeys = choosenTeams.map((team) => {
- team.apiKeys = [];
- return team;
- });
- let parent = null;
- if (this.selectedParent) {
- parent = { uuid: this.selectedParent.uuid };
+ createProject() {
+ this.resetValidationFeedback();
+ if (!this.validate()) {
+ this.scrollToFirstError();
+ return;
}
- this.tags.forEach((tag) => tagsNode.push({ name: tag.text }));
+
+ this.isCreating = true;
+ const url = `${this.$api.BASE_URL}/${this.$api.URL_PROJECT}`;
+ const chosenTeams = this.teams
+ .filter((team) =>
+ this.selectedTeams.some((st) => st.value === team.uuid),
+ )
+ .map((team) => ({ ...team, apiKeys: [] }));
+ const parent = this.selectedParent
+ ? { uuid: this.selectedParent.uuid }
+ : null;
+ const tagsNode = this.tags.map((tag) => ({ name: tag.text }));
+ const collectionTag =
+ this.project.collectionLogic === COLLECTION_LOGIC_AGGREGATE_TAG &&
+ this.collectionTags.length > 0
+ ? { name: this.collectionTags[0].text }
+ : null;
this.axios
.put(url, {
name: this.project.name,
version: this.project.version,
group: this.project.group,
description: this.project.description,
- //license: this.selectedLicense,
- parent: parent,
+ parent,
classifier: this.project.classifier,
- accessTeams: choosenTeamswithoutAPIKeys,
+ accessTeams: chosenTeams,
collectionLogic: this.project.collectionLogic,
- collectionTag:
- this.project.collectionLogic ===
- 'AGGREGATE_DIRECT_CHILDREN_WITH_TAG' &&
- this.collectionTags &&
- this.collectionTags.length > 0
- ? { name: this.collectionTags[0].text }
- : null,
+ collectionTag,
purl: this.project.purl,
cpe: this.project.cpe,
swidTagId: this.project.swidTagId,
@@ -411,102 +432,43 @@ export default {
isLatest: this.project.isLatest,
})
.then((response) => {
- this.$emit('refreshTable');
+ this.isCreating = false;
this.$toastr.s(this.$t('message.project_created'));
- this.selectedParent = null;
- this.availableParents = [{ value: null, text: '' }];
+ this.$root.$emit('bv::hide::modal', 'projectCreateProjectModal');
+ this.$router.push({ path: '/projects/' + response.data.uuid });
})
.catch((error) => {
- this.$toastr.w(this.$t('condition.unsuccessful_action'));
- })
- .finally(() => {
- this.$root.$emit('bv::hide::modal', 'projectCreateProjectModal');
+ this.isCreating = false;
+ if (this.applyValidationErrors(error)) {
+ this.showIdentity = true;
+ }
});
},
- retrieveLicenses: function () {
- return new Promise((resolve) => {
- let url = `${this.$api.BASE_URL}/${this.$api.URL_LICENSE_CONCISE}`;
- this.axios
- .get(url)
- .then((response) => {
- for (let i = 0; i < response.data.length; i++) {
- let license = response.data[i];
- this.selectableLicenses.push({
- value: license.licenseId,
- text: license.name,
- });
- if (
- this.project.resolvedLicense &&
- this.project.resolvedLicense.uuid === license.uuid
- ) {
- this.selectedLicense = license.licenseId;
- }
- }
- })
- .catch((error) => {
- this.$toastr.w(this.$t('condition.unsuccessful_action'));
- })
- .finally(() => {
- resolve();
- });
- });
+ parentSearchUrl(searchText) {
+ return common.setQueryParams(
+ `${this.$api.BASE_URL}/${this.$api.URL_PROJECT_CONCISE}`,
+ {
+ excludeInactive: true,
+ pageSize: 10,
+ pageNumber: 1,
+ searchText: searchText || null,
+ },
+ );
},
- resetValues: function () {
- this.project = {
- collectionLogic: null,
- team: [],
- };
+ resetValues() {
+ this.project = { ...FORM_FIELD_DEFAULTS, classifier: DEFAULT_CLASSIFIER };
this.isDisabled = false;
- this.tag = '';
this.tags = [];
+ this.collectionTags = [];
this.selectedParent = null;
- this.availableParents = [];
+ this.selectedTeams = [];
+ this.availableParents = this.defaultParents;
this.isCollection = false;
- this.collectionTagTyping = '';
- this.collectionTags = [];
- },
- createProjectLabel: function (project) {
- if (project.version) {
- return project.name + ' : ' + project.version;
- } else {
- return project.name;
- }
- },
- asyncFind: function (query) {
- if (query) {
- this.isLoading = true;
- let url = `${this.$api.BASE_URL}/${this.$api.URL_PROJECT}?searchText=${query}&excludeInactive=true`;
- this.axios.get(url).then((response) => {
- if (response.data) {
- this.availableParents = response.data;
- } else {
- this.availableParents = [];
- }
- this.isLoading = false;
- });
- }
- },
- searchTags: function (input) {
- clearTimeout(this.tagsAutoCompleteDebounce);
- if (!input) {
- this.tagsAutoCompleteItems = [];
- return;
- }
- this.tagsAutoCompleteDebounce = setTimeout(() => {
- const url = `${this.$api.BASE_URL}/${this.$api.URL_TAG}?searchText=${encodeURIComponent(input)}&pageNumber=1&pageSize=6`;
- this.axios.get(url).then((response) => {
- this.tagsAutoCompleteItems = response.data.map((tag) => {
- return { text: tag.name };
- });
- });
- }, 250);
- },
- },
- computed: {
- showCollectionTags() {
- return (
- this.project.collectionLogic === 'AGGREGATE_DIRECT_CHILDREN_WITH_TAG'
- );
+ this.showIdentity = false;
+ this.isCreating = false;
+ this.submitted = false;
+ this.resetValidationFeedback();
+ this.resetTagInputs();
},
},
};
@@ -515,17 +477,3 @@ export default {
-
-
diff --git a/src/views/portfolio/projects/ProjectDetailsModal.vue b/src/views/portfolio/projects/ProjectDetailsModal.vue
index a872f71f..e8942c83 100644
--- a/src/views/portfolio/projects/ProjectDetailsModal.vue
+++ b/src/views/portfolio/projects/ProjectDetailsModal.vue
@@ -5,49 +5,161 @@
hide-header-close
no-stacking
:title="$t('message.project_details')"
- @show="initializeTags"
+ @show="onShow"
@hide="resetValues()"
>
-
-
- {{ $t('message.general') }}
-
-
-
-
-
+
+
+ {{ $t('message.general') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ (this.collectionTags = newCollectionTags)
+ "
+ class="mw-100 bg-transparent text-lowercase"
+ :max-tags="1"
:readonly="
this.isNotPermitted([
PERMISSIONS.PORTFOLIO_MANAGEMENT,
@@ -55,13 +167,60 @@
])
"
/>
-
-
-
+
+
+
+
+
+
+
+
+ (this.tags = newTags)"
+ class="mw-100 bg-transparent text-lowercase"
:readonly="
this.isNotPermitted([
PERMISSIONS.PORTFOLIO_MANAGEMENT,
@@ -69,74 +228,54 @@
])
"
/>
-
-
-
-
- (this.collectionTags = newCollectionTags)
- "
- class="mw-100 bg-transparent text-lowercase"
- :max-tags="1"
- v-show="showCollectionTags"
- :readonly="this.isNotPermitted(PERMISSIONS.PORTFOLIO_MANAGEMENT)"
- />
-
-
-
-
-
+
+
+
+
+
+
+ {{ $t('message.identity') }}
-
+
-
-
- (this.tags = newTags)"
- class="mw-100 bg-transparent text-lowercase"
+
-
-
-
-
-
-
-
- {{ $t('message.identity') }}
-
-
-
-
-
-
-
-
-
-
-
- {{ $t('message.manufacturer') }}
+
+
+
+
-
-
-
+ {{ $t('message.manufacturer') }}
-
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
- {{ $t('message.supplier') }}
+
+
+
+
+
-
-
-
+ {{ $t('message.supplier') }}
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {{ $t('message.external_references') }}
+
-
-
-
-
-
- {{ $t('message.external_references') }}
+
+
-
- {{ $t('message.bom') }}
-
-
-
-
- {{ $t('message.bom') }}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
{{ $t('message.add_version') }}
- {{
- $t('message.close')
- }}
+ {{ $t('message.close') }}
{{ $t('message.update') }}
+
+ {{ $t('message.update') }}
+