notifications: Array<{
channel: string,
type: string,
- project: string | null,
- projectName: string | null
+ organization?: string,
+ project?: string,
+ projectName?: string
}>,
channels: Array<string>,
globalTypes: Array<string>,
getJSON('/api/notifications/list')
);
-export const addNotification = (channel: string, type: string, project: string | null): Promise<*> => {
+export const addNotification = (channel: string, type: string, project?: string): Promise<*> => {
const data: Object = { channel, type };
if (project) {
Object.assign(data, { project });
return post('/api/notifications/add', data);
};
-export const removeNotification = (channel: string, type: string, project: string | null): Promise<*> => {
+export const removeNotification = (channel: string, type: string, project?: string): Promise<*> => {
const data: Object = { channel, type };
if (project) {
Object.assign(data, { project });
*/
import React from 'react';
import { connect } from 'react-redux';
+import { Link } from 'react-router';
import NotificationsList from './NotificationsList';
+import Organization from '../../../components/shared/Organization';
import { translate } from '../../../helpers/l10n';
import {
- getProjectNotifications,
- getNotificationChannels,
- getNotificationPerProjectTypes
+getProjectNotifications,
+getNotificationChannels,
+getNotificationPerProjectTypes
} from '../../../store/rootReducer';
import type {
- Notification,
- NotificationsState,
- ChannelsState,
- TypesState
+Notification,
+NotificationsState,
+ChannelsState,
+TypesState
} from '../../../store/notifications/duck';
import { addNotification, removeNotification } from './actions';
+import { getProjectUrl } from '../../../helpers/urls';
class ProjectNotifications extends React.Component {
props: {
<thead>
<tr>
<th>
- <h4 className="display-inline-block">{project.name}</h4>
+ <span className="text-normal"><Organization organizationKey={project.organization}/></span>
+ <h4 className="display-inline-block">
+ <Link to={getProjectUrl(project.key)}>{project.name}</Link>
+ </h4>
</th>
{channels.map(channel => (
<th key={channel} className="text-center">
import { connect } from 'react-redux';
import differenceBy from 'lodash/differenceBy';
import ProjectNotifications from './ProjectNotifications';
+import Organization from '../../../components/shared/Organization';
import { translate } from '../../../helpers/l10n';
-import { getComponents } from '../../../api/components';
+import { getSuggestions } from '../../../api/components';
import { getProjectsWithNotifications } from '../../../store/rootReducer';
type Props = {
}
}
- loadOptions = query => {
- // TODO filter existing out
- return getComponents({ qualifiers: 'TRK', q: query })
- .then(r => r.components)
+ renderOption = option => {
+ return (
+ <span>
+ <Organization organizationKey={option.organization} link={false}/>
+ <strong>{option.label}</strong>
+ </span>
+ );
+ }
+
+ loadOptions = (query, cb) => {
+ if (query.length < 2) {
+ cb(null, { options: [] });
+ return;
+ }
+
+ getSuggestions(query)
+ .then(r => {
+ const projects = r.results.find(domain => domain.q === 'TRK');
+ return projects ? projects.items : [];
+ })
.then(projects => projects.map(project => ({
value: project.key,
- label: project.name
+ label: project.name,
+ organization: project.organization
})))
- .then(options => ({ options }));
+ .then(options => {
+ cb(null, { options });
+ });
};
handleAddProject = selected => {
- const project = { key: selected.value, name: selected.label };
+ const project = { key: selected.value, name: selected.label, organization: selected.organization };
this.setState({
addedProjects: [...this.state.addedProjects, project]
});
Set notifications for:
</span>
<Select.Async
+ autoload={false}
+ cache={false}
name="new_project"
style={{ width: '300px' }}
loadOptions={this.loadOptions}
minimumInput={2}
+ optionRenderer={this.renderOption}
onChange={this.handleAddProject}
- placeholder="Search Project"
- searchPromptText="Type at least 2 characters to search"/>
+ placeholder="Search Project"/>
</div>
</section>
);
<thead>
<tr>
<th>
+ <span
+ className="text-normal">
+ <Connect(Organization) />
+ </span>
<h4
className="display-inline-block">
- Foo
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "id": "foo",
+ },
+ }
+ }>
+ Foo
+ </Link>
</h4>
</th>
<th
Set notifications for:
</span>
<Async
- autoload={true}
- cache={Object {}}
+ autoload={false}
+ cache={false}
ignoreAccents={true}
ignoreCase={true}
loadOptions={[Function]}
minimumInput={2}
name="new_project"
onChange={[Function]}
+ optionRenderer={[Function]}
options={Array []}
placeholder="Search Project"
- searchPromptText="Type at least 2 characters to search"
+ searchPromptText="Type to search"
style={
Object {
"width": "300px",
Set notifications for:
</span>
<Async
- autoload={true}
- cache={Object {}}
+ autoload={false}
+ cache={false}
ignoreAccents={true}
ignoreCase={true}
loadOptions={[Function]}
minimumInput={2}
name="new_project"
onChange={[Function]}
+ optionRenderer={[Function]}
options={Array []}
placeholder="Search Project"
- searchPromptText="Type at least 2 characters to search"
+ searchPromptText="Type to search"
style={
Object {
"width": "300px",
Set notifications for:
</span>
<Async
- autoload={true}
- cache={Object {}}
+ autoload={false}
+ cache={false}
ignoreAccents={true}
ignoreCase={true}
loadOptions={[Function]}
minimumInput={2}
name="new_project"
onChange={[Function]}
+ optionRenderer={[Function]}
options={Array []}
placeholder="Search Project"
- searchPromptText="Type at least 2 characters to search"
+ searchPromptText="Type to search"
style={
Object {
"width": "300px",
// @flow
import * as api from '../../../api/notifications';
import type { GetNotificationsResponse } from '../../../api/notifications';
-import { onFail } from '../../../store/rootActions';
+import { onFail, fetchOrganizations } from '../../../store/rootActions';
import {
receiveNotifications,
addNotification as addNotificationAction,
export const fetchNotifications = () => (dispatch: Function) => {
const onFulfil = (response: GetNotificationsResponse) => {
- dispatch(receiveNotifications(
- response.notifications,
- response.channels,
- response.globalTypes,
- response.perProjectTypes
- ));
+ const organizations = response.notifications
+ .filter(n => n.organization)
+ .map(n => n.organization);
+
+ dispatch(fetchOrganizations(organizations)).then(() => {
+ dispatch(receiveNotifications(
+ response.notifications,
+ response.channels,
+ response.globalTypes,
+ response.perProjectTypes
+ ));
+ });
};
return api.getNotifications().then(onFulfil, onFail(dispatch));
};
type Props = {
+ link?: boolean,
organizationKey: string,
organization: null | {
key: string,
name: string
},
- shouldBeDisplayed: boolean
+ shouldBeDisplayed: boolean,
};
class Organization extends React.Component {
props: Props;
+ static defaultProps = {
+ link: true
+ };
+
render () {
const { organization, shouldBeDisplayed } = this.props;
return (
<span>
- <OrganizationLink organization={organization}>{organization.name}</OrganizationLink>
+ {this.props.link ? (
+ <OrganizationLink organization={organization}>{organization.name}</OrganizationLink>
+ ) : (
+ organization.name
+ )}
<span className="slash-separator"/>
</span>
);
return window.baseUrl + '/dashboard?id=' + encodeURIComponent(componentKey);
}
+export function getProjectUrl (key) {
+ return {
+ pathname: '/dashboard',
+ query: { id: key }
+ };
+}
+
/**
* Generate URL for a global issues page
* @param {object} query
export type Notification = {
channel: string,
type: string,
- project: string | null,
- projectName: string | null
+ project?: string,
+ projectName?: string,
+ organization?: string
};
export type NotificationsState = Array<Notification>;
export const getProjects = (state: State): Array<string> => (
uniqBy(
- state.notifications.filter(n => n.project).map(n => ({ key: n.project, name: n.projectName })),
+ state.notifications.filter(n => n.project).map(n => ({
+ key: n.project,
+ name: n.projectName,
+ organization: n.organization
+ })),
project => project.key
)
);