setup flow

This commit is contained in:
zmagod 2017-10-04 09:16:17 +02:00
parent f8fc2737d5
commit 84f279de22
19 changed files with 126 additions and 185 deletions

View file

@ -12,7 +12,8 @@
}
],
"react",
"es2015"
"es2015",
"flow"
],
"plugins": [
"transform-object-rest-spread",

9
.flowconfig Normal file
View file

@ -0,0 +1,9 @@
[ignore]
[include]
[libs]
[lints]
[options]

View file

@ -1,12 +1,11 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { string, number, func, shape, bool, arrayOf } from "prop-types";
import { FormattedMessage } from "react-intl";
import { Modal, Button } from "react-bootstrap";
import _ from "lodash";
import styled from "styled-components";
import { getActivities } from "../../actions/ActivitiesActions";
import { getActivities } from "../../../services/api/activities_api";
import ActivityElement from "./ActivityElement";
import ActivityDateElement from "./ActivityDateElement";
import {
@ -42,25 +41,26 @@ const StyledBottom = styled(Button)`
const StyledModalBody = styled(Modal.Body)`
background-color: ${COLOR_CONCRETE};
color: ${COLOR_MINE_SHAFT};;
color: ${COLOR_MINE_SHAFT};
`;
class GlobalActivitiesModal extends Component {
constructor(props) {
super(props);
this.state = { activities: this.props.activites, more: this.props.more };
this.displayActivities = this.displayActivities.bind(this);
this.addMoreActivities = this.addMoreActivities.bind(this);
}
displayActivities() {
if (this.props.activities.length === 0) {
if (this.state.activities.length === 0) {
return (
<li>
<FormattedMessage id="activities.no_data" />
</li>
);
}
return this.props.activities.map((activity, i, arr) => {
return this.state.activities.map((activity, i, arr) => {
const newDate = new Date(activity.created_at);
if (i > 0) {
const prevDate = new Date(arr[i - 1].created_at);
@ -81,8 +81,13 @@ class GlobalActivitiesModal extends Component {
}
addMoreActivities() {
const lastId = _.last(this.props.activities).id;
this.props.fetchActivities(lastId);
const lastId = _.last(this.state.activities).id;
getActivities(lastId).then(response => {
this.setState({
activities: [...this.state.activities, ...response.activities],
more: response.more
});
});
}
addMoreButton() {
@ -123,30 +128,16 @@ class GlobalActivitiesModal extends Component {
}
GlobalActivitiesModal.propTypes = {
showModal: PropTypes.bool.isRequired,
onCloseModal: PropTypes.func.isRequired,
fetchActivities: PropTypes.func.isRequired,
more: PropTypes.bool.isRequired,
activities: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
message: PropTypes.string.isRequired,
created_at: PropTypes.string.isRequired
showModal: bool.isRequired,
more: bool.isRequired,
activites: arrayOf(
shape({
id: number.isRequired,
message: string.isRequired,
created_at: string.isRequired
})
).isRequired
).isRequired,
onCloseModal: func.isRequired
};
const mapStateToProps = ({ global_activities }) => {
const { activities, more } = global_activities;
return { activities, more };
};
const mapDispatchToProps = dispatch => ({
fetchActivities(lastId) {
dispatch(getActivities(lastId));
}
});
export default connect(mapStateToProps, mapDispatchToProps)(
GlobalActivitiesModal
);
export default GlobalActivitiesModal;

View file

@ -9,7 +9,8 @@ import {
WHITE_COLOR,
BORDER_GRAY_COLOR
} from "../../config/constants/colors";
import { getActivities, destroyActivities } from "../actions/ActivitiesActions";
import { getActivities } from "../../services/api/activities_api";
import TeamSwitch from "./components/TeamSwitch";
import GlobalActivitiesModal from "./components/GlobalActivitiesModal";
import SearchDropdown from "./components/SearchDropdown";
@ -44,6 +45,8 @@ class Navigation extends Component {
super(props);
this.state = {
showActivitesModal: false,
activities: [],
more: false,
current_team: { id: 0 }
};
this.selectItemCallback = this.selectItemCallback.bind(this);
@ -64,8 +67,15 @@ class Navigation extends Component {
case 4:
ev.preventDefault();
this.setState({ showActivitesModal: !this.state.showActivitesModal });
// Call action creator to fetch activities from the server
this.props.fetchActivities();
// load activites if modal is shown
if (this.state.showActivitesModal) {
getActivities().then(response => {
this.setState({
activities: response.activities,
more: response.more
});
});
}
break;
default:
}
@ -73,7 +83,6 @@ class Navigation extends Component {
closeModalCallback() {
this.setState({ showActivitesModal: false });
this.props.destroyActivities();
}
render() {
@ -132,6 +141,8 @@ class Navigation extends Component {
</Nav>
</StyledNavbar>
<GlobalActivitiesModal
activities={this.state.activites}
more={this.state.more}
showModal={this.state.showActivitesModal}
onCloseModal={this.closeModalCallback}
/>
@ -141,8 +152,6 @@ class Navigation extends Component {
}
Navigation.propTypes = {
fetchActivities: PropTypes.func.isRequired,
destroyActivities: PropTypes.func.isRequired,
current_team: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
@ -153,14 +162,4 @@ Navigation.propTypes = {
// Map the states from store to component props
const mapStateToProps = ({ current_team }) => ({ current_team });
// Map the fetch activity action to component props
const mapDispatchToProps = dispatch => ({
fetchActivities() {
dispatch(getActivities());
},
destroyActivities() {
dispatch(destroyActivities());
}
});
export default connect(mapStateToProps, mapDispatchToProps)(Navigation);
export default connect(mapStateToProps)(Navigation);

View file

@ -1,47 +0,0 @@
import axios from "../../config/axios";
import { ACTIVITIES_PATH } from "../../config/api_endpoints";
import {
GLOBAL_ACTIVITIES_DATA,
DESTROY_GLOBAL_ACTIVITIES_DATA,
SPINNER_ON,
SPINNER_OFF
} from "../../config/action_types";
function addActivitiesData(data) {
return {
type: GLOBAL_ACTIVITIES_DATA,
payload: data
};
}
export function destroyActivities() {
return {
type: DESTROY_GLOBAL_ACTIVITIES_DATA
};
}
export function getActivities(lastId = 0) {
return dispatch => {
const path = `${ACTIVITIES_PATH}?from=${lastId}`;
axios
.get(path, { withCredentials: true })
.then(response => {
dispatch(addActivitiesData(response.data));
})
.catch(error => {
console.log("get Activites Error: ", error);
});
};
}
export function spinnerOn() {
return {
type: SPINNER_ON
};
}
export function spinnerOff() {
return {
type: SPINNER_OFF
};
}

View file

@ -1,33 +0,0 @@
import {
GLOBAL_ACTIVITIES_DATA,
DESTROY_GLOBAL_ACTIVITIES_DATA,
SPINNER_OFF,
SPINNER_ON
} from "../../config/action_types";
const initialStateu = { more: true, activities: [], spinner_on: false };
export function globalActivities(state = initialStateu, action) {
switch (action.type) {
case GLOBAL_ACTIVITIES_DATA:
return {
...state,
activities: [
...state.activities,
...action.payload.global_activities.activities
],
more: action.payload.global_activities.more
};
case DESTROY_GLOBAL_ACTIVITIES_DATA:
return {
...state,
...initialStateu
};
case SPINNER_OFF:
return Object.assign({}, state, { spinner_on: false });
case SPINNER_ON:
return Object.assign({}, state, { spinner_on: true });
default:
return state;
}
}

View file

@ -3,10 +3,6 @@ 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";
// activities
export const GLOBAL_ACTIVITIES_DATA = "GLOBAL_ACTIVITIES_DATA";
export const DESTROY_GLOBAL_ACTIVITIES_DATA = "DESTROY_GLOBAL_ACTIVITIES_DATA";
// users
export const SET_CURRENT_USER = "SET_CURRENT_USER";
export const CHANGE_CURRENT_USER_FULL_NAME = "CHANGE_CURRENT_USER_FULL_NAME";
@ -39,4 +35,4 @@ export const SPINNER_OFF = "SPINNER_OFF";
// alerts
export const ADD_ALERT = "ADD_ALERT";
export const CLEAR_ALERT = "CLEAR_ALERT";
export const CLEAR_ALL_ALERTS = "CLEAR_ALL_ALERTS";
export const CLEAR_ALL_ALERTS = "CLEAR_ALL_ALERTS";

View file

@ -1,5 +1,3 @@
// activities
export const ACTIVITIES_PATH = "/client_api/activities";
// settings
export const SETTINGS_PATH = "/settings";

View file

@ -4,14 +4,12 @@ import {
getListOfTeams,
showLeaveTeamModal,
} from "../components/reducers/TeamReducers";
import { globalActivities } from "../components/reducers/ActivitiesReducers";
import { currentUser } from "../components/reducers/UsersReducer";
import { alerts } from "../components/reducers/AlertsReducers";
export default combineReducers({
current_team: setCurrentTeam,
all_teams: getListOfTeams,
global_activities: globalActivities,
current_user: currentUser,
showLeaveTeamModal,
alerts

View file

@ -0,0 +1,7 @@
import { axiosInstance } from "./config";
import { ACTIVITIES_PATH } from "./endpoints";
export function getActivities(lastId = 0) {
const path = `${ACTIVITIES_PATH}?from=${lastId}`;
axiosInstance.get(path).then(({ data }) => data);
}

View file

@ -0,0 +1,8 @@
import axios from "axios";
export const axiosInstance = axios.create({
withCredentials: true,
headers: {
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').content
}
});

View file

@ -0,0 +1,2 @@
// activities
export const ACTIVITIES_PATH = "/client_api/activities";

View file

@ -1,5 +1,10 @@
Rails.application.routes.draw do
require 'subdomain'
def draw(routes_name)
instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
end
constraints UserSubdomain do
devise_for :users, controllers: { registrations: 'users/registrations',
sessions: 'users/sessions',
@ -13,50 +18,8 @@ Rails.application.routes.draw do
get '/settings/*all', to: 'client_api/settings#index'
namespace :client_api, defaults: { format: 'json' } do
# activities
get '/activities', to: 'activities#index'
# teams
get '/teams', to: 'teams/teams#index'
namespace :teams do
get '/new', to: 'teams#new'
get '/:team_id/details', to: 'teams#details'
post '/', to: 'teams#create'
post '/change_team', to: 'teams#change_team'
post '/update', to: 'teams#update'
end
# notifications
get '/recent_notifications', to: 'notifications#recent_notifications'
# users
get '/current_user_info', to: 'users/users#current_user_info'
namespace :users do
delete '/remove_user', to: 'user_teams#remove_user'
delete '/leave_team', to: 'user_teams#leave_team'
put '/update_role', to: 'user_teams#update_role'
get '/profile_info', to: 'users#profile_info'
get '/statistics_info', to: 'users#statistics_info'
get '/preferences_info', to: 'users#preferences_info'
delete '/leave_team', to: 'user_teams#leave_team'
post '/change_full_name', to: 'users#change_full_name'
post '/change_initials', to: 'users#change_initials'
post '/change_email', to: 'users#change_email'
post '/change_password', to: 'users#change_password'
post '/change_timezone', to: 'users#change_timezone'
post '/change_assignements_notification',
to: 'users#change_assignements_notification'
post '/change_assignements_notification_email',
to: 'users#change_assignements_notification_email'
post '/change_recent_notification',
to: 'users#change_recent_notification'
post '/change_recent_notification_email',
to: 'users#change_recent_notification_email'
post '/change_system_notification_email',
to: 'users#change_system_notification_email'
devise_scope :user do
put '/invite_users', to: 'invitations#invite_users'
end
%i(activities teams notifications users).each do |path|
draw path
end
end

View file

@ -0,0 +1,2 @@
# activities
get '/activities', to: 'activities#index'

View file

@ -0,0 +1,2 @@
# notifications
get '/recent_notifications', to: 'notifications#recent_notifications'

10
config/routes/teams.rb Normal file
View file

@ -0,0 +1,10 @@
# teams
get '/teams', to: 'teams/teams#index'
namespace :teams do
get '/new', to: 'teams#new'
get '/:team_id/details', to: 'teams#details'
post '/', to: 'teams#create'
post '/change_team', to: 'teams#change_team'
post '/update', to: 'teams#update'
end

30
config/routes/users.rb Normal file
View file

@ -0,0 +1,30 @@
# users
get '/current_user_info', to: 'users/users#current_user_info'
namespace :users do
delete '/remove_user', to: 'user_teams#remove_user'
delete '/leave_team', to: 'user_teams#leave_team'
put '/update_role', to: 'user_teams#update_role'
get '/profile_info', to: 'users#profile_info'
get '/statistics_info', to: 'users#statistics_info'
get '/preferences_info', to: 'users#preferences_info'
delete '/leave_team', to: 'user_teams#leave_team'
post '/change_full_name', to: 'users#change_full_name'
post '/change_initials', to: 'users#change_initials'
post '/change_email', to: 'users#change_email'
post '/change_password', to: 'users#change_password'
post '/change_timezone', to: 'users#change_timezone'
post '/change_assignements_notification',
to: 'users#change_assignements_notification'
post '/change_assignements_notification_email',
to: 'users#change_assignements_notification_email'
post '/change_recent_notification',
to: 'users#change_recent_notification'
post '/change_recent_notification_email',
to: 'users#change_recent_notification_email'
post '/change_system_notification_email',
to: 'users#change_system_notification_email'
devise_scope :user do
put '/invite_users', to: 'invitations#invite_users'
end
end

View file

@ -29,6 +29,7 @@
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-prettier": "^2.1.2",
"eslint-plugin-react": "^7.1.0",
"flow-bin": "^0.56.0",
"prettier": "^1.7.0",
"webpack-dev-server": "^2.5.1"
},
@ -68,8 +69,6 @@
"react-bootstrap-table": "^4.0.0",
"react-bootstrap-timezone-picker": "^1.0.11",
"react-data-grid": "^2.0.2",
"react-tagsinput": "^3.17.0",
"react-transition-group": "^2.2.0",
"react-dom": "^15.6.1",
"react-intl": "^2.3.0",
"react-intl-redux": "^0.6.0",
@ -78,7 +77,9 @@
"react-router-bootstrap": "^0.24.2",
"react-router-dom": "^4.1.2",
"react-router-prop-types": "^0.0.1",
"react-tagsinput": "^3.17.0",
"react-timezone": "^0.2.0",
"react-transition-group": "^2.2.0",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0",
"resolve-url-loader": "^2.1.0",

View file

@ -2357,6 +2357,10 @@ flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
flow-bin@^0.56.0:
version "0.56.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.56.0.tgz#ce43092203a344ba9bf63c0cabe95d95145f6cad"
follow-redirects@^1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.4.tgz#355e8f4d16876b43f577b0d5ce2668b9723214ea"