From eb0eaa5e1d353f65c3f1586d70d9caed30a78736 Mon Sep 17 00:00:00 2001 From: Jonathon Kelly Date: Thu, 6 Apr 2017 10:48:10 +0100 Subject: [PATCH] start refactoring --- package.json | 2 +- public/actions/section.js | 75 -------- public/components/SectionList.jsx | 12 +- public/components/SectionListItem.jsx | 17 +- public/constants/actionTypes.js | 22 --- public/containers/CreateSectionPage.jsx | 2 +- public/containers/LandingPage.jsx | 2 +- public/containers/SectionsPage.jsx | 99 ---------- public/containers/SectionsPage/index.jsx | 88 +++++++++ public/containers/SectionsPage/index.test.jsx | 0 public/containers/UpdateSectionPage.jsx | 2 +- public/reducers/section.js | 45 ----- public/reducers/sections.js | 39 ---- public/redux/index.js | 4 +- public/redux/sections.js | 177 ++++++++++++++++++ public/redux/sections.test.js | 0 public/redux/users.js | 10 +- public/types/index.js | 45 ++++- yarn.lock | 22 +-- 19 files changed, 334 insertions(+), 329 deletions(-) delete mode 100644 public/actions/section.js delete mode 100644 public/containers/SectionsPage.jsx create mode 100644 public/containers/SectionsPage/index.jsx create mode 100644 public/containers/SectionsPage/index.test.jsx delete mode 100644 public/reducers/section.js delete mode 100644 public/reducers/sections.js create mode 100644 public/redux/sections.js create mode 100644 public/redux/sections.test.js diff --git a/package.json b/package.json index e4c57eb..b612f5c 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "errorhandler": "1.5.0", "eslint": "3.19.0", "eslint-config-airbnb": "14.1.0", - "eslint-config-prettier": "1.5.0", + "eslint-config-prettier": "1.6.0", "eslint-loader": "1.7.1", "eslint-plugin-flowtype": "2.30.4", "eslint-plugin-import": "2.2.0", diff --git a/public/actions/section.js b/public/actions/section.js deleted file mode 100644 index 35c7ea2..0000000 --- a/public/actions/section.js +++ /dev/null @@ -1,75 +0,0 @@ -import { browserHistory } from 'react-router'; -import { createAction } from 'redux-actions'; -import { success } from '../redux/notifications'; -import { CALL_API } from '../middleware/api'; -import * as TYPES from '../constants/actionTypes'; -import { HTTP_METHODS } from '../constants/api'; -import { SECTIONS_ROUTE } from '../constants/routes'; - -export const moveSection = createAction(TYPES.MOVE_SECTION); - -export function loadSections() { - return { - [CALL_API]: { - endpoint: 'section', - method: HTTP_METHODS.GET, - types: [TYPES.LOAD_SECTIONS_REQUEST, TYPES.LOAD_SECTIONS_SUCCESS, TYPES.LOAD_SECTIONS_ERROR], - }, - }; -} - -export function loadSection(id) { - return { - [CALL_API]: { - endpoint: `section/${id}`, - method: HTTP_METHODS.GET, - authenticated: true, - types: [TYPES.LOAD_SECTION_REQUEST, TYPES.LOAD_SECTION_SUCCESS, TYPES.LOAD_SECTION_ERROR], - }, - }; -} - -export function createSection(data) { - return { - [CALL_API]: { - data, - endpoint: 'section', - method: HTTP_METHODS.POST, - authenticated: true, - onSuccess: dispatch => { - dispatch(success({ message: 'Section created successfully' })); - browserHistory.push(SECTIONS_ROUTE); - }, - types: [TYPES.CREATE_SECTION_REQUEST, TYPES.CREATE_SECTION_SUCCESS, TYPES.CREATE_SECTION_ERROR], - }, - }; -} - -export function updateSection(data) { - return { - [CALL_API]: { - data, - endpoint: `section/${data.id}`, - method: HTTP_METHODS.PUT, - authenticated: true, - onSuccess: dispatch => { - dispatch(success({ message: 'Section updated successfully' })); - }, - types: [TYPES.UPDATE_SECTION_REQUEST, TYPES.UPDATE_SECTION_SUCCESS, TYPES.UPDATE_SECTION_ERROR], - }, - }; -} - -export function deleteSection({ id }) { - return { - [CALL_API]: { - endpoint: `section/${id}`, - method: HTTP_METHODS.DELETE, - authenticated: true, - onSuccess: dispatch => { - dispatch(success({ message: 'Section deleted successfully' })); - }, - types: [TYPES.DELETE_SECTION_REQUEST, TYPES.DELETE_SECTION_SUCCESS, TYPES.DELETE_SECTION_ERROR], - }, - }; -} diff --git a/public/components/SectionList.jsx b/public/components/SectionList.jsx index 363c7e3..a28df23 100644 --- a/public/components/SectionList.jsx +++ b/public/components/SectionList.jsx @@ -7,23 +7,19 @@ import Loader from './Loader'; import SortableContainer from './SortableContainer'; import SortableItem from './SortableItem'; import SectionListItem from './SectionListItem'; +import type { SectionsType } from '../types'; type PropsType = { loading: boolean, - sections: Array<{ - id: number, - title: string, - hidden: boolean, - }>, + sections: SectionsType, onAdd: Function, onSelect: Function, onMove: Function, onDrop: Function, - onToggleVisibility: Function, onDelete: Function, }; -export default function SectionList({ loading, sections, onAdd, onSelect, onMove, onDrop, onToggleVisibility, onDelete }: PropsType) { +export default function SectionList({ loading, sections, onAdd, onSelect, onMove, onDrop, onDelete }: PropsType) { return ( @@ -42,7 +38,7 @@ export default function SectionList({ loading, sections, onAdd, onSelect, onMove {sections.map(section => ( - + diff --git a/public/components/SectionListItem.jsx b/public/components/SectionListItem.jsx index e52c5ca..cec4822 100644 --- a/public/components/SectionListItem.jsx +++ b/public/components/SectionListItem.jsx @@ -6,29 +6,20 @@ import Edit from 'material-ui/svg-icons/image/edit'; import Delete from 'material-ui/svg-icons/action/delete'; import Visibility from 'material-ui/svg-icons/action/visibility'; import VisibilityOff from 'material-ui/svg-icons/action/visibility-off'; +import type { SectionType } from '../types'; import css from './SectionListItem.styl'; type PropsType = { - section: { - title: string, - hidden: boolean, - }, + section: SectionType, onSelect: Function, - onToggleVisibility: Function, onDelete: Function, }; -export default class SectionListItem extends Component { - props: PropsType; - +export default class SectionListItem extends Component { onSelect = () => { this.props.onSelect(this.props.section); }; - onToggleVisibility = () => { - this.props.onToggleVisibility(this.props.section); - }; - onDelete = () => { this.props.onDelete(this.props.section); }; @@ -48,7 +39,7 @@ export default class SectionListItem extends Component { - + {hidden ? : } diff --git a/public/constants/actionTypes.js b/public/constants/actionTypes.js index 26f5b84..82031bf 100644 --- a/public/constants/actionTypes.js +++ b/public/constants/actionTypes.js @@ -76,28 +76,6 @@ export const DELETE_GIFT_REQUEST = 'DELETE_GIFT_REQUEST'; export const DELETE_GIFT_SUCCESS = 'DELETE_GIFT_SUCCESS'; export const DELETE_GIFT_ERROR = 'DELETE_GIFT_ERROR'; -export const LOAD_SECTIONS_REQUEST = 'LOAD_SECTIONS_REQUEST'; -export const LOAD_SECTIONS_SUCCESS = 'LOAD_SECTIONS_SUCCESS'; -export const LOAD_SECTIONS_ERROR = 'LOAD_SECTIONS_ERROR'; - -export const LOAD_SECTION_REQUEST = 'LOAD_SECTION_REQUEST'; -export const LOAD_SECTION_SUCCESS = 'LOAD_SECTION_SUCCESS'; -export const LOAD_SECTION_ERROR = 'LOAD_SECTION_ERROR'; - -export const CREATE_SECTION_REQUEST = 'CREATE_SECTION_REQUEST'; -export const CREATE_SECTION_SUCCESS = 'CREATE_SECTION_SUCCESS'; -export const CREATE_SECTION_ERROR = 'CREATE_SECTION_ERROR'; - -export const UPDATE_SECTION_REQUEST = 'UPDATE_SECTION_REQUEST'; -export const UPDATE_SECTION_SUCCESS = 'UPDATE_SECTION_SUCCESS'; -export const UPDATE_SECTION_ERROR = 'UPDATE_SECTION_ERROR'; - -export const DELETE_SECTION_REQUEST = 'DELETE_SECTION_REQUEST'; -export const DELETE_SECTION_SUCCESS = 'DELETE_SECTION_SUCCESS'; -export const DELETE_SECTION_ERROR = 'DELETE_SECTION_ERROR'; - -export const MOVE_SECTION = 'MOVE_SECTION'; - export const LOAD_GIFT_SETS_REQUEST = 'LOAD_GIFT_SETS_REQUEST'; export const LOAD_GIFT_SETS_SUCCESS = 'LOAD_GIFT_SETS_SUCCESS'; export const LOAD_GIFT_SETS_ERROR = 'LOAD_GIFT_SETS_ERROR'; diff --git a/public/containers/CreateSectionPage.jsx b/public/containers/CreateSectionPage.jsx index 8ef19c5..3d80b87 100644 --- a/public/containers/CreateSectionPage.jsx +++ b/public/containers/CreateSectionPage.jsx @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import * as actions from '../actions/section'; +import * as actions from '../redux/sections'; import SectionForm from '../components/SectionForm'; type PropsType = { diff --git a/public/containers/LandingPage.jsx b/public/containers/LandingPage.jsx index 7f20cf3..09fd637 100644 --- a/public/containers/LandingPage.jsx +++ b/public/containers/LandingPage.jsx @@ -5,7 +5,7 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import moment from 'moment'; import * as weddingProfileActions from '../actions/weddingProfile'; -import * as sectionActions from '../actions/section'; +import * as sectionActions from '../redux/sections'; import * as weddingPartyMembersActions from '../actions/weddingPartyMember'; import * as giftActions from '../actions/gift'; import * as basketActions from '../redux/basket'; diff --git a/public/containers/SectionsPage.jsx b/public/containers/SectionsPage.jsx deleted file mode 100644 index 391d083..0000000 --- a/public/containers/SectionsPage.jsx +++ /dev/null @@ -1,99 +0,0 @@ -/* @flow */ - -import React, { Component } from 'react'; -import { withRouter } from 'react-router'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import * as actions from '../actions/section'; -import { CREATE_SECTION_ROUTE, updateSectionRoute } from '../constants/routes'; -import SectionList from '../components/SectionList'; - -type PropsType = { - loading: boolean, - deleting: boolean, - sections: Array<{ - id: number, - title: string, - hidden: boolean, - }>, - actions: { - loadSections: Function, - updateSection: Function, - deleteSection: Function, - moveSection: Function, - }, - router: { - push: Function, - }, -}; - -@withRouter -@connect( - ({ sections: { sections, ...state } }) => { - const sortedSections = sections.sort((a, b) => a.position - b.position); - - return { - ...state, - sections: sortedSections, - }; - }, - dispatch => ({ actions: bindActionCreators(actions, dispatch) }) -) -export default class SectionsPage extends Component { - props: PropsType; - - componentDidMount() { - this.props.actions.loadSections(); - } - - // FIXME: This seems like a bit of a hack - componentWillReceiveProps({ deleting: nextDeleting }: PropsType) { - const { deleting, actions: { loadSections } } = this.props; - - if (deleting && !nextDeleting) { - loadSections(); - } - } - - onAdd = () => { - this.props.router.push(CREATE_SECTION_ROUTE); - }; - - onSelect = ({ id }: Object) => { - this.props.router.push(updateSectionRoute(id)); - }; - - onToggleVisibility = (/* section: Object */) => { - alert('coming soon!'); - }; - - onDrop = ({ id }: Object) => { - const sections = this.props.sections.find(o => o.id === id); - this.props.actions.updateSection(sections); - }; - - onDelete = (section: Object) => { - if (!confirm('Are you sure you want to delete this section?')) { - return; - } - - this.props.actions.deleteSection(section); - }; - - render() { - const { loading, sections, actions: { moveSection } } = this.props; - - return ( - - ); - } -} diff --git a/public/containers/SectionsPage/index.jsx b/public/containers/SectionsPage/index.jsx new file mode 100644 index 0000000..d550967 --- /dev/null +++ b/public/containers/SectionsPage/index.jsx @@ -0,0 +1,88 @@ +/* @flow */ + +import React, { Component } from 'react'; +import { withRouter } from 'react-router'; +import { connect } from 'react-redux'; +import type { Connector } from 'react-redux'; +import { loadSections, updateSection, deleteSection, moveSection } from '../../redux/sections'; +import { CREATE_SECTION_ROUTE, updateSectionRoute } from '../../constants/routes'; +import SectionList from '../../components/SectionList'; +import type { SectionsType, SectionType, SectionIdType } from '../../types'; + +type PropsType = { + loading: boolean, + deleting: boolean, + sections: SectionsType, + loadSections: () => void, + updateSection: (section: SectionType) => void, + deleteSection: (section: SectionType) => void, + moveSection: ({ sourceId: SectionIdType, targetId: SectionIdType }) => void, + router: { + push: (route: string) => void, + }, +}; + +const mapStateToProps = ({ sections: { sections, isLoading, isDeleting } }) => { + const sortedSections = sections.sort((a, b) => a.position - b.position); + + return { + loading: isLoading, + deleting: isDeleting, + sections: sortedSections, + }; +}; + +const mapDispatchToProps = { loadSections, updateSection, deleteSection, moveSection }; + +export class SectionsPage extends Component { + componentDidMount() { + this.props.loadSections(); + } + + onAdd = () => { + this.props.router.push(CREATE_SECTION_ROUTE); + }; + + onSelect = ({ id }: SectionType) => { + this.props.router.push(updateSectionRoute(id)); + }; + + onDrop = ({ id }: SectionType) => { + const section = this.props.sections.find(o => o.id === id); + + if (!section) { + // TODO: Error? + return; + } + + this.props.updateSection(section); + }; + + onDelete = (section: SectionType) => { + if (!confirm('Are you sure you want to delete this section?')) { + return; + } + + this.props.deleteSection(section); + }; + + render() { + const { loading, sections, moveSection: onMove } = this.props; + + return ( + + ); + } +} + +const connector: Connector = connect(mapStateToProps, mapDispatchToProps); + +export default withRouter(connector(SectionsPage)); diff --git a/public/containers/SectionsPage/index.test.jsx b/public/containers/SectionsPage/index.test.jsx new file mode 100644 index 0000000..e69de29 diff --git a/public/containers/UpdateSectionPage.jsx b/public/containers/UpdateSectionPage.jsx index 888c8de..0dc1816 100644 --- a/public/containers/UpdateSectionPage.jsx +++ b/public/containers/UpdateSectionPage.jsx @@ -3,7 +3,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import * as actions from '../actions/section'; +import * as actions from '../redux/sections'; import SectionForm from '../components/SectionForm'; type PropsType = { diff --git a/public/reducers/section.js b/public/reducers/section.js deleted file mode 100644 index c070382..0000000 --- a/public/reducers/section.js +++ /dev/null @@ -1,45 +0,0 @@ -import * as TYPES from '../constants/actionTypes'; - -const section = { - loading: false, - saving: false, - section: { - title: '', - content: '', - hidden: false, - }, -}; - -export default function weddingPartyMemberReducer(state = section, action) { - switch (action.type) { - case TYPES.CREATE_SECTION_REQUEST: - return Object.assign({}, state, { saving: true }); - - case TYPES.CREATE_SECTION_SUCCESS: - return Object.assign({}, state, { saving: false }); - - case TYPES.CREATE_SECTION_ERROR: - return Object.assign({}, state, { saving: false }); - - case TYPES.LOAD_SECTION_REQUEST: - return Object.assign({}, state, { loading: true }); - - case TYPES.LOAD_SECTION_SUCCESS: - return Object.assign({}, state, { section: action.payload, loading: false }); - - case TYPES.LOAD_SECTION_ERROR: - return Object.assign({}, state, { loading: false }); - - case TYPES.UPDATE_SECTION_REQUEST: - return Object.assign({}, state, { saving: true }); - - case TYPES.UPDATE_SECTION_SUCCESS: - return Object.assign({}, state, { section: action.payload, saving: false }); - - case TYPES.UPDATE_SECTION_ERROR: - return Object.assign({}, state, { saving: false }); - - default: - return state; - } -} diff --git a/public/reducers/sections.js b/public/reducers/sections.js deleted file mode 100644 index 2b29549..0000000 --- a/public/reducers/sections.js +++ /dev/null @@ -1,39 +0,0 @@ -import * as TYPES from '../constants/actionTypes'; -import { move } from '../utils/sortingHelper'; - -const initialSections = { - loading: false, - sections: [], -}; - -function moveSections({ sections }, { payload: { sourceId, targetId } }) { - return move({ sourceId, targetId, data: sections }); -} - -export default function sectionsReducer(state = initialSections, action) { - switch (action.type) { - case TYPES.LOAD_SECTIONS_REQUEST: - return Object.assign({}, state, { loading: true }); - - case TYPES.LOAD_SECTIONS_SUCCESS: - return Object.assign({}, state, { sections: action.payload, loading: false }); - - case TYPES.LOAD_SECTIONS_ERROR: - return Object.assign({}, state, { loading: false }); - - case TYPES.DELETE_SECTION_REQUEST: - return Object.assign({}, state, { deleting: true }); - - case TYPES.DELETE_SECTION_SUCCESS: - return Object.assign({}, state, { deleting: false }); - - case TYPES.DELETE_SECTION_ERROR: - return Object.assign({}, state, { deleting: false }); - - case TYPES.MOVE_SECTION: - return Object.assign({}, state, { sections: moveSections(state, action) }); - - default: - return state; - } -} diff --git a/public/redux/index.js b/public/redux/index.js index 6469184..a5d2263 100644 --- a/public/redux/index.js +++ b/public/redux/index.js @@ -9,8 +9,7 @@ import signUp from '../reducers/signUp'; import weddingPartyMembers from '../reducers/weddingPartyMembers'; import weddingPartyMember from '../reducers/weddingPartyMember'; import gifts from '../reducers/gifts'; -import sections from '../reducers/sections'; -import section from '../reducers/section'; +import sections from '../redux/sections'; import basket, * as fromBasket from './basket'; import giftSet from '../reducers/giftSet'; import giftSets from '../reducers/giftSets'; @@ -26,7 +25,6 @@ export default combineReducers({ weddingPartyMember, gifts, sections, - section, basket, giftSet, giftSets, diff --git a/public/redux/sections.js b/public/redux/sections.js new file mode 100644 index 0000000..114f673 --- /dev/null +++ b/public/redux/sections.js @@ -0,0 +1,177 @@ +// @flow + +import { combineReducers } from 'redux'; +import { browserHistory } from 'react-router'; +import { success } from './notifications'; +import { CALL_API } from '../middleware/api'; +import { HTTP_METHODS } from '../constants/api'; +import { SECTIONS_ROUTE } from '../constants/routes'; +import { move } from '../utils/sortingHelper'; +import type { ActionType, SectionType, SectionsType, SectionIdType, SectionsStateType } from '../types'; + +const LOAD_SECTIONS_REQUEST = 'our-wedding-heroes/sections/LOAD_SECTIONS_REQUEST'; +const LOAD_SECTIONS_SUCCESS = 'our-wedding-heroes/sections/LOAD_SECTIONS_SUCCESS'; +const LOAD_SECTIONS_ERROR = 'our-wedding-heroes/sections/LOAD_SECTIONS_ERROR'; +const LOAD_SECTION_REQUEST = 'our-wedding-heroes/sections/LOAD_SECTION_REQUEST'; +const LOAD_SECTION_SUCCESS = 'our-wedding-heroes/sections/LOAD_SECTION_SUCCESS'; +const LOAD_SECTION_ERROR = 'our-wedding-heroes/sections/LOAD_SECTION_ERROR'; +const CREATE_SECTION_REQUEST = 'our-wedding-heroes/sections/CREATE_SECTION_REQUEST'; +const CREATE_SECTION_SUCCESS = 'our-wedding-heroes/sections/CREATE_SECTION_SUCCESS'; +const CREATE_SECTION_ERROR = 'our-wedding-heroes/sections/CREATE_SECTION_ERROR'; +const UPDATE_SECTION_REQUEST = 'our-wedding-heroes/sections/UPDATE_SECTION_REQUEST'; +const UPDATE_SECTION_SUCCESS = 'our-wedding-heroes/sections/UPDATE_SECTION_SUCCESS'; +const UPDATE_SECTION_ERROR = 'our-wedding-heroes/sections/UPDATE_SECTION_ERROR'; +const DELETE_SECTION_REQUEST = 'our-wedding-heroes/sections/DELETE_SECTION_REQUEST'; +const DELETE_SECTION_SUCCESS = 'our-wedding-heroes/sections/DELETE_SECTION_SUCCESS'; +const DELETE_SECTION_ERROR = 'our-wedding-heroes/sections/DELETE_SECTION_ERROR'; +const MOVE_SECTION = 'our-wedding-heroes/sections/MOVE_SECTION'; + +const isLoading = (state: boolean = false, action: ActionType): boolean => { + switch (action.type) { + case LOAD_SECTIONS_REQUEST: + case LOAD_SECTION_REQUEST: + return true; + case LOAD_SECTIONS_SUCCESS: + case LOAD_SECTIONS_ERROR: + case LOAD_SECTION_SUCCESS: + case LOAD_SECTION_ERROR: + return false; + default: + return state; + } +}; + +const isSaving = (state: boolean = false, action: ActionType): boolean => { + switch (action.type) { + case CREATE_SECTION_REQUEST: + case UPDATE_SECTION_REQUEST: + return true; + case CREATE_SECTION_SUCCESS: + case CREATE_SECTION_ERROR: + case UPDATE_SECTION_SUCCESS: + case UPDATE_SECTION_ERROR: + return false; + default: + return state; + } +}; + +const isDeleting = (state: boolean = false, action: ActionType): boolean => { + switch (action.type) { + case DELETE_SECTION_REQUEST: + return true; + case DELETE_SECTION_SUCCESS: + case DELETE_SECTION_ERROR: + return false; + default: + return state; + } +}; + +const section = (state: ?SectionType, action: ActionType): ?SectionType => { + switch (action.type) { + case LOAD_SECTION_SUCCESS: + case CREATE_SECTION_SUCCESS: + case UPDATE_SECTION_SUCCESS: { + if (!state) { + return action.payload; + } + + return state.id === action.payload.id ? action.payload : state; + } + default: + return state; + } +}; + +const sections = (state: SectionsType = [], action: ActionType): SectionsType => { + switch (action.type) { + case LOAD_SECTIONS_SUCCESS: + return action.payload; + case LOAD_SECTION_SUCCESS: + case CREATE_SECTION_SUCCESS: + case UPDATE_SECTION_SUCCESS: { + const idToFind = action.payload.id; + const existingSection = state.find(({ id }) => id === idToFind); + + if (!existingSection) { + return [...state, section(undefined, action)]; + } + + return state.map(s => section(s, action)); + } + case DELETE_SECTION_REQUEST: { + const idToRemove = action.payload.id; + return state.filter(({ id }) => id !== idToRemove); + } + case MOVE_SECTION: + return move({ ...action.payload, data: state }); + default: + return state; + } +}; + +export default combineReducers({ isLoading, isSaving, isDeleting, sections }); + +export const moveSection = (payload: { sourceId: SectionIdType, targetId: SectionIdType }): ActionType => ({ type: MOVE_SECTION, payload }); + +export const loadSections = () => ({ + [CALL_API]: { + endpoint: 'section', + method: HTTP_METHODS.GET, + types: [LOAD_SECTIONS_REQUEST, LOAD_SECTIONS_SUCCESS, LOAD_SECTIONS_ERROR], + }, +}); + +export const loadSection = (id: SectionIdType) => ({ + [CALL_API]: { + endpoint: `section/${id}`, + method: HTTP_METHODS.GET, + authenticated: true, + types: [LOAD_SECTION_REQUEST, LOAD_SECTION_SUCCESS, LOAD_SECTION_ERROR], + }, +}); + +export const createSection = (data: SectionType) => ({ + [CALL_API]: { + data, + endpoint: 'section', + method: HTTP_METHODS.POST, + authenticated: true, + onSuccess: dispatch => { + dispatch(success({ message: 'Section created successfully' })); + browserHistory.push(SECTIONS_ROUTE); + }, + types: [CREATE_SECTION_REQUEST, CREATE_SECTION_SUCCESS, CREATE_SECTION_ERROR], + }, +}); + +export const updateSection = (data: SectionType) => ({ + [CALL_API]: { + data, + endpoint: `section/${data.id}`, + method: HTTP_METHODS.PUT, + authenticated: true, + onSuccess: dispatch => { + dispatch(success({ message: 'Section updated successfully' })); + }, + types: [UPDATE_SECTION_REQUEST, UPDATE_SECTION_SUCCESS, UPDATE_SECTION_ERROR], + }, +}); + +export const deleteSection = ({ id }: SectionType) => ({ + [CALL_API]: { + endpoint: `section/${id}`, + method: HTTP_METHODS.DELETE, + authenticated: true, + onSuccess: dispatch => { + dispatch(success({ message: 'Section deleted successfully' })); + }, + types: [DELETE_SECTION_REQUEST, DELETE_SECTION_SUCCESS, DELETE_SECTION_ERROR], + }, +}); + +export const getSortedSections = (state: SectionsStateType): SectionsType => state.sections.sort((a, b) => a.position - b.position); +export const getIsLoading = (state: SectionsStateType): boolean => state.isLoading; +export const getIsSaving = (state: SectionsStateType): boolean => state.isSaving; +export const getIsDeleting = (state: SectionsStateType): boolean => state.isDeleting; diff --git a/public/redux/sections.test.js b/public/redux/sections.test.js new file mode 100644 index 0000000..e69de29 diff --git a/public/redux/users.js b/public/redux/users.js index c6bd74a..535b400 100644 --- a/public/redux/users.js +++ b/public/redux/users.js @@ -21,7 +21,7 @@ const CHANGE_PASSWORD_REQUEST = 'our-wedding-heroes/users/CHANGE_PASSWORD_REQUES const CHANGE_PASSWORD_SUCCESS = 'our-wedding-heroes/users/CHANGE_PASSWORD_SUCCESS'; const CHANGE_PASSWORD_ERROR = 'our-wedding-heroes/users/CHANGE_PASSWORD_ERROR'; -const isModalOpen = (state: boolean = false, action: ActionType) => { +const isModalOpen = (state: boolean = false, action: ActionType): boolean => { switch (action.type) { case OPEN_USER_MODAL: return true; @@ -32,7 +32,7 @@ const isModalOpen = (state: boolean = false, action: ActionType) => { } }; -const isLoading = (state: boolean = false, action: ActionType) => { +const isLoading = (state: boolean = false, action: ActionType): boolean => { switch (action.type) { case LOAD_USERS_REQUEST: return true; @@ -44,7 +44,7 @@ const isLoading = (state: boolean = false, action: ActionType) => { } }; -const isSaving = (state: boolean = false, action: ActionType) => { +const isSaving = (state: boolean = false, action: ActionType): boolean => { switch (action.type) { case CREATE_USER_REQUEST: return true; @@ -56,7 +56,7 @@ const isSaving = (state: boolean = false, action: ActionType) => { } }; -const isDeleting = (state: boolean = false, action: ActionType) => { +const isDeleting = (state: boolean = false, action: ActionType): boolean => { switch (action.type) { case DELETE_USER_REQUEST: return true; @@ -68,7 +68,7 @@ const isDeleting = (state: boolean = false, action: ActionType) => { } }; -const users = (state: UsersType = [], action: ActionType) => { +const users = (state: UsersType = [], action: ActionType): UsersType => { switch (action.type) { case LOAD_USERS_SUCCESS: return action.payload; diff --git a/public/types/index.js b/public/types/index.js index 2e984fe..0c905ba 100644 --- a/public/types/index.js +++ b/public/types/index.js @@ -45,6 +45,28 @@ export type UserType = { export type UsersType = Array; +export type SectionIdType = number; + +export type SectionType = { + +id: SectionIdType, + +title: string, + +hidden: boolean, + +position: number, +}; + +export type SectionsType = Array; + +export type AuthUser = { + +email: string, +}; + +export type SectionsStateType = { + +isLoading: boolean, + +isSaving: boolean, + +isDeleting: boolean, + +sections: SectionsType, +}; + export type UsersStateType = { +isModalOpen: boolean, +isLoading: boolean, @@ -53,10 +75,6 @@ export type UsersStateType = { +users: UsersType, }; -export type AuthUser = { - +email: string, -}; - export type StateType = { +auth: { +user: AuthUser, @@ -93,7 +111,24 @@ export type ActionType = | { type: 'our-wedding-heroes/users/DELETE_USER_ERROR' } | { type: 'our-wedding-heroes/users/CHANGE_PASSWORD_REQUEST' } | { type: 'our-wedding-heroes/users/CHANGE_PASSWORD_SUCCESS' } - | { type: 'our-wedding-heroes/users/CHANGE_PASSWORD_ERROR' }; + | { type: 'our-wedding-heroes/users/CHANGE_PASSWORD_ERROR' } + // Sections Actions + | { type: 'our-wedding-heroes/sections/LOAD_SECTIONS_REQUEST' } + | { type: 'our-wedding-heroes/sections/LOAD_SECTIONS_SUCCESS', +payload: SectionsType } + | { type: 'our-wedding-heroes/sections/LOAD_SECTIONS_ERROR' } + | { type: 'our-wedding-heroes/sections/LOAD_SECTION_REQUEST' } + | { type: 'our-wedding-heroes/sections/LOAD_SECTION_SUCCESS', +payload: SectionType } + | { type: 'our-wedding-heroes/sections/LOAD_SECTION_ERROR' } + | { type: 'our-wedding-heroes/sections/CREATE_SECTION_REQUEST' } + | { type: 'our-wedding-heroes/sections/CREATE_SECTION_SUCCESS', +payload: SectionType } + | { type: 'our-wedding-heroes/sections/CREATE_SECTION_ERROR' } + | { type: 'our-wedding-heroes/sections/UPDATE_SECTION_REQUEST' } + | { type: 'our-wedding-heroes/sections/UPDATE_SECTION_SUCCESS', +payload: SectionType } + | { type: 'our-wedding-heroes/sections/UPDATE_SECTION_ERROR' } + | { type: 'our-wedding-heroes/sections/DELETE_SECTION_REQUEST', +payload: SectionType } + | { type: 'our-wedding-heroes/sections/DELETE_SECTION_SUCCESS' } + | { type: 'our-wedding-heroes/sections/DELETE_SECTION_ERROR' } + | { type: 'our-wedding-heroes/sections/MOVE_SECTION', +payload: { sourceId: SectionIdType, targetId: SectionIdType } }; export type StoreType = ReduxStore; export type DispatchType = ReduxDispatch; diff --git a/yarn.lock b/yarn.lock index bf0a77c..009594a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1738,8 +1738,8 @@ cookie@0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" cookiejar@^2.0.6: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.0.tgz#86549689539b6d0e269b6637a304be508194d898" + version "2.1.1" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" core-js@^1.0.0: version "1.2.7" @@ -2534,9 +2534,9 @@ eslint-config-airbnb@14.1.0: dependencies: eslint-config-airbnb-base "^11.1.0" -eslint-config-prettier@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-1.5.0.tgz#969b6d21b2eb2574a6810426507f755072db1963" +eslint-config-prettier@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-1.6.0.tgz#56e53a8eb461c06eced20cec40d765c185100fd5" dependencies: get-stdin "^5.0.1" @@ -4206,8 +4206,8 @@ js-yaml@3.6.1, js-yaml@^3.4.3, js-yaml@^3.5.1: esprima "^2.6.0" js-yaml@^3.7.0: - version "3.8.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.2.tgz#02d3e2c0f6beab20248d412c352203827d786721" + version "3.8.3" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766" dependencies: argparse "^1.0.7" esprima "^3.1.1" @@ -5058,8 +5058,8 @@ mute-stream@0.0.5: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" nan@^2.3.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" + version "2.6.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" natural-compare@^1.4.0: version "1.4.0" @@ -6005,8 +6005,8 @@ prettier@0.22.0: minimist "1.2.0" pretty-error@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.0.3.tgz#bed3d816a008e76da617cde8216f4b778849b5d9" + version "2.1.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.0.tgz#87f4e9d706a24c87d6cbee9fabec001fcf8c75d8" dependencies: renderkid "^2.0.1" utila "~0.4"