Merge branch 'decoupling-settings-page' of https://github.com/biosistemika/scinote-web into zd_SCI_1688

This commit is contained in:
zmagod 2017-10-19 16:06:40 +02:00
commit e3e3f4c75d
10 changed files with 261 additions and 187 deletions

View file

@ -0,0 +1,27 @@
// @flow
import React from "react";
import type { Node } from "react";
import type { MessageDescriptor } from "flow-typed";
import DocumentTitle from "react-document-title";
import { formatMessage, defineMessages, injectIntl } from "react-intl";
type Props = {
intl: any,
localeID: string,
values?: any,
children: Node
};
const PageTitle = (props: Props): Node => {
const message = defineMessages({
placeholder: { id: props.localeID }
});
const title = props.intl.formatMessage(message.placeholder, props.values);
return <DocumentTitle title={title}>{props.children}</DocumentTitle>;
};
PageTitle.defaultProps = {
values: {}
};
export default injectIntl(PageTitle);

View file

@ -8,6 +8,14 @@ export default {
loading: "Loading ...", loading: "Loading ...",
upload: "Upload" upload: "Upload"
}, },
page_title: {
root: "SciNote",
settings_preference_page: "SciNote | Settings | Preferences",
settings_profile_page: "SciNote | Settings | Profile",
team_page: "SciNote | Settings | Teams | {name}",
all_teams_page: "SciNote | Settings | Teams",
new_team_page: "SciNote | Settings | Teams | New"
},
error_messages: { error_messages: {
text_too_short: "is too short (minimum is {min_length} characters)", text_too_short: "is too short (minimum is {min_length} characters)",
text_too_long: "is too long (maximum is {max_length} characters)", text_too_long: "is too long (maximum is {max_length} characters)",

View file

@ -16,6 +16,7 @@ import {
import { SETTINGS_PATH, SETTINGS_TEAMS } from "../../config/api_endpoints"; import { SETTINGS_PATH, SETTINGS_TEAMS } from "../../config/api_endpoints";
import PageTitle from "../../components/PageTitle";
import NotFound from "../../components/404/NotFound"; import NotFound from "../../components/404/NotFound";
import SettingsProfile from "./scenes/profile"; import SettingsProfile from "./scenes/profile";
import SettingsPreferences from "./scenes/preferences"; import SettingsPreferences from "./scenes/preferences";
@ -61,7 +62,7 @@ export default class SettingsPage extends Component<*, State> {
render() { render() {
return ( return (
<div> <PageTitle localeID="page_title.root">
<div className="container"> <div className="container">
<Nav bsStyle="tabs" activeKey="1" onSelect={this.handleSelect}> <Nav bsStyle="tabs" activeKey="1" onSelect={this.handleSelect}>
<LinkContainer <LinkContainer
@ -126,7 +127,7 @@ export default class SettingsPage extends Component<*, State> {
<Route component={NotFound} /> <Route component={NotFound} />
</Switch> </Switch>
</div> </div>
</div> </PageTitle>
); );
} }
} }

View file

@ -3,6 +3,7 @@ import React, { Component } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { getUserPreferencesInfo } from "../../../../services/api/users_api"; import { getUserPreferencesInfo } from "../../../../services/api/users_api";
import PageTitle from "../../../../components/PageTitle";
import SettingsAccountWrapper from "../../components/SettingsAccountWrapper"; import SettingsAccountWrapper from "../../components/SettingsAccountWrapper";
import InputTimezone from "./components/InputTimezone"; import InputTimezone from "./components/InputTimezone";
import NotificationsGroup from "./components/NotificationsGroup"; import NotificationsGroup from "./components/NotificationsGroup";
@ -60,48 +61,46 @@ class SettingsPreferences extends Component<Props, State> {
render() { render() {
return ( return (
<SettingsAccountWrapper> <PageTitle localeID="page_title.settings_preference_page">
<div className="col-xs-12 col-sm-9"> <SettingsAccountWrapper>
<InputTimezone <div className="col-xs-12 col-sm-9">
value={this.state.timeZone} <InputTimezone
loadPreferences={this.getPreferencesInfo} value={this.state.timeZone}
/> loadPreferences={this.getPreferencesInfo}
<h3> />
<FormattedMessage id="settings_page.notifications" /> <h3>
</h3> <FormattedMessage id="settings_page.notifications" />
<NotificationsGroup </h3>
type={ASSIGNMENT_NOTIFICATION} <NotificationsGroup
title="settings_page.assignement" type={ASSIGNMENT_NOTIFICATION}
subtitle="settings_page.assignement_msg" title="settings_page.assignement"
iconClasses="fa fa-newspaper-o" subtitle="settings_page.assignement_msg"
inAppNotification={this.state.assignments_notification} iconClasses="fa fa-newspaper-o"
emailNotification={ inAppNotification={this.state.assignments_notification}
this.state.assignments_email_notification emailNotification={this.state.assignments_email_notification}
} iconBackground={MAIN_COLOR_BLUE}
iconBackground={MAIN_COLOR_BLUE} reloadInfo={this.getPreferencesInfo}
reloadInfo={this.getPreferencesInfo} />
/> <NotificationsGroup
<NotificationsGroup type={RECENT_NOTIFICATION}
type={RECENT_NOTIFICATION} title="settings_page.recent_changes"
title="settings_page.recent_changes" subtitle="settings_page.recent_changes_msg"
subtitle="settings_page.recent_changes_msg" inAppNotification={this.state.recent_notification}
inAppNotification={this.state.recent_notification} emailNotification={this.state.recent_email_notification}
emailNotification={this.state.recent_email_notification} reloadInfo={this.getPreferencesInfo}
reloadInfo={this.getPreferencesInfo} />
/> <NotificationsGroup
<NotificationsGroup type={SYSTEM_NOTIFICATION}
type={SYSTEM_NOTIFICATION} title="settings_page.system_message"
title="settings_page.system_message" subtitle="settings_page.system_message_msg"
subtitle="settings_page.system_message_msg" emailNotification={this.state.system_message_email_notification}
emailNotification={ iconClasses="glyphicon glyphicon-tower"
this.state.system_message_email_notification iconBackground={ICON_GREEN_COLOR}
} reloadInfo={this.getPreferencesInfo}
iconClasses="glyphicon glyphicon-tower" />
iconBackground={ICON_GREEN_COLOR} </div>
reloadInfo={this.getPreferencesInfo} </SettingsAccountWrapper>
/> </PageTitle>
</div>
</SettingsAccountWrapper>
); );
} }
} }

View file

@ -1,29 +1,33 @@
// @flow // @flow
import React, { Component } from "react"; import React, { Component } from "react";
import PageTitle from "../../../../components/PageTitle";
import SettingsAccountWrapper from "../../components/SettingsAccountWrapper"; import SettingsAccountWrapper from "../../components/SettingsAccountWrapper";
import MyProfile from "./components/MyProfile"; import MyProfile from "./components/MyProfile";
import MyStatistics from "./components/MyStatistics"; import MyStatistics from "./components/MyStatistics";
type Props = { type Props = {
tabState: Function tabState: Function
} };
class SettingsProfile extends Component<Props> { class SettingsProfile extends Component<Props> {
componentDidMount() { componentDidMount() {
this.props.tabState("1") this.props.tabState("1");
} }
render() { render() {
return ( return (
<SettingsAccountWrapper> <PageTitle localeID="page_title.settings_profile_page">
<div className="col-xs-12 col-sm-4"> <SettingsAccountWrapper>
<MyProfile /> <div className="col-xs-12 col-sm-4">
</div> <MyProfile />
<div className="col-xs-12 col-sm-5"> </div>
<MyStatistics /> <div className="col-xs-12 col-sm-5">
</div> <MyStatistics />
</SettingsAccountWrapper> </div>
</SettingsAccountWrapper>
</PageTitle>
); );
} }
} }

View file

@ -15,6 +15,7 @@ import { getTeamDetails } from "../../../../services/api/teams_api";
import { SETTINGS_TEAMS_ROUTE } from "../../../../config/routes"; import { SETTINGS_TEAMS_ROUTE } from "../../../../config/routes";
import { BORDER_LIGHT_COLOR } from "../../../../config/constants/colors"; import { BORDER_LIGHT_COLOR } from "../../../../config/constants/colors";
import PageTitle from "../../../../components/PageTitle";
import TeamsMembers from "./components/TeamsMembers"; import TeamsMembers from "./components/TeamsMembers";
import UpdateTeamDescriptionModal from "./components/UpdateTeamDescriptionModal"; import UpdateTeamDescriptionModal from "./components/UpdateTeamDescriptionModal";
import UpdateTeamNameModal from "./components/UpdateTeamNameModal"; import UpdateTeamNameModal from "./components/UpdateTeamNameModal";
@ -108,7 +109,7 @@ class SettingsTeam extends Component<Props, State> {
// set team tab on active // set team tab on active
(this: any).props.tabState("2"); (this: any).props.tabState("2");
const { id } = this.props.match.params; const { id } = this.props.match.params;
if(id) { if (id) {
getTeamDetails(parseInt(id)).then(response => { getTeamDetails(parseInt(id)).then(response => {
const { team, users } = response; const { team, users } = response;
(this: any).setState({ users, team }); (this: any).setState({ users, team });
@ -165,86 +166,91 @@ class SettingsTeam extends Component<Props, State> {
render() { render() {
return ( return (
<Wrapper> <PageTitle
<Breadcrumb> localeID="page_title.team_page"
<LinkContainer to={SETTINGS_TEAMS_ROUTE}> values={{ name: this.state.team.name }}
<Breadcrumb.Item> >
<FormattedMessage id="settings_page.all_teams" /> <Wrapper>
<Breadcrumb>
<LinkContainer to={SETTINGS_TEAMS_ROUTE}>
<Breadcrumb.Item>
<FormattedMessage id="settings_page.all_teams" />
</Breadcrumb.Item>
</LinkContainer>
<Breadcrumb.Item active={true}>
{this.state.team.name}
</Breadcrumb.Item> </Breadcrumb.Item>
</LinkContainer> </Breadcrumb>
<Breadcrumb.Item active={true}> <TabTitle>
{this.state.team.name} <StyledH3 onClick={this.showNameModal}>
</Breadcrumb.Item> {this.state.team.name}
</Breadcrumb> </StyledH3>
<TabTitle> </TabTitle>
<StyledH3 onClick={this.showNameModal}> <Row>
{this.state.team.name} <Col xs={6} sm={3}>
</StyledH3> <BadgeWrapper>
</TabTitle> <Glyphicon glyph="calendar" />
<Row> </BadgeWrapper>
<Col xs={6} sm={3}> <StyledWell>
<BadgeWrapper> <FormattedHTMLMessage
<Glyphicon glyph="calendar" /> id="settings_page.single_team.created_on"
</BadgeWrapper> values={{
<StyledWell> created_at: moment(this.state.team.created_at).format(
<FormattedHTMLMessage "DD.MM.YYYY"
id="settings_page.single_team.created_on" )
values={{ }}
created_at: moment(this.state.team.created_at).format( />
"DD.MM.YYYY" </StyledWell>
) </Col>
}} <Col xs={10} sm={5}>
/> <BadgeWrapper>
</StyledWell> <Glyphicon glyph="user" />
</Col> </BadgeWrapper>
<Col xs={10} sm={5}> <StyledWell>
<BadgeWrapper> <FormattedHTMLMessage
<Glyphicon glyph="user" /> id="settings_page.single_team.created_by"
</BadgeWrapper> values={{ created_by: this.state.team.created_by }}
<StyledWell> />
<FormattedHTMLMessage </StyledWell>
id="settings_page.single_team.created_by" </Col>
values={{ created_by: this.state.team.created_by }} <Col xs={8} sm={4}>
/> <BadgeWrapper>
</StyledWell> <Glyphicon glyph="hdd" />
</Col> </BadgeWrapper>
<Col xs={8} sm={4}> <StyledWell>
<BadgeWrapper> <FormattedHTMLMessage
<Glyphicon glyph="hdd" /> id="settings_page.single_team.space_usage"
</BadgeWrapper> values={{
<StyledWell> space_usage: formatBytes(this.state.team.space_taken)
<FormattedHTMLMessage }}
id="settings_page.single_team.space_usage" />
values={{ </StyledWell>
space_usage: formatBytes(this.state.team.space_taken) </Col>
}} </Row>
/> <Row>
</StyledWell> <Col sm={12} onClick={this.showDescriptionModal}>
</Col> <BadgeWrapper>
</Row> <Glyphicon glyph="info-sign" />
<Row> </BadgeWrapper>
<Col sm={12} onClick={this.showDescriptionModal}> <StyledDescriptionWell>
<BadgeWrapper> <span>{this.renderDescription()}</span>
<Glyphicon glyph="info-sign" /> </StyledDescriptionWell>
</BadgeWrapper> </Col>
<StyledDescriptionWell> </Row>
<span>{this.renderDescription()}</span> <TeamsMembers
</StyledDescriptionWell> members={this.state.users}
</Col> updateUsersCallback={this.updateUsersCallback}
</Row> team={this.state.team}
<TeamsMembers />
members={this.state.users} <UpdateTeamDescriptionModal
updateUsersCallback={this.updateUsersCallback} showModal={this.state.showDescriptionModal}
team={this.state.team} hideModal={this.hideDescriptionModalCallback}
/> team={this.state.team}
<UpdateTeamDescriptionModal updateTeamCallback={this.updateTeamCallback}
showModal={this.state.showDescriptionModal} />
hideModal={this.hideDescriptionModalCallback} {this.renderEditNameModal()}
team={this.state.team} </Wrapper>
updateTeamCallback={this.updateTeamCallback} </PageTitle>
/>
{this.renderEditNameModal()}
</Wrapper>
); );
} }
} }

View file

@ -6,10 +6,11 @@ import { Breadcrumb } from "react-bootstrap";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import type { State } from "flow-typed"; import type { State } from "flow-typed";
import type { MapStateToProps } from "react-redux" import type { MapStateToProps } from "react-redux";
import { BORDER_LIGHT_COLOR } from "../../../../config/constants/colors"; import { BORDER_LIGHT_COLOR } from "../../../../config/constants/colors";
import PageTitle from "../../../../components/PageTitle";
import TeamsPageDetails from "./components/TeamsPageDetails"; import TeamsPageDetails from "./components/TeamsPageDetails";
import TeamsDataTable from "./components/TeamsDataTable"; import TeamsDataTable from "./components/TeamsDataTable";
@ -25,12 +26,12 @@ const Wrapper = styled.div`
type Props = { type Props = {
tabState: Function, tabState: Function,
teams: Array<Teams$Team> teams: Array<Teams$Team>
} };
class SettingsTeams extends Component<Props> { class SettingsTeams extends Component<Props> {
static defaultProps = { static defaultProps = {
teams: [{ id: 0, name: "", current_team: "", role: "", members: 0 }] teams: [{ id: 0, name: "", current_team: "", role: "", members: 0 }]
} };
componentDidMount() { componentDidMount() {
// set team tab on active // set team tab on active
@ -40,15 +41,17 @@ class SettingsTeams extends Component<Props> {
render() { render() {
const { teams } = this.props; const { teams } = this.props;
return ( return (
<Wrapper> <PageTitle localeID="page_title.all_teams_page">
<Breadcrumb> <Wrapper>
<Breadcrumb.Item active> <Breadcrumb>
<FormattedMessage id="settings_page.all_teams" /> <Breadcrumb.Item active>
</Breadcrumb.Item> <FormattedMessage id="settings_page.all_teams" />
</Breadcrumb> </Breadcrumb.Item>
<TeamsPageDetails teams={teams} /> </Breadcrumb>
<TeamsDataTable teams={teams} /> <TeamsPageDetails teams={teams} />
</Wrapper> <TeamsDataTable teams={teams} />
</Wrapper>
</PageTitle>
); );
} }
} }

View file

@ -32,6 +32,7 @@ import { getTeamsList } from "../../../../../components/actions/TeamsActions";
import { BORDER_LIGHT_COLOR } from "../../../../../config/constants/colors"; import { BORDER_LIGHT_COLOR } from "../../../../../config/constants/colors";
import PageTitle from "../../../../../components/PageTitle";
import NameFormControl from "./components/NameFormControl"; import NameFormControl from "./components/NameFormControl";
const Wrapper = styled.div` const Wrapper = styled.div`
@ -245,48 +246,50 @@ class SettingsNewTeam extends Component<Props, State> {
!_.isEmpty(this.state.formErrors.description); !_.isEmpty(this.state.formErrors.description);
return ( return (
<Wrapper> <PageTitle localeID="page_title.new_team_page">
<Breadcrumb> <Wrapper>
<LinkContainer to={SETTINGS_TEAMS_ROUTE}> <Breadcrumb>
<Breadcrumb.Item>
<FormattedMessage id="settings_page.all_teams" />
</Breadcrumb.Item>
</LinkContainer>
<Breadcrumb.Item active={true}>
<FormattedMessage id="settings_page.new_team.title" />
</Breadcrumb.Item>
</Breadcrumb>
<form onSubmit={this.onSubmit} style={{ maxWidth: "500px" }}>
<MyFormGroupDiv>
{this.renderTeamNameFormGroup()}
<small>
<FormattedMessage id="settings_page.new_team.name_sublabel" />
</small>
</MyFormGroupDiv>
<MyFormGroupDiv>
{this.renderTeamDescriptionFormGroup()}
<small>
<FormattedMessage id="settings_page.new_team.description_sublabel" />
</small>
</MyFormGroupDiv>
<ButtonToolbar>
<Button
type="submit"
className="btn-primary"
disabled={btnDisabled}
>
<FormattedMessage id="settings_page.new_team.create" />
</Button>
<LinkContainer to={SETTINGS_TEAMS_ROUTE}> <LinkContainer to={SETTINGS_TEAMS_ROUTE}>
<Button> <Breadcrumb.Item>
<FormattedMessage id="general.cancel" /> <FormattedMessage id="settings_page.all_teams" />
</Button> </Breadcrumb.Item>
</LinkContainer> </LinkContainer>
</ButtonToolbar> <Breadcrumb.Item active={true}>
</form> <FormattedMessage id="settings_page.new_team.title" />
</Wrapper> </Breadcrumb.Item>
</Breadcrumb>
<form onSubmit={this.onSubmit} style={{ maxWidth: "500px" }}>
<MyFormGroupDiv>
{this.renderTeamNameFormGroup()}
<small>
<FormattedMessage id="settings_page.new_team.name_sublabel" />
</small>
</MyFormGroupDiv>
<MyFormGroupDiv>
{this.renderTeamDescriptionFormGroup()}
<small>
<FormattedMessage id="settings_page.new_team.description_sublabel" />
</small>
</MyFormGroupDiv>
<ButtonToolbar>
<Button
type="submit"
className="btn-primary"
disabled={btnDisabled}
>
<FormattedMessage id="settings_page.new_team.create" />
</Button>
<LinkContainer to={SETTINGS_TEAMS_ROUTE}>
<Button>
<FormattedMessage id="general.cancel" />
</Button>
</LinkContainer>
</ButtonToolbar>
</form>
</Wrapper>
</PageTitle>
); );
} }
} }

View file

@ -72,6 +72,7 @@
"react-bootstrap-table": "^4.0.0", "react-bootstrap-table": "^4.0.0",
"react-bootstrap-timezone-picker": "^1.0.11", "react-bootstrap-timezone-picker": "^1.0.11",
"react-data-grid": "^2.0.2", "react-data-grid": "^2.0.2",
"react-document-title": "^2.0.3",
"react-dom": "15.6.1", "react-dom": "15.6.1",
"react-intl": "^2.3.0", "react-intl": "^2.3.0",
"react-intl-redux": "^0.6.0", "react-intl-redux": "^0.6.0",

View file

@ -2289,6 +2289,10 @@ exenv@1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.0.tgz#3835f127abf075bfe082d0aed4484057c78e3c89" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.0.tgz#3835f127abf075bfe082d0aed4484057c78e3c89"
exenv@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
expand-brackets@^0.1.4: expand-brackets@^0.1.4:
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@ -5018,6 +5022,13 @@ react-data-grid@^2.0.2:
version "2.0.60" version "2.0.60"
resolved "https://registry.yarnpkg.com/react-data-grid/-/react-data-grid-2.0.60.tgz#4a1549d4dad032677439f25dbcf1036ab966032f" resolved "https://registry.yarnpkg.com/react-data-grid/-/react-data-grid-2.0.60.tgz#4a1549d4dad032677439f25dbcf1036ab966032f"
react-document-title@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/react-document-title/-/react-document-title-2.0.3.tgz#bbf922a0d71412fc948245e4283b2412df70f2b9"
dependencies:
prop-types "^15.5.6"
react-side-effect "^1.0.2"
react-dom-factories@^1.0.0: react-dom-factories@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.1.tgz#c50692ac5ff1adb39d86dfe6dbe3485dacf58455" resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.1.tgz#c50692ac5ff1adb39d86dfe6dbe3485dacf58455"
@ -5124,6 +5135,13 @@ react-s-alert@^1.3.0:
dependencies: dependencies:
babel-runtime "^6.23.0" babel-runtime "^6.23.0"
react-side-effect@^1.0.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.3.tgz#512c25abe0dec172834c4001ec5c51e04d41bc5c"
dependencies:
exenv "^1.2.1"
shallowequal "^1.0.1"
react-tagsinput@^3.17.0: react-tagsinput@^3.17.0:
version "3.18.0" version "3.18.0"
resolved "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.18.0.tgz#40e036fc0f4c3d6b4689858189ab02926717a818" resolved "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.18.0.tgz#40e036fc0f4c3d6b4689858189ab02926717a818"
@ -5601,6 +5619,10 @@ shallow-clone@^0.1.2:
lazy-cache "^0.2.3" lazy-cache "^0.2.3"
mixin-object "^2.0.1" mixin-object "^2.0.1"
shallowequal@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f"
shebang-command@^1.2.0: shebang-command@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"