@@ -19,17 +19,46 @@ | |||
*/ | |||
import { getJSON, postJSON, post, RequestData } from '../helpers/request'; | |||
import throwGlobalError from '../app/utils/throwGlobalError'; | |||
import { Paging, Visibility } from '../app/types'; | |||
export function getComponents(data: RequestData): Promise<any> { | |||
return getJSON('/api/projects/search', data); | |||
export interface BaseSearchProjectsParameters { | |||
analyzedBefore?: string; | |||
onProvisionedOnly?: boolean; | |||
organization: string; | |||
projects?: string; | |||
q?: string; | |||
qualifiers?: string; | |||
visibility?: Visibility; | |||
} | |||
export interface SearchProjectsParameters extends BaseSearchProjectsParameters { | |||
p?: number; | |||
ps?: number; | |||
} | |||
export interface SearchProjectsResponseComponent { | |||
id: string; | |||
key: string; | |||
lastAnalysisDate?: string; | |||
name: string; | |||
organization: string; | |||
qualifier: string; | |||
visibility: Visibility; | |||
} | |||
export interface SearchProjectsResponse { | |||
components: SearchProjectsResponseComponent[]; | |||
paging: Paging; | |||
} | |||
export function getProvisioned(data: RequestData): Promise<any> { | |||
return getJSON('/api/projects/provisioned', data); | |||
export function getComponents( | |||
parameters: SearchProjectsParameters | |||
): Promise<SearchProjectsResponse> { | |||
return getJSON('/api/projects/search', parameters); | |||
} | |||
export function deleteComponents(projects: string[], organization: string): Promise<void> { | |||
return post('/api/projects/bulk_delete', { projects: projects.join(), organization }); | |||
export function bulkDeleteProjects(parameters: BaseSearchProjectsParameters): Promise<void> { | |||
return post('/api/projects/bulk_delete', parameters); | |||
} | |||
export function deleteProject(project: string): Promise<void> { |
@@ -18,6 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { getJSON, post, postJSON, RequestData } from '../helpers/request'; | |||
import { BaseSearchProjectsParameters } from './components'; | |||
const PAGE_SIZE = 100; | |||
@@ -136,7 +137,7 @@ export function applyTemplateToProject(data: RequestData): Promise<void> { | |||
return post('/api/permissions/apply_template', data); | |||
} | |||
export function bulkApplyTemplate(data: RequestData): Promise<void> { | |||
export function bulkApplyTemplate(data: BaseSearchProjectsParameters): Promise<void> { | |||
return post('/api/permissions/bulk_apply_template', data); | |||
} | |||
@@ -118,3 +118,14 @@ export interface Organization { | |||
projectVisibility: string; | |||
url?: string; | |||
} | |||
export interface Paging { | |||
pageIndex: number; | |||
pageSize: number; | |||
total: number; | |||
} | |||
export enum Visibility { | |||
Public = 'public', | |||
Private = 'private' | |||
} |
@@ -26,7 +26,7 @@ import Projects from './Projects'; | |||
import CreateProjectForm from './CreateProjectForm'; | |||
import ListFooter from '../../components/controls/ListFooter'; | |||
import { PAGE_SIZE, Project } from './utils'; | |||
import { getComponents, getProvisioned } from '../../api/components'; | |||
import { getComponents } from '../../api/components'; | |||
import { Organization } from '../../app/types'; | |||
import { translate } from '../../helpers/l10n'; | |||
@@ -78,38 +78,17 @@ export default class App extends React.PureComponent<Props, State> { | |||
this.mounted = false; | |||
} | |||
getFilters = () => ({ | |||
analyzedBefore: this.state.analyzedBefore, | |||
organization: this.props.organization.key, | |||
p: this.state.page !== 1 ? this.state.page : undefined, | |||
ps: PAGE_SIZE, | |||
q: this.state.query ? this.state.query : undefined | |||
}); | |||
requestProjects = () => | |||
this.state.provisioned ? this.requestProvisioned() : this.requestAllProjects(); | |||
requestProvisioned = () => { | |||
const data = this.getFilters(); | |||
getProvisioned(data).then(r => { | |||
if (this.mounted) { | |||
let projects: Project[] = r.projects.map((project: any) => ({ | |||
...project, | |||
id: project.uuid, | |||
qualifier: 'TRK' | |||
})); | |||
if (this.state.page > 1) { | |||
projects = [...this.state.projects, ...projects]; | |||
} | |||
this.setState({ ready: true, projects, selection: [], total: r.paging.total }); | |||
} | |||
}); | |||
}; | |||
requestAllProjects = () => { | |||
const data = this.getFilters(); | |||
Object.assign(data, { qualifiers: this.state.qualifiers }); | |||
getComponents(data).then(r => { | |||
requestProjects = () => { | |||
const parameters = { | |||
analyzedBefore: this.state.analyzedBefore, | |||
onProvisionedOnly: this.state.provisioned || undefined, | |||
organization: this.props.organization.key, | |||
p: this.state.page !== 1 ? this.state.page : undefined, | |||
ps: PAGE_SIZE, | |||
q: this.state.query || undefined, | |||
qualifiers: this.state.qualifiers | |||
}; | |||
getComponents(parameters).then(r => { | |||
if (this.mounted) { | |||
let projects: Project[] = r.components; | |||
if (this.state.page > 1) { |
@@ -22,13 +22,14 @@ import Modal from 'react-modal'; | |||
import * as Select from 'react-select'; | |||
import { | |||
getPermissionTemplates, | |||
PermissionTemplate, | |||
bulkApplyTemplate, | |||
applyTemplateToProject | |||
PermissionTemplate | |||
} from '../../api/permissions'; | |||
import { translate, translateWithParameters } from '../../helpers/l10n'; | |||
import AlertWarnIcon from '../../components/icons-components/AlertWarnIcon'; | |||
export interface Props { | |||
analyzedBefore?: string; | |||
onClose: () => void; | |||
organization: string; | |||
provisioned: boolean; | |||
@@ -80,32 +81,6 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S | |||
); | |||
} | |||
bulkApplyToAll = (permissionTemplate: string) => { | |||
const data = { | |||
organization: this.props.organization, | |||
q: this.props.query ? this.props.query : undefined, | |||
qualifier: this.props.qualifier, | |||
templateId: permissionTemplate | |||
}; | |||
return bulkApplyTemplate(data); | |||
}; | |||
bulkApplyToSelected = (permissionTemplate: string) => { | |||
const { selection } = this.props; | |||
let lastRequest = Promise.resolve(); | |||
selection.forEach(projectKey => { | |||
const data = { | |||
organization: this.props.organization, | |||
projectKey, | |||
templateId: permissionTemplate | |||
}; | |||
lastRequest = lastRequest.then(() => applyTemplateToProject(data)); | |||
}); | |||
return lastRequest; | |||
}; | |||
handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => { | |||
event.preventDefault(); | |||
this.props.onClose(); | |||
@@ -115,10 +90,21 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S | |||
const { permissionTemplate } = this.state; | |||
if (permissionTemplate) { | |||
this.setState({ submitting: true }); | |||
const request = this.props.selection.length | |||
? this.bulkApplyToSelected(permissionTemplate) | |||
: this.bulkApplyToAll(permissionTemplate); | |||
request.then( | |||
const parameters = this.props.selection.length | |||
? { | |||
organization: this.props.organization, | |||
projects: this.props.selection.join(), | |||
templateId: permissionTemplate | |||
} | |||
: { | |||
analyzedBefore: this.props.analyzedBefore, | |||
onProvisionedOnly: this.props.provisioned || undefined, | |||
organization: this.props.organization, | |||
qualifiers: this.props.qualifier, | |||
q: this.props.query || undefined, | |||
templateId: permissionTemplate | |||
}; | |||
bulkApplyTemplate(parameters).then( | |||
() => { | |||
if (this.mounted) { | |||
this.setState({ done: true, submitting: false }); | |||
@@ -137,21 +123,19 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S | |||
this.setState({ permissionTemplate: value }); | |||
}; | |||
renderWarning = () => { | |||
return this.props.selection.length | |||
? <div className="alert alert-info"> | |||
{translateWithParameters( | |||
renderWarning = () => | |||
<div className="alert alert-warning modal-alert"> | |||
<AlertWarnIcon className="spacer-right" /> | |||
{this.props.selection.length | |||
? translateWithParameters( | |||
'permission_templates.bulk_apply_permission_template.apply_to_selected', | |||
this.props.selection.length | |||
)} | |||
</div> | |||
: <div className="alert alert-warning"> | |||
{translateWithParameters( | |||
) | |||
: translateWithParameters( | |||
'permission_templates.bulk_apply_permission_template.apply_to_all', | |||
this.props.total | |||
)} | |||
</div>; | |||
}; | |||
</div>; | |||
renderSelect = () => | |||
<div className="modal-field"> |
@@ -20,10 +20,9 @@ | |||
import * as React from 'react'; | |||
import Modal from 'react-modal'; | |||
import * as classNames from 'classnames'; | |||
import { Organization } from '../../app/types'; | |||
import { Organization, Visibility } from '../../app/types'; | |||
import UpgradeOrganizationBox from '../../components/common/UpgradeOrganizationBox'; | |||
import { translate } from '../../helpers/l10n'; | |||
import { Visibility } from './utils'; | |||
export interface Props { | |||
onClose: () => void; |
@@ -19,15 +19,20 @@ | |||
*/ | |||
import * as React from 'react'; | |||
import Modal from 'react-modal'; | |||
import { deleteComponents } from '../../api/components'; | |||
import { translate } from '../../helpers/l10n'; | |||
import { bulkDeleteProjects } from '../../api/components'; | |||
import { translate, translateWithParameters } from '../../helpers/l10n'; | |||
import AlertWarnIcon from '../../components/icons-components/AlertWarnIcon'; | |||
export interface Props { | |||
analyzedBefore?: string; | |||
onClose: () => void; | |||
onConfirm: () => void; | |||
organization: string; | |||
provisioned: boolean; | |||
qualifier: string; | |||
query: string; | |||
selection: string[]; | |||
total: number; | |||
} | |||
interface State { | |||
@@ -53,7 +58,19 @@ export default class DeleteModal extends React.PureComponent<Props, State> { | |||
handleConfirmClick = () => { | |||
this.setState({ loading: true }); | |||
deleteComponents(this.props.selection, this.props.organization).then( | |||
const parameters = this.props.selection.length | |||
? { | |||
organization: this.props.organization, | |||
projects: this.props.selection.join() | |||
} | |||
: { | |||
analyzedBefore: this.props.analyzedBefore, | |||
onProvisionedOnly: this.props.provisioned || undefined, | |||
organization: this.props.organization, | |||
qualifiers: this.props.qualifier, | |||
q: this.props.query || undefined | |||
}; | |||
bulkDeleteProjects(parameters).then( | |||
() => { | |||
if (this.mounted) { | |||
this.props.onConfirm(); | |||
@@ -67,6 +84,17 @@ export default class DeleteModal extends React.PureComponent<Props, State> { | |||
); | |||
}; | |||
renderWarning = () => | |||
<div className="alert alert-warning modal-alert"> | |||
<AlertWarnIcon className="spacer-right" /> | |||
{this.props.selection.length | |||
? translateWithParameters( | |||
'projects_management.delete_selected_warning', | |||
this.props.selection.length | |||
) | |||
: translateWithParameters('projects_management.delete_all_warning', this.props.total)} | |||
</div>; | |||
render() { | |||
const header = translate('qualifiers.delete', this.props.qualifier); | |||
@@ -84,6 +112,7 @@ export default class DeleteModal extends React.PureComponent<Props, State> { | |||
</header> | |||
<div className="modal-body"> | |||
{this.renderWarning()} | |||
{translate('qualifiers.delete_confirm', this.props.qualifier)} | |||
</div> | |||
@@ -19,8 +19,7 @@ | |||
*/ | |||
import * as React from 'react'; | |||
import ChangeVisibilityForm from './ChangeVisibilityForm'; | |||
import { Visibility } from './utils'; | |||
import { Organization } from '../../app/types'; | |||
import { Organization, Visibility } from '../../app/types'; | |||
import { translate } from '../../helpers/l10n'; | |||
export interface Props { |
@@ -19,7 +19,8 @@ | |||
*/ | |||
import * as React from 'react'; | |||
import { Link } from 'react-router'; | |||
import { Project, Visibility } from './utils'; | |||
import { Project } from './utils'; | |||
import { Visibility } from '../../app/types'; | |||
import PrivateBadge from '../../components/common/PrivateBadge'; | |||
import Checkbox from '../../components/controls/Checkbox'; | |||
import QualifierIcon from '../../components/shared/QualifierIcon'; |
@@ -186,7 +186,7 @@ export default class Search extends React.PureComponent<Props, State> { | |||
inputClassName="input-medium" | |||
name="analyzed-before" | |||
onChange={this.props.onDateChanged} | |||
placeholder={translate('analyzed_before')} | |||
placeholder={translate('last_analysis_before')} | |||
value={this.props.analyzedBefore} | |||
/> | |||
</td> | |||
@@ -194,7 +194,6 @@ export default class Search extends React.PureComponent<Props, State> { | |||
}; | |||
render() { | |||
const isSomethingSelected = this.props.projects.length > 0 && this.props.selection.length > 0; | |||
return ( | |||
<div className="big-spacer-bottom"> | |||
<table className="data"> | |||
@@ -224,13 +223,14 @@ export default class Search extends React.PureComponent<Props, State> { | |||
<td className="thin nowrap text-middle"> | |||
<button | |||
className="spacer-right js-bulk-apply-permission-template" | |||
disabled={this.props.total === 0} | |||
onClick={this.handleBulkApplyTemplateClick}> | |||
{translate('permission_templates.bulk_apply_permission_template')} | |||
</button> | |||
<button | |||
onClick={this.handleDeleteClick} | |||
className="js-delete button-red" | |||
disabled={!isSomethingSelected}> | |||
disabled={this.props.total === 0} | |||
onClick={this.handleDeleteClick}> | |||
{translate('delete')} | |||
</button> | |||
</td> | |||
@@ -240,6 +240,7 @@ export default class Search extends React.PureComponent<Props, State> { | |||
{this.state.bulkApplyTemplateModal && | |||
<BulkApplyTemplateModal | |||
analyzedBefore={this.props.analyzedBefore} | |||
onClose={this.closeBulkApplyTemplateModal} | |||
organization={this.props.organization.key} | |||
provisioned={this.props.provisioned} | |||
@@ -251,11 +252,15 @@ export default class Search extends React.PureComponent<Props, State> { | |||
{this.state.deleteModal && | |||
<DeleteModal | |||
analyzedBefore={this.props.analyzedBefore} | |||
onClose={this.closeDeleteModal} | |||
onConfirm={this.handleDeleteConfirm} | |||
organization={this.props.organization.key} | |||
provisioned={this.props.provisioned} | |||
qualifier={this.props.qualifiers} | |||
query={this.props.query} | |||
selection={this.props.selection} | |||
total={this.props.total} | |||
/>} | |||
</div> | |||
); |
@@ -30,17 +30,13 @@ jest.mock('rc-tooltip', () => ({ | |||
} | |||
})); | |||
jest.mock('../../../api/components', () => ({ | |||
getComponents: jest.fn(), | |||
getProvisioned: jest.fn(() => Promise.resolve({ paging: { total: 0 }, projects: [] })) | |||
})); | |||
jest.mock('../../../api/components', () => ({ getComponents: jest.fn() })); | |||
import * as React from 'react'; | |||
import { mount } from 'enzyme'; | |||
import App, { Props } from '../App'; | |||
const getComponents = require('../../../api/components').getComponents as jest.Mock<any>; | |||
const getProvisioned = require('../../../api/components').getProvisioned as jest.Mock<any>; | |||
const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; | |||
@@ -55,7 +51,6 @@ beforeEach(() => { | |||
getComponents | |||
.mockImplementation(() => Promise.resolve({ paging: { total: 0 }, components: [] })) | |||
.mockClear(); | |||
getProvisioned.mockClear(); | |||
}); | |||
it('fetches all projects on mount', () => { | |||
@@ -66,7 +61,11 @@ it('fetches all projects on mount', () => { | |||
it('selects provisioned', () => { | |||
const wrapper = mountRender(); | |||
wrapper.find('Search').prop<Function>('onProvisionedChanged')(true); | |||
expect(getProvisioned).lastCalledWith(defaultSearchParameters); | |||
expect(getComponents).lastCalledWith({ | |||
...defaultSearchParameters, | |||
onProvisionedOnly: true, | |||
qualifiers: 'TRK' | |||
}); | |||
}); | |||
it('changes qualifier and resets provisioned', () => { |
@@ -18,9 +18,8 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
jest.mock('../../../api/permissions', () => ({ | |||
applyTemplateToProject: jest.fn(), | |||
bulkApplyTemplate: jest.fn(), | |||
getPermissionTemplates: jest.fn() | |||
bulkApplyTemplate: jest.fn(() => Promise.resolve()), | |||
getPermissionTemplates: jest.fn(() => Promise.resolve({ permissionTemplates: [] })) | |||
})); | |||
import * as React from 'react'; | |||
@@ -28,18 +27,13 @@ import { mount, shallow } from 'enzyme'; | |||
import BulkApplyTemplateModal, { Props } from '../BulkApplyTemplateModal'; | |||
import { click } from '../../../helpers/testUtils'; | |||
const applyTemplateToProject = require('../../../api/permissions') | |||
.applyTemplateToProject as jest.Mock<any>; | |||
const bulkApplyTemplate = require('../../../api/permissions').bulkApplyTemplate as jest.Mock<any>; | |||
const getPermissionTemplates = require('../../../api/permissions') | |||
.getPermissionTemplates as jest.Mock<any>; | |||
beforeEach(() => { | |||
applyTemplateToProject.mockImplementation(() => Promise.resolve()).mockClear(); | |||
bulkApplyTemplate.mockImplementation(() => Promise.resolve()).mockClear(); | |||
getPermissionTemplates | |||
.mockImplementation(() => Promise.resolve({ permissionTemplates: [] })) | |||
.mockClear(); | |||
bulkApplyTemplate.mockClear(); | |||
getPermissionTemplates.mockClear(); | |||
}); | |||
it('fetches permission templates on mount', () => { | |||
@@ -61,9 +55,11 @@ it('bulk applies template to all results', async () => { | |||
click(wrapper.find('button')); | |||
expect(bulkApplyTemplate).toBeCalledWith({ | |||
analyzedBefore: '2017-04-08T00:00:00.000Z', | |||
onProvisionedOnly: true, | |||
organization: 'org', | |||
q: 'bla', | |||
qualifier: 'TRK', | |||
qualifiers: 'TRK', | |||
templateId: 'foo' | |||
}); | |||
expect(wrapper).toMatchSnapshot(); | |||
@@ -88,15 +84,9 @@ it('bulk applies template to selected results', async () => { | |||
click(wrapper.find('button')); | |||
expect(wrapper).toMatchSnapshot(); | |||
await new Promise(setImmediate); | |||
expect(applyTemplateToProject.mock.calls).toHaveLength(2); | |||
expect(applyTemplateToProject).toBeCalledWith({ | |||
organization: 'org', | |||
projectKey: 'proj1', | |||
templateId: 'foo' | |||
}); | |||
expect(applyTemplateToProject).toBeCalledWith({ | |||
expect(bulkApplyTemplate).toBeCalledWith({ | |||
organization: 'org', | |||
projectKey: 'proj2', | |||
projects: 'proj1,proj2', | |||
templateId: 'foo' | |||
}); | |||
@@ -114,6 +104,7 @@ it('closes', () => { | |||
function render(props?: { [P in keyof Props]?: Props[P] }) { | |||
return ( | |||
<BulkApplyTemplateModal | |||
analyzedBefore="2017-04-08T00:00:00.000Z" | |||
onClose={jest.fn()} | |||
organization="org" | |||
provisioned={true} |
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
jest.mock('../../../api/components', () => ({ | |||
deleteComponents: jest.fn(() => Promise.resolve()) | |||
bulkDeleteProjects: jest.fn(() => Promise.resolve()) | |||
})); | |||
import * as React from 'react'; | |||
@@ -26,9 +26,13 @@ import { shallow } from 'enzyme'; | |||
import DeleteModal, { Props } from '../DeleteModal'; | |||
import { click } from '../../../helpers/testUtils'; | |||
const deleteComponents = require('../../../api/components').deleteComponents as jest.Mock<any>; | |||
const bulkDeleteProjects = require('../../../api/components').bulkDeleteProjects as jest.Mock<any>; | |||
it('deletes projects', async () => { | |||
beforeEach(() => { | |||
bulkDeleteProjects.mockClear(); | |||
}); | |||
it('deletes all projects', async () => { | |||
const onConfirm = jest.fn(); | |||
const wrapper = shallowRender({ onConfirm }); | |||
(wrapper.instance() as DeleteModal).mounted = true; | |||
@@ -36,7 +40,27 @@ it('deletes projects', async () => { | |||
click(wrapper.find('button')); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect(deleteComponents).toBeCalledWith(['foo', 'bar'], 'org'); | |||
expect(bulkDeleteProjects).toBeCalledWith({ | |||
analyzedBefore: '2017-04-08T00:00:00.000Z', | |||
onProvisionedOnly: undefined, | |||
organization: 'org', | |||
q: 'bla', | |||
qualifiers: 'TRK' | |||
}); | |||
await new Promise(setImmediate); | |||
expect(onConfirm).toBeCalled(); | |||
}); | |||
it('deletes selected projects', async () => { | |||
const onConfirm = jest.fn(); | |||
const wrapper = shallowRender({ onConfirm, selection: ['proj1', 'proj2'] }); | |||
(wrapper.instance() as DeleteModal).mounted = true; | |||
expect(wrapper).toMatchSnapshot(); | |||
click(wrapper.find('button')); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect(bulkDeleteProjects).toBeCalledWith({ organization: 'org', projects: 'proj1,proj2' }); | |||
await new Promise(setImmediate); | |||
expect(onConfirm).toBeCalled(); | |||
@@ -52,11 +76,15 @@ it('closes', () => { | |||
function shallowRender(props?: { [P in keyof Props]?: Props[P] }) { | |||
return shallow( | |||
<DeleteModal | |||
analyzedBefore="2017-04-08T00:00:00.000Z" | |||
onClose={jest.fn()} | |||
onConfirm={jest.fn()} | |||
organization="org" | |||
provisioned={false} | |||
qualifier="TRK" | |||
selection={['foo', 'bar']} | |||
query="bla" | |||
selection={[]} | |||
total={17} | |||
{...props} | |||
/> | |||
); |
@@ -20,7 +20,7 @@ | |||
import * as React from 'react'; | |||
import { shallow } from 'enzyme'; | |||
import Header, { Props } from '../Header'; | |||
import { Visibility } from '../utils'; | |||
import { Visibility } from '../../../app/types'; | |||
import { click } from '../../../helpers/testUtils'; | |||
const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; |
@@ -20,7 +20,7 @@ | |||
import * as React from 'react'; | |||
import { shallow } from 'enzyme'; | |||
import ProjectRow from '../ProjectRow'; | |||
import { Visibility } from '../utils'; | |||
import { Visibility } from '../../../app/types'; | |||
import { click } from '../../../helpers/testUtils'; | |||
const project = { |
@@ -22,8 +22,8 @@ jest.mock('../../permissions/project/views/ApplyTemplateView'); | |||
import * as React from 'react'; | |||
import { shallow } from 'enzyme'; | |||
import Projects from '../Projects'; | |||
import { Visibility } from '../utils'; | |||
import ApplyTemplateView from '../../permissions/project/views/ApplyTemplateView'; | |||
import { Visibility } from '../../../app/types'; | |||
const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; | |||
const projects = [ |
@@ -118,7 +118,7 @@ function shallowRender(props?: { [P in keyof Props]?: Props[P] }) { | |||
ready={true} | |||
selection={[]} | |||
topLevelQualifiers={['TRK']} | |||
total={0} | |||
total={17} | |||
{...props} | |||
/> | |||
); |
@@ -67,8 +67,11 @@ exports[`bulk applies template to all results 2`] = ` | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-warning" | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
permission_templates.bulk_apply_permission_template.apply_to_all.17 | |||
</div> | |||
<div | |||
@@ -183,8 +186,11 @@ exports[`bulk applies template to all results 3`] = ` | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-warning" | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
permission_templates.bulk_apply_permission_template.apply_to_all.17 | |||
</div> | |||
<div | |||
@@ -388,8 +394,11 @@ exports[`bulk applies template to selected results 2`] = ` | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-info" | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
permission_templates.bulk_apply_permission_template.apply_to_selected.2 | |||
</div> | |||
<div | |||
@@ -504,8 +513,11 @@ exports[`bulk applies template to selected results 3`] = ` | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-info" | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
permission_templates.bulk_apply_permission_template.apply_to_selected.2 | |||
</div> | |||
<div |
@@ -1,6 +1,6 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`deletes projects 1`] = ` | |||
exports[`deletes all projects 1`] = ` | |||
<Modal | |||
ariaHideApp={true} | |||
bodyOpenClassName="ReactModal__Body--open" | |||
@@ -24,6 +24,14 @@ exports[`deletes projects 1`] = ` | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
projects_management.delete_all_warning.17 | |||
</div> | |||
qualifiers.delete_confirm.TRK | |||
</div> | |||
<footer | |||
@@ -47,7 +55,7 @@ exports[`deletes projects 1`] = ` | |||
</Modal> | |||
`; | |||
exports[`deletes projects 2`] = ` | |||
exports[`deletes all projects 2`] = ` | |||
<Modal | |||
ariaHideApp={true} | |||
bodyOpenClassName="ReactModal__Body--open" | |||
@@ -71,6 +79,127 @@ exports[`deletes projects 2`] = ` | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
projects_management.delete_all_warning.17 | |||
</div> | |||
qualifiers.delete_confirm.TRK | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<i | |||
className="spinner spacer-right" | |||
/> | |||
<button | |||
className="button-red" | |||
disabled={true} | |||
onClick={[Function]} | |||
> | |||
delete | |||
</button> | |||
<a | |||
className="js-modal-close" | |||
href="#" | |||
onClick={[Function]} | |||
> | |||
cancel | |||
</a> | |||
</footer> | |||
</Modal> | |||
`; | |||
exports[`deletes selected projects 1`] = ` | |||
<Modal | |||
ariaHideApp={true} | |||
bodyOpenClassName="ReactModal__Body--open" | |||
className="modal" | |||
closeTimeoutMS={0} | |||
contentLabel="qualifiers.delete.TRK" | |||
isOpen={true} | |||
onRequestClose={[Function]} | |||
overlayClassName="modal-overlay" | |||
parentSelector={[Function]} | |||
portalClassName="ReactModalPortal" | |||
shouldCloseOnOverlayClick={true} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
qualifiers.delete.TRK | |||
</h2> | |||
</header> | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
projects_management.delete_selected_warning.2 | |||
</div> | |||
qualifiers.delete_confirm.TRK | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<button | |||
className="button-red" | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
delete | |||
</button> | |||
<a | |||
className="js-modal-close" | |||
href="#" | |||
onClick={[Function]} | |||
> | |||
cancel | |||
</a> | |||
</footer> | |||
</Modal> | |||
`; | |||
exports[`deletes selected projects 2`] = ` | |||
<Modal | |||
ariaHideApp={true} | |||
bodyOpenClassName="ReactModal__Body--open" | |||
className="modal" | |||
closeTimeoutMS={0} | |||
contentLabel="qualifiers.delete.TRK" | |||
isOpen={true} | |||
onRequestClose={[Function]} | |||
overlayClassName="modal-overlay" | |||
parentSelector={[Function]} | |||
portalClassName="ReactModalPortal" | |||
shouldCloseOnOverlayClick={true} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
qualifiers.delete.TRK | |||
</h2> | |||
</header> | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="alert alert-warning modal-alert" | |||
> | |||
<AlertWarnIcon | |||
className="spacer-right" | |||
/> | |||
projects_management.delete_selected_warning.2 | |||
</div> | |||
qualifiers.delete_confirm.TRK | |||
</div> | |||
<footer |
@@ -8,7 +8,7 @@ exports[`bulk applies permission template 1`] = ` | |||
qualifier="TRK" | |||
query="" | |||
selection={Array []} | |||
total={0} | |||
total={17} | |||
/> | |||
`; | |||
@@ -17,13 +17,16 @@ exports[`deletes projects 1`] = ` | |||
onClose={[Function]} | |||
onConfirm={[Function]} | |||
organization="org" | |||
provisioned={false} | |||
qualifier="TRK" | |||
query="" | |||
selection={ | |||
Array [ | |||
"foo", | |||
"bar", | |||
] | |||
} | |||
total={17} | |||
/> | |||
`; | |||
@@ -176,13 +179,14 @@ exports[`render qualifiers filter 1`] = ` | |||
> | |||
<button | |||
className="spacer-right js-bulk-apply-permission-template" | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
permission_templates.bulk_apply_permission_template | |||
</button> | |||
<button | |||
className="js-delete button-red" | |||
disabled={true} | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
delete | |||
@@ -277,13 +281,14 @@ exports[`renders 1`] = ` | |||
> | |||
<button | |||
className="spacer-right js-bulk-apply-permission-template" | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
permission_templates.bulk_apply_permission_template | |||
</button> | |||
<button | |||
className="js-delete button-red" | |||
disabled={true} | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
delete |
@@ -17,19 +17,10 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { SearchProjectsResponseComponent } from '../../api/components'; | |||
export const PAGE_SIZE = 50; | |||
export const QUALIFIERS_ORDER = ['TRK', 'VW', 'APP']; | |||
export interface Project { | |||
key: string; | |||
lastAnalysisDate?: string; | |||
name: string; | |||
qualifier: string; | |||
visibility: Visibility; | |||
} | |||
export enum Visibility { | |||
Public = 'public', | |||
Private = 'private' | |||
} | |||
export type Project = SearchProjectsResponseComponent; |
@@ -38,6 +38,14 @@ | |||
vertical-align: middle; | |||
} | |||
.modal-alert { | |||
margin: -10px -10px 16px; | |||
padding: 10px; | |||
border-top: none; | |||
border-left: none; | |||
border-right: none; | |||
} | |||
// Color | |||
.alert-emphasis-variant(@color, @background-color, @border-color) { |
@@ -225,7 +225,6 @@ added_since_previous_version_detailed=Added since previous version ({0}) | |||
added_since_version=Added since version {0} | |||
all_violations=All violations | |||
all_issues=All issues | |||
analyzed_before=Analyzed before | |||
and_worse=and worse | |||
are_you_sure=Are you sure? | |||
assigned_to=Assigned to | |||
@@ -253,6 +252,7 @@ full_source=Full source | |||
greater_or_equals=Greater or equals | |||
greater_than=Greater than | |||
help_tips=Help tips | |||
last_analysis_before=Last analysis before | |||
less_or_equals=Less or equals | |||
less_than=Less than | |||
logging_out=You're logging out, please wait... | |||
@@ -1406,6 +1406,8 @@ project_quality_gate.successfully_updated=Quality gate has been successfully upd | |||
project_deletion.delete_resource_confirmation=Are you sure you want to delete "{0}"? | |||
projects_management.delete_resource_confirmation=Are you sure you want to delete "{0}"? | |||
projects_management.delete_selected_warning=You're about to delete {0} selected items. | |||
projects_management.delete_all_warning=You're about to delete all {0} items. | |||
#------------------------------------------------------------------------------ |