diff --git a/app/controllers/client_api/users/users_controller.rb b/app/controllers/client_api/users/users_controller.rb index 6da879ed6..0f37d089b 100644 --- a/app/controllers/client_api/users/users_controller.rb +++ b/app/controllers/client_api/users/users_controller.rb @@ -3,11 +3,15 @@ module ClientApi class UsersController < ApplicationController def preferences_info + settings = current_user.settings respond_to do |format| format.json do render template: 'client_api/users/preferences', status: :ok, - locals: { user: current_user} + locals: { + timeZone: settings['time_zone'], + notifications: settings['notifications'] + } end end end @@ -67,7 +71,9 @@ module ClientApi :full_name, :password_confirmation, :current_password, - :avatar) + :avatar, + :assignments, + :time_zone) end def change_notification(dinamic_param, params) diff --git a/app/javascript/src/config/locales/messages.js b/app/javascript/src/config/locales/messages.js index 07fb397e6..185054f76 100644 --- a/app/javascript/src/config/locales/messages.js +++ b/app/javascript/src/config/locales/messages.js @@ -54,6 +54,7 @@ export default { time_zone: "Time zone", time_zone_warning: "Time zone setting affects all time & date fields throughout application.", + repeat_tutorial: "Repeat tutorial", profile: "Profile", preferences: "Preferences", assignement: "Assignement", diff --git a/app/javascript/src/scenes/SettingsPage/scenes/preferences/components/InputTimezone.jsx b/app/javascript/src/scenes/SettingsPage/scenes/preferences/components/InputTimezone.jsx index fc9b3fb8f..1dc3cf290 100644 --- a/app/javascript/src/scenes/SettingsPage/scenes/preferences/components/InputTimezone.jsx +++ b/app/javascript/src/scenes/SettingsPage/scenes/preferences/components/InputTimezone.jsx @@ -1,11 +1,13 @@ import React, { Component } from "react"; -import PropType from "prop-types"; +import { string, func } from "prop-types"; import { Button } from "react-bootstrap"; import styled from "styled-components"; import TimezonePicker from "react-bootstrap-timezone-picker"; import "react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css"; import { FormattedMessage } from "react-intl"; +import { updateUser } from "../../../../../services/api/users_api"; +import InputDisabled from "../../../components/InputDisabled"; import { BORDER_LIGHT_COLOR } from "../../../../../config/constants/colors"; const Wrapper = styled.div` @@ -22,16 +24,29 @@ const Wrapper = styled.div` } `; +const WrapperInputDisabled = styled.div` + margin: 20px 0; + padding-bottom: 15px; + border-bottom: 1px solid ${BORDER_LIGHT_COLOR}; + + .settings-warning { + margin-top: -5px; + } +`; + class InputTimezone extends Component { constructor(props) { super(props); this.state = { - value: props.inputValue + value: "", + disabled: true }; this.handleChange = this.handleChange.bind(this); this.handleUpdate = this.handleUpdate.bind(this); + this.enableEdit = this.enableEdit.bind(this); + this.disableEdit = this.disableEdit.bind(this); } handleChange(timezone) { @@ -40,20 +55,49 @@ class InputTimezone extends Component { handleUpdate() { if (this.state.value !== "") { - this.props.saveData(this.state.value); + updateUser({ time_zone: this.state.value }).then(() => { + this.disableEdit(); + }); } - this.props.disableEdit(); } + + enableEdit() { + this.setState({ disabled: false, value: this.props.value }); + } + + disableEdit() { + this.setState({ disabled: true }); + this.props.loadPreferences(); + } + render() { + if (this.state.disabled) { + return ( + + +
+ + + +
+
+ ); + } + return (

- {this.props.labelValue} +

@@ -62,11 +106,11 @@ class InputTimezone extends Component { -
); @@ -74,10 +118,8 @@ class InputTimezone extends Component { } InputTimezone.propTypes = { - labelValue: PropType.string.isRequired, - inputValue: PropType.string.isRequired, - disableEdit: PropType.func.isRequired, - saveData: PropType.func.isRequired + value: string.isRequired, + loadPreferences: func.isRequired }; export default InputTimezone; diff --git a/app/javascript/src/scenes/SettingsPage/scenes/preferences/index.jsx b/app/javascript/src/scenes/SettingsPage/scenes/preferences/index.jsx index bd4b5dcb2..3529564ab 100644 --- a/app/javascript/src/scenes/SettingsPage/scenes/preferences/index.jsx +++ b/app/javascript/src/scenes/SettingsPage/scenes/preferences/index.jsx @@ -1,10 +1,9 @@ import React, { Component } from "react"; -import { connect } from "react-redux"; -import PropTypes from "prop-types"; -import styled from "styled-components"; import { FormattedMessage } from "react-intl"; +import { Button } from "react-bootstrap"; +import styled from "styled-components"; -import InputDisabled from "../../components/InputDisabled"; +import { getUserPreferencesInfo } from "../../../../services/api/users_api"; import SettingsAccountWrapper from "../../components/SettingsAccountWrapper"; import InputTimezone from "./components/InputTimezone"; import NotificationsGroup from "./components/NotificationsGroup"; @@ -21,106 +20,50 @@ import { BORDER_LIGHT_COLOR } from "../../../../config/constants/colors"; -const WrapperInputDisabled = styled.div` +const TutorialWrapper = styled.div` margin: 20px 0; padding-bottom: 15px; border-bottom: 1px solid ${BORDER_LIGHT_COLOR}; - - .settings-warning { - margin-top: -5px; - } -`; - +` class SettingsPreferences extends Component { constructor(props) { super(props); this.state = { - isTimeZoneEditable: false, - email: "", - notifications: { - assignmentsNotification: false, - assignmentsNotificationEmail: false, - recentNotification: false, - recentNotificationEmail: false, - systemMessageNofificationEmail: false - } + timeZone: "", + assignmentsNotification: false, + assignmentsEmailNotification: false, + recentNotification: false, + recentEmailNotification: false, + systemMessageEmailNofification: false }; - this.setData = this.setData.bind(this); + this.getPreferencesInfo = this.getPreferencesInfo.bind(this) } componentDidMount() { this.getPreferencesInfo(); } - toggleIsEditable(fieldNameEnabled) { - const editableState = this.state[fieldNameEnabled]; - this.setState({ [fieldNameEnabled]: !editableState }); - } - - setData({ data }) { - const user = data.user; - - const newData = { - timeZone: user.timeZone, - notifications: { - assignmentsNotification: user.notifications.assignmentsNotification, - assignmentsNotificationEmail: - user.notifications.assignmentsNotificationEmail, - recentNotification: user.notifications.recentNotification, - recentNotificationEmail: user.notifications.recentNotificationEmail, - systemMessageNofificationEmail: - user.notifications.systemMessageNofificationEmail - } - }; - - this.setState(Object.assign({}, this.state, newData)); - } - getPreferencesInfo() { - // axios - // .get("/client_api/users/preferences_info") - // .then(response => this.setData(response)) - // .catch(error => console.log(error)); + getUserPreferencesInfo().then(data => { + this.setState(data); + }); } render() { - const isTimeZoneEditable = "isTimeZoneEditable"; - let timezoneField; - - if (this.state.isTimeZoneEditable) { - timezoneField = ( - this.toggleIsEditable(isTimeZoneEditable)} - saveData={timeZone => this.props.changeTimezone(timeZone)} - /> - ); - } else { - timezoneField = ( - - this.toggleIsEditable(isTimeZoneEditable)} - /> -
- - - -
-
- ); - } - return (
- {timezoneField} - + + + +

Notifications

state.current_user; - -export default connect(mapStateToProps)(SettingsPreferences); +export default SettingsPreferences; diff --git a/app/javascript/src/services/api/endpoints.js b/app/javascript/src/services/api/endpoints.js index 1ef90b80c..35f04ed47 100644 --- a/app/javascript/src/services/api/endpoints.js +++ b/app/javascript/src/services/api/endpoints.js @@ -9,6 +9,7 @@ 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"; +export const CURRENT_USER_PATH = "/client_api/current_user_info" // search export const SEARCH_PATH = "/search"; @@ -19,6 +20,7 @@ export const RECENT_NOTIFICATIONS_PATH = "/client_api/recent_notifications"; // users export const USER_PROFILE_INFO = "/client_api/users/profile_info"; export const UPDATE_USER_PATH = "/client_api/users/update"; +export const PREFERENCES_INFO_PATH = "/client_api/users/preferences_info" // info dropdown_title export const CUSTOMER_SUPPORT_LINK = "http://scinote.net/support"; diff --git a/app/javascript/src/services/api/users_api.js b/app/javascript/src/services/api/users_api.js index 7107abb94..952c21d64 100644 --- a/app/javascript/src/services/api/users_api.js +++ b/app/javascript/src/services/api/users_api.js @@ -2,12 +2,15 @@ import { axiosInstance } from "./config"; import { USER_PROFILE_INFO, UPDATE_USER_PATH, - CURRENT_USER_PATH + CURRENT_USER_PATH, + PREFERENCES_INFO_PATH } from "./endpoints"; -export const getUserProfileInfo = () => { - return axiosInstance.get(USER_PROFILE_INFO).then(({ data }) => data.user); -}; +export const getUserProfileInfo = () => + axiosInstance.get(USER_PROFILE_INFO).then(({ data }) => data.user); + +export const getUserPreferencesInfo = () => + axiosInstance.get(PREFERENCES_INFO_PATH).then(({ data }) => data); export const updateUser = (params, formObj = false) => { if (formObj) { diff --git a/app/models/user.rb b/app/models/user.rb index 14a18f417..6f4d2ae1c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -36,7 +36,7 @@ class User < ApplicationRecord size: { less_than: Constants::AVATAR_MAX_SIZE_MB.megabytes } validate :time_zone_check - store_accessor :settings, :time_zone + store_accessor :settings, :time_zone, :notifications default_settings( time_zone: 'UTC', @@ -48,7 +48,31 @@ class User < ApplicationRecord system_message_email: false } ) + # json.assignmentsNotification notifications['assignments'] + # json.assignmentsEmailNotification notifications['assignments_email'] + # json.recentNotification notifications['recent'] + # json.recentEmailNotification notifications['recent_email'] + # json.systemMessageEmailNofification notifications['system_message_email'] + # joson friendly attributes + NOTIFICATIONS_TYPES = %w(assignmentsNotification assignmentsEmailNotification + recentNotification recentEmailNotification + systemMessageEmailNofification) + # declare notifications getters + NOTIFICATIONS_TYPES.each do |name| + define_method(name) do + attr_name = name.slice!('Notification').underscore + self.notifications.fetch(attr_name.to_sym) + end + end + # declare notifications setters + NOTIFICATIONS_TYPES.each do |name| + define_method("#{name}=") do |value| + attr_name = name.slice!('Notification').underscore + self.notifications[attr_name.to_sym] = value + save + end + end # Relations has_many :user_teams, inverse_of: :user has_many :teams, through: :user_teams diff --git a/app/views/client_api/users/preferences.json.jbuilder b/app/views/client_api/users/preferences.json.jbuilder index 493b8874b..4eded607b 100644 --- a/app/views/client_api/users/preferences.json.jbuilder +++ b/app/views/client_api/users/preferences.json.jbuilder @@ -1,10 +1,6 @@ -json.user do - json.timeZone user.time_zone - json.notifications do - json.assignmentsNotification user.assignments_notification - json.assignmentsNotificationEmail user.assignments_notification_email - json.recentNotification user.recent_notification - json.recentNotificationEmail user.recent_notification_email - json.systemMessageNofificationEmail user.system_message_notification_email - end -end +json.timeZone timeZone +json.assignmentsNotification notifications['assignments'] +json.assignmentsEmailNotification notifications['assignments_email'] +json.recentNotification notifications['recent'] +json.recentEmailNotification notifications['recent_email'] +json.systemMessageEmailNofification notifications['system_message_email'] diff --git a/app/views/client_api/users/show.json.jbuilder b/app/views/client_api/users/show.json.jbuilder index 91a8f4081..12a84ab9e 100644 --- a/app/views/client_api/users/show.json.jbuilder +++ b/app/views/client_api/users/show.json.jbuilder @@ -1,5 +1,5 @@ json.user do json.id user.id json.fullName user.full_name - json.avatarPath avatar_path(user, :icon_small) + json.avatarThumb avatar_path(user, :icon_small) end