diff --git a/app/controllers/client_api/users/invitations_controller.rb b/app/controllers/client_api/users/invitations_controller.rb index 21636fa71..eef944984 100644 --- a/app/controllers/client_api/users/invitations_controller.rb +++ b/app/controllers/client_api/users/invitations_controller.rb @@ -11,6 +11,12 @@ module ClientApi emails: params[:emails]) invite_results = invite_service.invitation success_response(invite_results) + rescue => error + respond_to do |format| + format.json do + render json: { message: error }, status: :unprocessable_entity + end + end end private diff --git a/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx b/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx index 7764041a6..1fcf52c43 100644 --- a/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx +++ b/app/javascript/src/components/InviteUsersModal/components/InviteUsersButton.jsx @@ -1,5 +1,4 @@ // @flow - import React from "react"; import { FormattedMessage } from "react-intl"; import { DropdownButton, MenuItem } from "react-bootstrap"; @@ -9,26 +8,25 @@ type Props = { status: boolean }; -const InviteUsersButton = ({ - handleClick, - status -} : Props) => ( - } - id="invite_users.submit_button" - disabled={status} - > - handleClick("guest")}> - - - handleClick("normal_user")}> - - - handleClick("admin")}> - - - -); +const InviteUsersButton = ({ handleClick, status }: Props) => { + return ( + } + id="invite_users.submit_button" + disabled={status} + > + handleClick("guest")}> + + + handleClick("normal_user")}> + + + handleClick("admin")}> + + + + ); +}; export default InviteUsersButton; diff --git a/app/javascript/src/components/InviteUsersModal/index.jsx b/app/javascript/src/components/InviteUsersModal/index.jsx index 5ad716764..5e854c03d 100644 --- a/app/javascript/src/components/InviteUsersModal/index.jsx +++ b/app/javascript/src/components/InviteUsersModal/index.jsx @@ -77,7 +77,8 @@ class InviteUsersModal extends Component { .then(response => { (this: any).setState({ inviteResults: response, - showInviteUsersResults: true + showInviteUsersResults: true, + inviteUserButtonDisabled: true }); }) .catch(error => { diff --git a/app/javascript/src/components/ModalsContainer/index.jsx b/app/javascript/src/components/ModalsContainer/index.jsx deleted file mode 100644 index 7cf9aff55..000000000 --- a/app/javascript/src/components/ModalsContainer/index.jsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from "react"; -import LeaveTeamModal from "./modals/LeaveTeamModal"; - -export default () => -
- -
; diff --git a/app/javascript/src/components/actions/TeamsActions.js b/app/javascript/src/components/actions/TeamsActions.js index ba7fa7262..3784c4693 100644 --- a/app/javascript/src/components/actions/TeamsActions.js +++ b/app/javascript/src/components/actions/TeamsActions.js @@ -1,41 +1,33 @@ -import axios from "../../config/axios"; -import _ from "lodash"; -import { TEAMS_PATH, CHANGE_TEAM_PATH } from "../../config/api_endpoints"; -import { - GET_LIST_OF_TEAMS, - SET_CURRENT_TEAM, - SHOW_LEAVE_TEAM_MODAL -} from "../../config/action_types"; +// @flow +import type { + Teams$Team, + Action$AddTeamData, + Actopm$SetCurrentTeam +} from "flow-typed"; +import type { Dispatch } from "redux-thunk"; +import { getTeams, changeCurrentTeam } from "../../services/api/teams_api"; +import { GET_LIST_OF_TEAMS, SET_CURRENT_TEAM } from "../../config/action_types"; -export function leaveTeamModalShow(show = false, team = {}) { - return { - payload: { team, show }, - type: SHOW_LEAVE_TEAM_MODAL - }; -} - -export function addTeamsData(data) { +export function addTeamsData(data: Array): Action$AddTeamData { return { type: GET_LIST_OF_TEAMS, payload: data }; } -export function setCurrentTeam(team) { +export function setCurrentTeam(team: Teams$Team): Actopm$SetCurrentTeam { return { team, type: SET_CURRENT_TEAM }; } -export function getTeamsList() { +export function getTeamsList(): Dispatch { return dispatch => { - axios - .get(TEAMS_PATH, { withCredentials: true }) + getTeams() .then(response => { - const teams = response.data.teams.collection; + const { teams, currentTeam } = response; dispatch(addTeamsData(teams)); - const currentTeam = _.find(teams, team => team.current_team); dispatch(setCurrentTeam(currentTeam)); }) .catch(error => { @@ -44,14 +36,12 @@ export function getTeamsList() { }; } -export function changeTeam(team_id) { +export function changeTeam(teamID: number): Dispatch { return dispatch => { - axios - .post(CHANGE_TEAM_PATH, { team_id }, { withCredentials: true }) + changeCurrentTeam(teamID) .then(response => { - const teams = response.data.teams.collection; + const { teams, currentTeam } = response; dispatch(addTeamsData(teams)); - const currentTeam = _.find(teams, team => team.current_team); dispatch(setCurrentTeam(currentTeam)); }) .catch(error => { diff --git a/app/javascript/src/components/reducers/TeamReducers.js b/app/javascript/src/components/reducers/TeamReducers.js index ac889c627..34ff7566c 100644 --- a/app/javascript/src/components/reducers/TeamReducers.js +++ b/app/javascript/src/components/reducers/TeamReducers.js @@ -1,7 +1,6 @@ import { SET_CURRENT_TEAM, - GET_LIST_OF_TEAMS, - SHOW_LEAVE_TEAM_MODAL + GET_LIST_OF_TEAMS } from "../../config/action_types"; export const setCurrentTeam = ( @@ -23,13 +22,3 @@ export const getListOfTeams = (state = { collection: [] }, action) => { } return state; }; - -export const showLeaveTeamModal = ( - state = { show: false, team: { id: 0, name: "", user_team_id: 0 } }, - action -) => { - if (action.type === SHOW_LEAVE_TEAM_MODAL) { - return { ...state, ...action.payload }; - } - return state; -}; diff --git a/app/javascript/src/config/action_types.js b/app/javascript/src/config/action_types.js index 2ae8f4162..a3536e9ad 100644 --- a/app/javascript/src/config/action_types.js +++ b/app/javascript/src/config/action_types.js @@ -1,20 +1,18 @@ +// @flow + // teams -export const SET_CURRENT_TEAM = "SET_CURRENT_TEAM"; -export const GET_LIST_OF_TEAMS = "GET_LIST_OF_TEAMS"; -export const SET_TEAM_DETAILS = "SET_TEAM_DETAILS"; +export const SET_CURRENT_TEAM: string = "SET_CURRENT_TEAM"; +export const GET_LIST_OF_TEAMS: string = "GET_LIST_OF_TEAMS"; +export const SET_TEAM_DETAILS: string = "SET_TEAM_DETAILS"; // users -export const USER_LOGOUT = "USER_LOGOUT"; -export const SET_CURRENT_USER = "SET_CURRENT_USER"; +export const USER_LOGOUT: string = "USER_LOGOUT"; +export const SET_CURRENT_USER: string = "SET_CURRENT_USER"; // user teams -export const LEAVE_TEAM = "LEAVE_TEAM"; - -// modals -export const SHOW_LEAVE_TEAM_MODAL = "SHOW_LEAVE_TEAM_MODAL"; -export const UPDATE_TEAM_DESCRIPTION_MODAL = "UPDATE_TEAM_DESCRIPTION_MODAL"; +export const LEAVE_TEAM: string = "LEAVE_TEAM"; // alerts -export const ADD_ALERT = "ADD_ALERT"; -export const CLEAR_ALERT = "CLEAR_ALERT"; -export const CLEAR_ALL_ALERTS = "CLEAR_ALL_ALERTS"; +export const ADD_ALERT: string = "ADD_ALERT"; +export const CLEAR_ALERT: string = "CLEAR_ALERT"; +export const CLEAR_ALL_ALERTS: string = "CLEAR_ALL_ALERTS"; diff --git a/app/javascript/src/config/api_endpoints.js b/app/javascript/src/config/api_endpoints.js deleted file mode 100644 index b760af418..000000000 --- a/app/javascript/src/config/api_endpoints.js +++ /dev/null @@ -1,27 +0,0 @@ -// settings -export const SETTINGS_PATH = "/settings"; -export const SETTINGS_ACCOUNT_PATH = "/settings/account"; -// teams -export const TEAMS_PATH = "/client_api/teams"; -export const CHANGE_TEAM_PATH = "/client_api/teams/change_team"; -export const TEAM_DETAILS_PATH = "/client_api/teams/:team_id/details"; -export const TEAM_UPDATE_PATH = "/client_api/teams/update"; - -// search -export const SEARCH_PATH = "/search"; - -// notifications -export const RECENT_NOTIFICATIONS_PATH = "/client_api/recent_notifications"; -// info dropdown_title -export const CUSTOMER_SUPPORT_LINK = "http://scinote.net/support"; -export const TUTORIALS_LINK = "http://scinote.net/product/tutorials/"; -export const RELEASE_NOTES_LINK = "http://scinote.net/docs/release-notes/"; -export const PREMIUM_LINK = "http://scinote.net/premium/"; -export const CONTACT_US_LINK = - "http://scinote.net/story-of-scinote/#contact-scinote"; - -// user teams -export const LEAVE_TEAM_PATH = "/client_api/users/leave_team"; - -// settings -export const SETTINGS_TEAMS = "/settings/teams"; diff --git a/app/javascript/src/config/axios.js b/app/javascript/src/config/axios.js deleted file mode 100644 index b514b7001..000000000 --- a/app/javascript/src/config/axios.js +++ /dev/null @@ -1,19 +0,0 @@ -// @TODO remove this file ASAP the preferences/profile refactoring is merged -import axios from "axios"; -import store from "./store"; -import { SIGN_IN_PATH } from "./routes"; -import { destroyState } from "../components/actions/UsersActions"; - -export default axios.create({ - withCredentials: true, - headers: { - "X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').content - }, - validateStatus(status) { - if (status === 401) { - store.dispatch(destroyState); - window.location = SIGN_IN_PATH; - } - return status >= 200 && status < 300; - } -}); diff --git a/app/javascript/src/config/reducers.js b/app/javascript/src/config/reducers.js index 1f7cbc866..075aa2e18 100644 --- a/app/javascript/src/config/reducers.js +++ b/app/javascript/src/config/reducers.js @@ -2,8 +2,7 @@ import { combineReducers } from "redux"; import { USER_LOGOUT } from "./action_types"; import { setCurrentTeam, - getListOfTeams, - showLeaveTeamModal + getListOfTeams } from "../components/reducers/TeamReducers"; import { currentUser } from "../components/reducers/UsersReducer"; import { alerts } from "../components/reducers/AlertsReducers"; @@ -12,7 +11,6 @@ const appReducer = combineReducers({ current_team: setCurrentTeam, all_teams: getListOfTeams, current_user: currentUser, - showLeaveTeamModal, alerts }); diff --git a/app/javascript/src/config/routes.js b/app/javascript/src/config/routes.js index 74abccff0..a46097b54 100644 --- a/app/javascript/src/config/routes.js +++ b/app/javascript/src/config/routes.js @@ -2,8 +2,21 @@ export const ROOT_PATH = "/"; export const SIGN_IN_PATH = "/users/sign_in"; // Settings page +export const SETTINGS_PATH = "/settings"; export const SETTINGS_TEAMS_ROUTE = "/settings/teams"; export const SETTINGS_TEAM_ROUTE = "/settings/teams/:id"; export const SETTINGS_NEW_TEAM_ROUTE = "/settings/teams/new"; export const SETTINGS_ACCOUNT_PROFILE = "/settings/account/profile"; export const SETTINGS_ACCOUNT_PREFERENCES = "/settings/account/preferences"; + +// search +export const SEARCH_PATH = "/search"; + + +// info dropdown_title +export const CUSTOMER_SUPPORT_LINK = "http://scinote.net/support"; +export const TUTORIALS_LINK = "http://scinote.net/product/tutorials/"; +export const RELEASE_NOTES_LINK = "http://scinote.net/docs/release-notes/"; +export const PREMIUM_LINK = "http://scinote.net/premium/"; +export const CONTACT_US_LINK = + "http://scinote.net/story-of-scinote/#contact-scinote"; diff --git a/app/javascript/src/index.jsx b/app/javascript/src/index.jsx index 19e7725f6..335cae4bb 100644 --- a/app/javascript/src/index.jsx +++ b/app/javascript/src/index.jsx @@ -9,7 +9,6 @@ import messages from "./config/locales/messages"; import store from "./config/store"; import AlertsContainer from "./components/AlertsContainer"; -import ModalsContainer from "./components/ModalsContainer"; import SettingsPage from "./scenes/SettingsPage"; import Navigation from "./components/Navigation"; @@ -34,7 +33,6 @@ const ScinoteApp = () => - ; diff --git a/app/javascript/src/scenes/SettingsPage/index.jsx b/app/javascript/src/scenes/SettingsPage/index.jsx index 8125b54ad..82f300155 100644 --- a/app/javascript/src/scenes/SettingsPage/index.jsx +++ b/app/javascript/src/scenes/SettingsPage/index.jsx @@ -7,6 +7,7 @@ import { FormattedMessage } from "react-intl"; import { ROOT_PATH, + SETTINGS_PATH, SETTINGS_TEAMS_ROUTE, SETTINGS_TEAM_ROUTE, SETTINGS_ACCOUNT_PROFILE, @@ -14,8 +15,6 @@ import { SETTINGS_NEW_TEAM_ROUTE } from "../../config/routes"; -import { SETTINGS_PATH, SETTINGS_TEAMS } from "../../config/api_endpoints"; - import PageTitle from "../../components/PageTitle"; import NotFound from "../../components/404/NotFound"; import SettingsProfile from "./scenes/profile"; @@ -74,7 +73,7 @@ export default class SettingsPage extends Component<*, State> { diff --git a/app/javascript/src/scenes/SettingsPage/scenes/profile/components/InputEnabled.jsx b/app/javascript/src/scenes/SettingsPage/scenes/profile/components/InputEnabled.jsx index aebda3857..a4c0b7704 100644 --- a/app/javascript/src/scenes/SettingsPage/scenes/profile/components/InputEnabled.jsx +++ b/app/javascript/src/scenes/SettingsPage/scenes/profile/components/InputEnabled.jsx @@ -9,8 +9,8 @@ import { ButtonToolbar, } from "react-bootstrap"; import update from "immutability-helper"; -import { updateUser } from "../../../../../services/api/users_api"; import { transformName } from "../../../../../services/helpers/string_helper"; +import { updateUser } from "../../../../../services/api/users_api"; import { addAlert } from "../../../../../components/actions/AlertsActions"; import { @@ -259,6 +259,7 @@ class InputEnabled extends Component { return ( { + constructor(props: Props) { super(props); this.state = { description: "" }; - this.onCloseModal = this.onCloseModal.bind(this); - this.updateDescription = this.updateDescription.bind(this); - this.handleDescription = this.handleDescription.bind(this); + (this: any).onCloseModal = this.onCloseModal.bind(this); + (this: any).updateDescription = this.updateDescription.bind(this); + (this: any).handleDescription = this.handleDescription.bind(this); } - onCloseModal() { - this.setState({ description: "" }); + onCloseModal(): void { + (this: any).setState({ description: "" }); this.props.hideModal(); } - handleDescription(el) { - this.setState({ description: el.target.value }); + handleDescription(e: SyntheticInputEvent): void { + (this: any).setState({ description: e.target.value }); } - updateDescription() { - axios({ - method: "post", - url: TEAM_UPDATE_PATH, - withCredentials: true, - data: { - team_id: this.props.team.id, - team: { description: this.state.description } - } - }) + updateDescription(): void { + updateTeam(this.props.team.id, { description: this.state.description }) .then(response => { - this.props.updateTeamCallback(response.data.team); + this.props.updateTeamCallback(response); this.onCloseModal(); }) - .catch(error => this.setState({ errorMessage: error.message })); + .catch(error => { + (this: any).form.setErrorsForTag('description', [error.message]) + }); } - render() { + render(): Node { return ( - { this.form = f; }}> + { + (this: any).form = f; + }} + > @@ -98,14 +104,4 @@ class UpdateTeamDescriptionModal extends Component { } } -UpdateTeamDescriptionModal.propTypes = { - showModal: bool.isRequired, - hideModal: func.isRequired, - team: PropTypes.shape({ - id: number.isRequired, - description: string - }).isRequired, - updateTeamCallback: func.isRequired -}; - export default UpdateTeamDescriptionModal; diff --git a/app/javascript/src/scenes/SettingsPage/scenes/team/components/UpdateTeamNameModal.jsx b/app/javascript/src/scenes/SettingsPage/scenes/team/components/UpdateTeamNameModal.jsx index 6e18cfb24..a82da272c 100644 --- a/app/javascript/src/scenes/SettingsPage/scenes/team/components/UpdateTeamNameModal.jsx +++ b/app/javascript/src/scenes/SettingsPage/scenes/team/components/UpdateTeamNameModal.jsx @@ -1,13 +1,8 @@ +// @flow import React, { Component } from "react"; -import PropTypes, { bool, number, string, func } from "prop-types"; -import { - Modal, - Button, - ControlLabel, - FormControl, -} from "react-bootstrap"; +import type { Node } from "react"; +import { Modal, Button, ControlLabel, FormControl } from "react-bootstrap"; import { FormattedMessage } from "react-intl"; -import axios from "../../../../../config/axios"; import { ValidatedForm, ValidatedFormGroup, @@ -15,53 +10,62 @@ import { ValidatedErrorHelpBlock, ValidatedSubmitButton } from "../../../../../components/validation"; -import { - nameLengthValidator -} from "../../../../../components/validation/validators/text"; +import { nameLengthValidator } from "../../../../../components/validation/validators/text"; +import { updateTeam } from "../../../../../services/api/teams_api"; -import { TEAM_UPDATE_PATH } from "../../../../../config/api_endpoints"; +type Team = { + id: number, + name: string +}; -class UpdateTeamNameModal extends Component { - constructor(props) { +type State = { + name: string +}; + +type Props = { + showModal: boolean, + hideModal: Function, + team: Team, + updateTeamCallback: Function +}; + +class UpdateTeamNameModal extends Component { + constructor(props: Props) { super(props); this.state = { name: props.team.name }; - this.onCloseModal = this.onCloseModal.bind(this); - this.updateName = this.updateName.bind(this); - this.handleName = this.handleName.bind(this); + (this: any).onCloseModal = this.onCloseModal.bind(this); + (this: any).updateName = this.updateName.bind(this); + (this: any).handleName = this.handleName.bind(this); } - onCloseModal() { - this.setState({ name: "" }); + onCloseModal(): void { + (this: any).setState({ name: "" }); this.props.hideModal(); } - handleName(e) { - this.setState({ name: e.target.value }); + handleName(e: SyntheticInputEvent): void { + (this: any).setState({ name: e.target.value }); } - updateName() { - axios({ - method: "post", - url: TEAM_UPDATE_PATH, - withCredentials: true, - data: { - team_id: this.props.team.id, - team: { name: this.state.name } - } - }) + updateName(): void { + updateTeam(this.props.team.id, { name: this.state.name }) .then(response => { - this.props.updateTeamCallback(response.data.team); + this.props.updateTeamCallback(response); this.onCloseModal(); }) .catch(error => { - this.form.setErrorsForTag("name", [error.message]); + (this: any).form.setErrorsForTag("name", [error.message]); }); } - render() { + render(): Node { return ( - { this.form = f; }}> + { + (this: any).form = f; + }} + > @@ -84,10 +88,7 @@ class UpdateTeamNameModal extends Component { - +