diff --git a/app/javascript/packs/app/action_types.js b/app/javascript/packs/app/action_types.js
index c4ee85503..4a2ee313a 100644
--- a/app/javascript/packs/app/action_types.js
+++ b/app/javascript/packs/app/action_types.js
@@ -12,3 +12,4 @@ export const CHANGE_CURRENT_USER_INITIALS = "CHANGE_CURRENT_USER_INITIALS";
export const CHANGE_CURRENT_USER_EMAIL = "CHANGE_CURRENT_USER_EMAIL";
export const CHANGE_CURRENT_USER_PASSWORD = "CHANGE_CURRENT_USER_PASSWORD";
export const CHANGE_CURRENT_USER_AVATAR = "CHANGE_CURRENT_USER_AVATAR";
+export const CHANGE_CURRENT_USER_TIMEZONE = "CHANGE_CURRENT_USER_TIMEZONE";
diff --git a/app/javascript/packs/shared/actions/UsersActions.js b/app/javascript/packs/shared/actions/UsersActions.js
index 7aa6a114a..4c4ff3b8d 100644
--- a/app/javascript/packs/shared/actions/UsersActions.js
+++ b/app/javascript/packs/shared/actions/UsersActions.js
@@ -6,7 +6,8 @@ import {
CHANGE_CURRENT_USER_INITIALS,
CHANGE_CURRENT_USER_EMAIL,
CHANGE_CURRENT_USER_PASSWORD,
- CHANGE_CURRENT_USER_AVATAR
+ CHANGE_CURRENT_USER_AVATAR,
+ CHANGE_CURRENT_USER_TIMEZONE
} from "../../app/action_types";
function addCurrentUser(data) {
@@ -63,3 +64,10 @@ export function changeAvatar(avatarSrc) {
payload: avatarSrc
};
}
+
+export function changeTimezone(timezone) {
+ return {
+ type: CHANGE_CURRENT_USER_TIMEZONE,
+ payload: timezone
+ };
+}
diff --git a/app/javascript/packs/shared/reducers/UsersReducer.js b/app/javascript/packs/shared/reducers/UsersReducer.js
index aa1a49a87..32a1ca6d5 100644
--- a/app/javascript/packs/shared/reducers/UsersReducer.js
+++ b/app/javascript/packs/shared/reducers/UsersReducer.js
@@ -4,7 +4,8 @@ import {
CHANGE_CURRENT_USER_INITIALS,
CHANGE_CURRENT_USER_EMAIL,
CHANGE_CURRENT_USER_PASSWORD,
- CHANGE_CURRENT_USER_AVATAR
+ CHANGE_CURRENT_USER_AVATAR,
+ CHANGE_CURRENT_USER_TIMEZONE
} from "../../app/action_types";
export function currentUser(
@@ -14,7 +15,8 @@ export function currentUser(
initials: "",
email: "",
avatarPath: "",
- avatarThumbPath: ""
+ avatarThumbPath: "",
+ timezone: ""
},
action
) {
@@ -33,6 +35,8 @@ export function currentUser(
return state;
case CHANGE_CURRENT_USER_AVATAR:
return Object.assign({}, state, { avatar: action.payload });
+ case CHANGE_CURRENT_USER_TIMEZONE:
+ return Object.assign({}, state, { timezone: action.payload });
default:
return state;
}
diff --git a/app/javascript/packs/src/settings/components/Avatar.jsx b/app/javascript/packs/src/settings/components/Avatar.jsx
index ad976c8fb..530537593 100644
--- a/app/javascript/packs/src/settings/components/Avatar.jsx
+++ b/app/javascript/packs/src/settings/components/Avatar.jsx
@@ -6,6 +6,7 @@ const AvatarWrapper = styled.div`
width: 100px;
height: 100px;
position: relative;
+ cursor: pointer;
`;
const EditAvatar = styled.span`
position: absolute;
diff --git a/app/javascript/packs/src/settings/components/InputTimezone.jsx b/app/javascript/packs/src/settings/components/InputTimezone.jsx
new file mode 100644
index 000000000..175e3d849
--- /dev/null
+++ b/app/javascript/packs/src/settings/components/InputTimezone.jsx
@@ -0,0 +1,61 @@
+import React, { Component } from "react";
+import PropType from "prop-types";
+import { Button } from "react-bootstrap";
+import TimezonePicker from "react-bootstrap-timezone-picker";
+import "react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css";
+
+class InputTimezone extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ value: props.inputValue
+ };
+
+ this.handleChange = this.handleChange.bind(this);
+ this.handleUpdate = this.handleUpdate.bind(this);
+ }
+
+ handleChange(timezone) {
+ this.setState({ value: timezone });
+ }
+
+ handleUpdate() {
+ if (this.state.value !== "") {
+ this.props.saveData(this.state.value);
+ }
+ this.props.disableEdit();
+ }
+ render() {
+ return (
+
+
+ {this.props.labelValue}
+
+
+
+
+
+ );
+ }
+}
+
+InputTimezone.propTypes = {
+ labelValue: PropType.string.isRequired,
+ inputValue: PropType.string.isRequired,
+ disableEdit: PropType.func.isRequired,
+ saveData: PropType.func.isRequired
+};
+
+export default InputTimezone;
diff --git a/app/javascript/packs/src/settings/components/MyStatistics.jsx b/app/javascript/packs/src/settings/components/MyStatistics.jsx
index 287bbc6cb..10bce4a1d 100644
--- a/app/javascript/packs/src/settings/components/MyStatistics.jsx
+++ b/app/javascript/packs/src/settings/components/MyStatistics.jsx
@@ -15,7 +15,7 @@ class MyStatistics extends Component {
this.toggleIsEditable(isTimeZoneEditable)}
+ saveData={timeZone => this.props.changeTimezone(timeZone)}
+ />
+ );
+ } else {
+ timezoneField = (
+ this.toggleIsEditable(isTimeZoneEditable)}
+ />
+ );
+ }
+
+ return (
+
+ {timezoneField}
+
+ Time zone setting affects all time & date fields throughout
+ application.
+
+
+ );
+ }
+}
+
+SettingsPreferences.propTypes = {
+ timezone: PropTypes.string.isRequired,
+ changeTimezone: PropTypes.func.isRequired
+};
+
+const mapStateToProps = state => state.current_user;
+const mapDispatchToProps = dispatch => ({
+ changeTimezone(timezone) {
+ dispatch(changeTimezone(timezone));
+ }
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(
+ SettingsPreferences
+);
diff --git a/app/javascript/packs/styles/main.scss b/app/javascript/packs/styles/main.scss
index 975dbeb83..7744764ca 100644
--- a/app/javascript/packs/styles/main.scss
+++ b/app/javascript/packs/styles/main.scss
@@ -3,6 +3,27 @@
body {
background-color: $color-concrete;
color: $color-emperor;
- font-family: "Open Sans",Arial,Helvetica,sans-serif;
+ font-family: "Open Sans", Arial, Helvetica, sans-serif;
font-size: 13px;
}
+
+.time-zone-container {
+ background: $color-white;
+ ul {
+ width: 100%;
+ height: 70vh;
+ overflow: auto;
+ position: relative;
+ padding-left: 5px;
+ }
+
+ li {
+ list-style-type: none;
+ line-height: 1.4 em;
+ font-size: 1.5 em;
+ cursor: pointer;
+ &:hover {
+ background: $color-gainsboro;
+ }
+ }
+}
diff --git a/app/views/client_api/users/show.json.jbuilder b/app/views/client_api/users/show.json.jbuilder
index 6a5c034cf..c60f96122 100644
--- a/app/views/client_api/users/show.json.jbuilder
+++ b/app/views/client_api/users/show.json.jbuilder
@@ -6,4 +6,5 @@ json.user do
json.avatarPath avatar_path(user, :icon_small)
json.avatarThumbPath avatar_path(user, :thumb)
json.statistics user.statistics
+ json.timezone user.time_zone
end
diff --git a/package-lock.json b/package-lock.json
index 9e52cef83..45abc42ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7687,6 +7687,15 @@
"warning": "3.0.0"
}
},
+ "react-bootstrap-timezone-picker": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/react-bootstrap-timezone-picker/-/react-bootstrap-timezone-picker-1.0.11.tgz",
+ "integrity": "sha1-IQooSuNcCjJDVHuupGV9eKv5aoY=",
+ "requires": {
+ "classnames": "2.2.5",
+ "prop-types": "15.5.10"
+ }
+ },
"react-dom": {
"version": "15.6.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz",
@@ -7800,6 +7809,14 @@
"react-router": "4.1.2"
}
},
+ "react-timezone": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/react-timezone/-/react-timezone-0.2.0.tgz",
+ "integrity": "sha1-AxttcAsV623Zd+MmsF/pCAuKK20=",
+ "requires": {
+ "classnames": "2.2.5"
+ }
+ },
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
diff --git a/package.json b/package.json
index 2f80c6f0e..218cf51db 100644
--- a/package.json
+++ b/package.json
@@ -62,12 +62,14 @@
"rails-erb-loader": "^5.0.2",
"react": "^15.6.1",
"react-bootstrap": "^0.31.1",
+ "react-bootstrap-timezone-picker": "^1.0.11",
"react-dom": "^15.6.1",
"react-intl": "^2.3.0",
"react-intl-redux": "^0.6.0",
"react-redux": "^5.0.5",
"react-router-bootstrap": "^0.24.2",
"react-router-dom": "^4.1.2",
+ "react-timezone": "^0.2.0",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"resolve-url-loader": "^2.1.0",