@@ -20,11 +20,17 @@ | |||
import { getJSON, post, postJSON, RequestData } from '../helpers/request'; | |||
import throwGlobalError from '../app/utils/throwGlobalError'; | |||
interface GetOrganizationsParameters { | |||
organizations?: string; | |||
member?: boolean; | |||
} | |||
interface GetOrganizationsResponse { | |||
organizations: Array<{ | |||
avatar?: string; | |||
description?: string; | |||
guarded: boolean; | |||
isAdmin: boolean; | |||
key: string; | |||
name: string; | |||
url?: string; | |||
@@ -36,20 +42,14 @@ interface GetOrganizationsResponse { | |||
}; | |||
} | |||
export function getOrganizations(organizations?: string[]): Promise<GetOrganizationsResponse> { | |||
const data: RequestData = {}; | |||
if (organizations) { | |||
Object.assign(data, { organizations: organizations.join() }); | |||
} | |||
export function getOrganizations( | |||
data: GetOrganizationsParameters | |||
): Promise<GetOrganizationsResponse> { | |||
return getJSON('/api/organizations/search', data); | |||
} | |||
export function getMyOrganizations(): Promise<any> { | |||
return getJSON('/api/organizations/search_my_organizations').then(r => r.organizations); | |||
} | |||
export function getOrganization(key: string): Promise<any> { | |||
return getOrganizations([key]) | |||
return getOrganizations({ organizations: key }) | |||
.then(r => r.organizations.find((o: any) => o.key === key)) | |||
.catch(throwGlobalError); | |||
} |
@@ -44,7 +44,7 @@ type Props = { | |||
currentUser: CurrentUser, | |||
fetchMyOrganizations: () => Promise<*>, | |||
location: Object, | |||
organizations: Array<{ key: string, name: string }>, | |||
organizations: Array<{ isAdmin: bool, key: string, name: string }>, | |||
router: { push: string => void } | |||
}; | |||
*/ | |||
@@ -157,9 +157,17 @@ export default class GlobalNavUser extends React.PureComponent { | |||
{hasOrganizations && | |||
sortBy(organizations, org => org.name.toLowerCase()).map(organization => ( | |||
<li key={organization.key}> | |||
<OrganizationLink organization={organization} onClick={this.closeDropdown}> | |||
<OrganizationIcon /> | |||
<span className="spacer-left">{organization.name}</span> | |||
<OrganizationLink | |||
className="dropdown-item-flex" | |||
organization={organization} | |||
onClick={this.closeDropdown}> | |||
<div> | |||
<OrganizationIcon /> | |||
<span className="spacer-left">{organization.name}</span> | |||
</div> | |||
{organization.isAdmin && ( | |||
<span className="outline-badge spacer-left">{translate('admin')}</span> | |||
)} | |||
</OrganizationLink> | |||
</li> | |||
))} |
@@ -201,6 +201,7 @@ exports[`should render the users organizations 1`] = ` | |||
key="bar" | |||
> | |||
<OrganizationLink | |||
className="dropdown-item-flex" | |||
onClick={[Function]} | |||
organization={ | |||
Object { | |||
@@ -209,18 +210,21 @@ exports[`should render the users organizations 1`] = ` | |||
} | |||
} | |||
> | |||
<OrganizationIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
bar | |||
</span> | |||
<div> | |||
<OrganizationIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
bar | |||
</span> | |||
</div> | |||
</OrganizationLink> | |||
</li> | |||
<li | |||
key="foo" | |||
> | |||
<OrganizationLink | |||
className="dropdown-item-flex" | |||
onClick={[Function]} | |||
organization={ | |||
Object { | |||
@@ -229,18 +233,21 @@ exports[`should render the users organizations 1`] = ` | |||
} | |||
} | |||
> | |||
<OrganizationIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
Foo | |||
</span> | |||
<div> | |||
<OrganizationIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
Foo | |||
</span> | |||
</div> | |||
</OrganizationLink> | |||
</li> | |||
<li | |||
key="myorg" | |||
> | |||
<OrganizationLink | |||
className="dropdown-item-flex" | |||
onClick={[Function]} | |||
organization={ | |||
Object { | |||
@@ -249,12 +256,14 @@ exports[`should render the users organizations 1`] = ` | |||
} | |||
} | |||
> | |||
<OrganizationIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
MyOrg | |||
</span> | |||
<div> | |||
<OrganizationIcon /> | |||
<span | |||
className="spacer-left" | |||
> | |||
MyOrg | |||
</span> | |||
</div> | |||
</OrganizationLink> | |||
</li> | |||
<li |
@@ -176,3 +176,9 @@ | |||
color: var(--secondFontColor); | |||
font-size: 11px; | |||
} | |||
.dropdown-item-flex { | |||
display: flex !important; | |||
justify-content: space-between; | |||
align-items: center; | |||
} |
@@ -51,6 +51,9 @@ export default function OrganizationCard(props /*: Props */) { | |||
<h3 className="account-project-name"> | |||
<OrganizationLink organization={organization}>{organization.name}</OrganizationLink> | |||
{organization.isAdmin && ( | |||
<span className="outline-badge spacer-left">{translate('admin')}</span> | |||
)} | |||
</h3> | |||
{!!organization.description && ( |
@@ -17,20 +17,14 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as api from '../../../api/organizations'; | |||
import { getOrganizations } from '../../../api/organizations'; | |||
import { receiveMyOrganizations } from '../../../store/organizations/duck'; | |||
import { getValues } from '../../../api/settings'; | |||
import { receiveValues } from '../../settings/store/values/actions'; | |||
export const fetchMyOrganizations = () => dispatch => { | |||
return api.getMyOrganizations().then(keys => { | |||
if (keys.length > 0) { | |||
return api.getOrganizations(keys).then(({ organizations }) => { | |||
return dispatch(receiveMyOrganizations(organizations)); | |||
}); | |||
} else { | |||
return dispatch(receiveMyOrganizations([])); | |||
} | |||
return getOrganizations({ member: true }).then(({ organizations }) => { | |||
return dispatch(receiveMyOrganizations(organizations)); | |||
}); | |||
}; | |||
@@ -41,7 +41,7 @@ const fetchIssueOrganizations = issues => dispatch => { | |||
} | |||
const organizationKeys = uniq(issues.map(issue => issue.organization)); | |||
return getOrganizations(organizationKeys).then( | |||
return getOrganizations({ organizations: organizationKeys.join() }).then( | |||
response => dispatch(receiveOrganizations(response.organizations)), | |||
throwGlobalError | |||
); |
@@ -270,7 +270,7 @@ function fetchProjectOrganizations(projects: Array<{ organization: string }>) { | |||
} | |||
const organizations = uniq(projects.map(project => project.organization)); | |||
return getOrganizations(organizations).then(r => r.organizations); | |||
return getOrganizations({ organizations: organizations.join() }).then(r => r.organizations); | |||
} | |||
function mapFacetValues(values: Array<{ val: string; count: number }>) { |
@@ -23,7 +23,7 @@ import classNames from 'classnames'; | |||
import { sortBy } from 'lodash'; | |||
import Step from './Step'; | |||
import NewOrganizationForm from './NewOrganizationForm'; | |||
import { getMyOrganizations } from '../../../api/organizations'; | |||
import { getOrganizations } from '../../../api/organizations'; | |||
import Select from '../../../components/controls/Select'; | |||
import { translate } from '../../../helpers/l10n'; | |||
@@ -68,13 +68,15 @@ export default class OrganizationStep extends React.PureComponent { | |||
} | |||
fetchOrganizations = () => { | |||
getMyOrganizations().then( | |||
organizations => { | |||
getOrganizations({ member: true }).then( | |||
({ organizations }) => { | |||
if (this.mounted) { | |||
const organizationKeys = organizations.map(o => o.key); | |||
// best guess: if there is only one organization, then it is personal | |||
// otherwise, we can't guess, let's display them all as just "existing organizations" | |||
const personalOrganization = organizations.length === 1 ? organizations[0] : undefined; | |||
const existingOrganizations = organizations.length > 1 ? sortBy(organizations) : []; | |||
const personalOrganization = | |||
organizationKeys.length === 1 ? organizationKeys[0] : undefined; | |||
const existingOrganizations = organizationKeys.length > 1 ? sortBy(organizationKeys) : []; | |||
const selection = personalOrganization | |||
? 'personal' | |||
: existingOrganizations.length > 0 ? 'existing' : 'new'; |
@@ -22,16 +22,18 @@ import React from 'react'; | |||
import { mount } from 'enzyme'; | |||
import OrganizationStep from '../OrganizationStep'; | |||
import { click } from '../../../../helpers/testUtils'; | |||
import { getMyOrganizations } from '../../../../api/organizations'; | |||
import { getOrganizations } from '../../../../api/organizations'; | |||
jest.mock('../../../../api/organizations', () => ({ | |||
getMyOrganizations: jest.fn(() => Promise.resolve(['user', 'another'])) | |||
getOrganizations: jest.fn(() => | |||
Promise.resolve({ organizations: [{ key: 'user' }, { key: 'another' }] }) | |||
) | |||
})); | |||
const currentUser = { isLoggedIn: true, login: 'user' }; | |||
beforeEach(() => { | |||
getMyOrganizations.mockClear(); | |||
getOrganizations.mockClear(); | |||
}); | |||
// FIXME |
@@ -30,6 +30,7 @@ export type Organization = { | |||
canProvisionProjects?: boolean, | |||
canUpdateProjectsVisibilityToPrivate?: boolean, | |||
description?: string, | |||
isAdmin: bool, | |||
key: string, | |||
name: string, | |||
pages?: Array<{ key: string, name: string }>, |
@@ -45,7 +45,7 @@ export const fetchMetrics = () => dispatch => | |||
getAllMetrics().then(metrics => dispatch(receiveMetrics(metrics)), onFail(dispatch)); | |||
export const fetchOrganizations = (organizations /*: Array<string> | void */) => dispatch => | |||
getOrganizations(organizations).then( | |||
getOrganizations({ organizations: organizations && organizations.join() }).then( | |||
r => dispatch(receiveOrganizations(r.organizations)), | |||
onFail(dispatch) | |||
); |
@@ -8,6 +8,7 @@ action=Action | |||
actions=Actions | |||
active=Active | |||
add_verb=Add | |||
admin=Admin | |||
apply=Apply | |||
all=All | |||
and=And |