diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-07-29 16:08:36 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-29 16:08:36 +0200 |
commit | 0b1226871a26f136739f29050de52088b2aa1c3e (patch) | |
tree | 1124abd73a6cf58d45e17cc0fb075a4076ca4320 /server/sonar-web/src/main/js | |
parent | 3e3e4b1e16c0e5ebf8a485d3014cb7bd2577e192 (diff) | |
download | sonarqube-0b1226871a26f136739f29050de52088b2aa1c3e.tar.gz sonarqube-0b1226871a26f136739f29050de52088b2aa1c3e.zip |
SONAR-7920 Rewrite Links project page (#1127)
Diffstat (limited to 'server/sonar-web/src/main/js')
15 files changed, 681 insertions, 1 deletions
diff --git a/server/sonar-web/src/main/js/api/projectLinks.js b/server/sonar-web/src/main/js/api/projectLinks.js new file mode 100644 index 00000000000..06e1eaa7d26 --- /dev/null +++ b/server/sonar-web/src/main/js/api/projectLinks.js @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { getJSON, post, postJSON } from '../helpers/request'; + +export function getProjectLinks (projectKey) { + const url = '/api/project_links/search'; + const data = { projectKey }; + return getJSON(url, data).then(r => r.links); +} + +export function deleteLink (linkId) { + const url = '/api/project_links/delete'; + const data = { id: linkId }; + return post(url, data); +} + +export function createLink (projectKey, name, url) { + const apiURL = '/api/project_links/create'; + const data = { projectKey, name, url }; + return postJSON(apiURL, data).then(r => r.link); +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/app.js b/server/sonar-web/src/main/js/apps/project-admin/app.js index 7bcb91f9e1c..85fb770962f 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/app.js +++ b/server/sonar-web/src/main/js/apps/project-admin/app.js @@ -24,6 +24,7 @@ import { Router, Route, useRouterHistory } from 'react-router'; import { createHistory } from 'history'; import Deletion from './deletion/Deletion'; import QualityProfiles from './quality-profiles/QualityProfiles'; +import Links from './links/Links'; import rootReducer from './store/rootReducer'; import configureStore from '../../components/store/configureStore'; @@ -48,6 +49,9 @@ window.sonarqube.appStarted.then(options => { <Route path="/quality_profiles" component={withComponent(QualityProfiles)}/> + <Route + path="/links" + component={withComponent(Links)}/> </Router> </Provider> ), el); diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Header.js b/server/sonar-web/src/main/js/apps/project-admin/links/Header.js new file mode 100644 index 00000000000..e6471ca9fb6 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Header.js @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import CreationModal from './views/CreationModal'; +import { translate } from '../../../helpers/l10n'; + +export default class Header extends React.Component { + static propTypes = { + onCreate: React.PropTypes.func.isRequired + }; + + handleCreateClick (e) { + e.preventDefault(); + e.target.blur(); + new CreationModal({ + onCreate: this.props.onCreate + }).render(); + } + + render () { + return ( + <header className="page-header"> + <h1 className="page-title"> + {translate('project_links.page')} + </h1> + <div className="page-actions"> + <button + id="create-project-link" + onClick={this.handleCreateClick.bind(this)}> + {translate('create')} + </button> + </div> + <div className="page-description"> + {translate('project_links.page.description')} + </div> + </header> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.js b/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.js new file mode 100644 index 00000000000..e55651dce89 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.js @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import { isProvided, isClickable } from './utils'; +import { translate } from '../../../helpers/l10n'; + +export default class LinkRow extends React.Component { + static propTypes = { + link: React.PropTypes.object.isRequired, + onDelete: React.PropTypes.func.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + handleDeleteClick (e) { + e.preventDefault(); + e.target.blur(); + this.props.onDelete(); + } + + renderIcon (iconClassName) { + return ( + <div className="display-inline-block text-top spacer-right"> + <i className={iconClassName}/> + </div> + ); + } + + renderNameForProvided (link) { + return ( + <div> + {this.renderIcon(`icon-${link.type}`)} + <div className="display-inline-block text-top"> + <div> + <span className="js-name">{link.name}</span> + </div> + <div className="note little-spacer-top"> + <span className="js-type">{`sonar.links.${link.type}`}</span> + </div> + </div> + </div> + ); + } + + renderName (link) { + if (isProvided(link)) { + return this.renderNameForProvided(link); + } + + return ( + <div> + {this.renderIcon('icon-detach')} + <div className="display-inline-block text-top"> + <span className="js-name">{link.name}</span> + </div> + </div> + ); + } + + renderUrl (link) { + if (isClickable(link)) { + return <a href={link.url} target="_blank">{link.url}</a>; + } + + return link.url; + } + + renderDeleteButton (link) { + if (isProvided(link)) { + return null; + } + + return ( + <button + className="button-red js-delete-button" + onClick={this.handleDeleteClick.bind(this)}> + {translate('delete')} + </button> + ); + } + + render () { + const { link } = this.props; + + return ( + <tr data-name={link.name}> + <td className="nowrap">{this.renderName(link)}</td> + <td className="nowrap js-url">{this.renderUrl(link)}</td> + <td className="thin nowrap">{this.renderDeleteButton(link)}</td> + </tr> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Links.js b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js new file mode 100644 index 00000000000..88602532ea9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import { connect } from 'react-redux'; +import Header from './Header'; +import Table from './Table'; +import DeletionModal from './views/DeletionModal'; +import { getProjectLinks } from '../store/rootReducer'; +import { + fetchProjectLinks, + deleteProjectLink, + createProjectLink +} from '../store/actions'; + +class Links extends React.Component { + static propTypes = { + component: React.PropTypes.object.isRequired, + links: React.PropTypes.array + }; + + componentWillMount () { + this.handleCreateLink = this.handleCreateLink.bind(this); + this.handleDeleteLink = this.handleDeleteLink.bind(this); + } + + componentDidMount () { + this.props.fetchProjectLinks(this.props.component.key); + } + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + handleCreateLink (name, url) { + return this.props.createProjectLink(this.props.component.key, name, url); + } + + handleDeleteLink (link) { + new DeletionModal({ link }).on('done', () => { + this.props.deleteProjectLink(this.props.component.key, link.id); + }).render(); + } + + render () { + return ( + <div className="page page-limited"> + <Header + onCreate={this.handleCreateLink}/> + <Table + links={this.props.links} + onDelete={this.handleDeleteLink}/> + </div> + ); + } +} + +const mapStateToProps = (state, ownProps) => ({ + links: getProjectLinks(state, ownProps.component.key) +}); + +export default connect( + mapStateToProps, + { fetchProjectLinks, createProjectLink, deleteProjectLink } +)(Links); diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Table.js b/server/sonar-web/src/main/js/apps/project-admin/links/Table.js new file mode 100644 index 00000000000..95f37f6841c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Table.js @@ -0,0 +1,74 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import LinkRow from './LinkRow'; +import { orderLinks } from './utils'; +import { translate } from '../../../helpers/l10n'; + +export default class Table extends React.Component { + static propTypes = { + links: React.PropTypes.array.isRequired, + onDelete: React.PropTypes.func.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + handleDeleteLink (link) { + this.props.onDelete(link); + } + + renderHeader () { + // keep empty cell for actions + return ( + <thead> + <tr> + <th className="nowrap"> + {translate('project_links.name')} + </th> + <th className="nowrap width-100"> + {translate('project_links.url')} + </th> + <th className="thin"> </th> + </tr> + </thead> + ); + } + + render () { + const orderedLinks = orderLinks(this.props.links); + + const linkRows = orderedLinks.map(link => ( + <LinkRow + key={link.id} + link={link} + onDelete={this.handleDeleteLink.bind(this, link)}/> + )); + + return ( + <table id="project-links" className="data zebra"> + {this.renderHeader()} + <tbody>{linkRows}</tbody> + </table> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/utils.js b/server/sonar-web/src/main/js/apps/project-admin/links/utils.js new file mode 100644 index 00000000000..9a34509d38b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/utils.js @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import partition from 'lodash/partition'; +import sortBy from 'lodash/sortBy'; + +const PROVIDED_TYPES = [ + 'homepage', + 'ci', + 'issue', + 'scm', + 'scm_dev' +]; + +export function isProvided (link) { + return PROVIDED_TYPES.includes(link.type); +} + +export function orderLinks (links) { + const [provided, unknown] = partition(links, isProvided); + return [ + ...sortBy(provided, link => PROVIDED_TYPES.indexOf(link.type)), + ...sortBy(unknown, link => link.name) + ]; +} + +export function isClickable (link) { + // stupid simple check + return link.url.indexOf('http') === 0; +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModal.js b/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModal.js new file mode 100644 index 00000000000..a2fa0e36358 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModal.js @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import ModalForm from '../../../../components/common/modal-form'; +import Template from './CreationModalTemplate.hbs'; + +export default ModalForm.extend({ + template: Template, + + onFormSubmit () { + ModalForm.prototype.onFormSubmit.apply(this, arguments); + this.disableForm(); + + const name = this.$('#create-link-name').val(); + const url = this.$('#create-link-url').val(); + + this.options.onCreate(name, url) + .then(() => { + this.destroy(); + }) + .catch(function (e) { + e.response.json().then(r => { + this.showErrors(r.errors, r.warnings); + this.enableForm(); + }); + }); + } +}); + diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModalTemplate.hbs b/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModalTemplate.hbs new file mode 100644 index 00000000000..7405f30d1b4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModalTemplate.hbs @@ -0,0 +1,22 @@ +<form> + <div class="modal-head"> + <h2>{{t 'project_links.create_new_project_link'}}</h2> + </div> + <div class="modal-body"> + <div class="js-modal-messages"></div> + + <div class="modal-field"> + <label for="create-link-name">{{t 'project_links.name'}}<em class="mandatory">*</em></label> + <input id="create-link-name" name="name" type="text" required> + </div> + + <div class="modal-field"> + <label for="create-link-url">{{t 'project_links.url'}}<em class="mandatory">*</em></label> + <input id="create-link-url" name="url" type="text" required> + </div> + </div> + <div class="modal-foot"> + <button id="create-link-confirm">{{t 'create'}}</button> + <a href="#" class="js-modal-close">{{t 'cancel'}}</a> + </div> +</form> diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModal.js b/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModal.js new file mode 100644 index 00000000000..a1064dfb66e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModal.js @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import ModalForm from '../../../../components/common/modal-form'; +import Template from './DeletionModalTemplate.hbs'; +import { deleteLink } from '../../../../api/projectLinks'; + +export default ModalForm.extend({ + template: Template, + + onFormSubmit () { + ModalForm.prototype.onFormSubmit.apply(this, arguments); + this.disableForm(); + + deleteLink(this.options.link.id) + .then(() => { + this.trigger('done'); + this.destroy(); + }) + .catch(function (e) { + e.response.json().then(r => { + this.showErrors(r.errors, r.warnings); + this.enableForm(); + }); + }); + }, + + serializeData () { + return { link: this.options.link }; + } +}); + diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModalTemplate.hbs b/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModalTemplate.hbs new file mode 100644 index 00000000000..b8d744c83d9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModalTemplate.hbs @@ -0,0 +1,13 @@ +<form> + <div class="modal-head"> + <h2>{{t 'project_links.delete_project_link'}}</h2> + </div> + <div class="modal-body"> + <div class="js-modal-messages"></div> + {{tp 'project_links.are_you_sure_to_delete_x_link' link.name}} + </div> + <div class="modal-foot"> + <button id="delete-link-confirm" class="button-red">{{t 'delete'}}</button> + <a href="#" class="js-modal-close">{{t 'cancel'}}</a> + </div> +</form> diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js index cad25a70e7a..083a28bfa92 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js +++ b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js @@ -23,6 +23,7 @@ import { dissociateProject } from '../../../api/quality-profiles'; import { getProfileByKey } from './rootReducer'; +import { getProjectLinks, createLink } from '../../../api/projectLinks'; export const RECEIVE_PROFILES = 'RECEIVE_PROFILES'; export const receiveProfiles = profiles => ({ @@ -68,3 +69,36 @@ export const setProjectProfile = (projectKey, oldKey, newKey) => dispatch(setProjectProfileAction(projectKey, oldKey, newKey)); }); }; + +export const RECEIVE_PROJECT_LINKS = 'RECEIVE_PROJECT_LINKS'; +export const receiveProjectLinks = (projectKey, links) => ({ + type: RECEIVE_PROJECT_LINKS, + projectKey, + links +}); + +export const fetchProjectLinks = projectKey => dispatch => { + getProjectLinks(projectKey).then(links => { + dispatch(receiveProjectLinks(projectKey, links)); + }); +}; + +export const ADD_PROJECT_LINK = 'ADD_PROJECT_LINK'; +const addProjectLink = (projectKey, link) => ({ + type: ADD_PROJECT_LINK, + projectKey, + link +}); + +export const createProjectLink = (projectKey, name, url) => dispatch => { + return createLink(projectKey, name, url).then(link => { + dispatch(addProjectLink(projectKey, link)); + }); +}; + +export const DELETE_PROJECT_LINK = 'DELETE_PROJECT_LINK'; +export const deleteProjectLink = (projectKey, linkId) => ({ + type: DELETE_PROJECT_LINK, + projectKey, + linkId +}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/links.js b/server/sonar-web/src/main/js/apps/project-admin/store/links.js new file mode 100644 index 00000000000..9a79d3d7905 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/store/links.js @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import keyBy from 'lodash/keyBy'; +import omit from 'lodash/omit'; +import { RECEIVE_PROJECT_LINKS, DELETE_PROJECT_LINK } from './actions'; +import { ADD_PROJECT_LINK } from './actions'; + +const links = (state = {}, action = {}) => { + if (action.type === RECEIVE_PROJECT_LINKS) { + const newLinksById = keyBy(action.links, 'id'); + return { ...state, ...newLinksById }; + } + + if (action.type === ADD_PROJECT_LINK) { + return { ...state, [action.link.id]: action.link }; + } + + if (action.type === DELETE_PROJECT_LINK) { + return omit(state, action.linkId); + } + + return state; +}; + +export default links; + +export const getLink = (state, id) => + state[id]; diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/linksByProject.js b/server/sonar-web/src/main/js/apps/project-admin/store/linksByProject.js new file mode 100644 index 00000000000..dd48ff45f98 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/store/linksByProject.js @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import without from 'lodash/without'; +import { + RECEIVE_PROJECT_LINKS, + DELETE_PROJECT_LINK, + ADD_PROJECT_LINK +} from './actions'; + +const linksByProject = (state = {}, action = {}) => { + if (action.type === RECEIVE_PROJECT_LINKS) { + const linkIds = action.links.map(link => link.id); + return { ...state, [action.projectKey]: linkIds }; + } + + if (action.type === ADD_PROJECT_LINK) { + const byProject = state[action.projectKey] || []; + const ids = [...byProject, action.link.id]; + return { ...state, [action.projectKey]: ids }; + } + + if (action.type === DELETE_PROJECT_LINK) { + const ids = without(state[action.projectKey], action.linkId); + return { ...state, [action.projectKey]: ids }; + } + + return state; +}; + +export default linksByProject; + +export const getLinks = (state, projectKey) => state[projectKey] || []; diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js b/server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js index 5f5dc3d7899..e8174f53aa0 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js +++ b/server/sonar-web/src/main/js/apps/project-admin/store/rootReducer.js @@ -23,10 +23,14 @@ import profiles, { getAllProfiles as nextGetAllProfiles } from './profiles'; import profilesByProject, { getProfiles } from './profilesByProject'; +import links, { getLink } from './links'; +import linksByProject, { getLinks } from './linksByProject'; const rootReducer = combineReducers({ profiles, - profilesByProject + profilesByProject, + links, + linksByProject }); export default rootReducer; @@ -40,3 +44,10 @@ export const getAllProfiles = state => export const getProjectProfiles = (state, projectKey) => getProfiles(state.profilesByProject, projectKey) .map(profileKey => getProfileByKey(state, profileKey)); + +export const getLinkById = (state, linkId) => + getLink(state.links, linkId); + +export const getProjectLinks = (state, projectKey) => + getLinks(state.linksByProject, projectKey) + .map(linkId => getLinkById(state, linkId)); |