Skip to content

Commit

Permalink
Issue #28: Specoffer list
Browse files Browse the repository at this point in the history
  • Loading branch information
ormus2002 committed Mar 30, 2016
1 parent 3fc5193 commit d0eaffd
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 4 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "ums-frontned",
"name": "ums-frontend",
"version": "0.0.1",
"description": "",
"homepage": "https://github.com/mkozhukharenko/ums-frontend",
Expand All @@ -25,6 +25,7 @@
"amcharts3": "github:amcharts/amcharts3",
"ammap3": "github:amcharts/ammap3",
"bootstrap": "^3.3.6",
"fixed-data-table": "^0.6.0",
"history": "2.0.0",
"lodash": "^4.6.1",
"lscache": "^1.0.5",
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import store from 'store';
import routes from './system/routes';
import '../assets/stylesheets/index.css';
import 'bootstrap/dist/css/bootstrap.css';
import 'fixed-data-table/dist/fixed-data-table.css';
import {syncHistoryWithStore} from 'react-router-redux';

// Create an enhanced history that syncs navigation events with the store
Expand Down
3 changes: 1 addition & 2 deletions src/modules/enrolments/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ let {

/**
* check if data is loaded
* @param storeState
* @param reducerName
* @param entityData
* @returns {boolean}
*/
export function isEntityDataLoaded(entityData) {
Expand Down
8 changes: 8 additions & 0 deletions src/modules/navbar/NavBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ export default class NavBar extends Component {
<NavItem>Статистика</NavItem>
</LinkContainer>

<LinkContainer to={{ pathname: '/specoffers', query: this.props.specoffersQueryParams}}>
<NavItem>Пропозиції</NavItem>
</LinkContainer>

{this.authItem('rating', 'Рейтинг', {query: this.props.ratingQueryParams})}
{this.authItem('persons', 'Персони')}
</Nav>
Expand Down Expand Up @@ -91,6 +95,10 @@ function select(state) {
ratingQueryParams: {
departmentId: state.rating.specofferChooser.departmentId,
specofferId: state.rating.specofferChooser.specofferId
},
specoffersQueryParams: {
timePeriodId: state.specoffers.timePeriodId,
limit: state.specoffers.limit
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/rating/container/SpecofferChooser.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {connect} from 'react-redux';
import {loadSpecoffersChooser} from './../actions';
import Nav from 'react-bootstrap/lib/Nav';
import NavItem from 'react-bootstrap/lib/NavItem';
import { LinkContainer } from 'react-router-bootstrap';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import find from 'lodash/find'
import DepartmentsList from './../components/DepartmentsList'
import SpecoffersList from './../components/SpecoffersList'
Expand Down
19 changes: 19 additions & 0 deletions src/modules/specoffers/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {REQUEST_API} from '../../system/constants';
import * as types from './constants';

export function loadSpecoffersList(params) {
return {
type: REQUEST_API,
request: {
url: `/specoffers?timePeriodId=${params.timePeriodId}&limit=${params.limit}`,
actions: {
start: {type: types.LOAD_ALL_SPECOFFERS_START},
success: {type: types.LOAD_ALL_SPECOFFERS_SUCCESS},
fail: {type: types.LOAD_ALL_SPECOFFERS_FAIL}
},
params,
cache: true
},
interrupt: (store) => !!store.getState().specoffers.resources.length
};
}
17 changes: 17 additions & 0 deletions src/modules/specoffers/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const LOAD_ALL_SPECOFFERS_START = 'LOAD_ALL_SPECOFFERS_START';
export const LOAD_ALL_SPECOFFERS_SUCCESS = 'LOAD_ALL_SPECOFFERS_SUCCESS';
export const LOAD_ALL_SPECOFFERS_FAIL = 'LOAD_ALL_SPECOFFERS_START';

export const SPECOFFERS_REDUCER = 'specoffers';

export const FIELD_NAMES = [
{'name': 'Спеціальність', 'field': 'specialtyId'},
{'name': 'Структурний підрозділ', 'field': 'departmentId'},
{'name': 'Тип пропозиції', 'field': 'specofferTypeId'},
{'name': 'docNum', 'field': 'docNum'},
{'name': 'weightCertificate', 'field': 'weightCertificate'},
{'name': 'weightAward', 'field': 'weightAward'},
{'name': 'Форма навчання', 'field': 'educationFormTypeId'},
{'name': 'Ліцензований обсяг', 'field': 'licCount'},
{'name': 'Державне замовлення', 'field': 'stateCount'},
];
7 changes: 7 additions & 0 deletions src/modules/specoffers/containers/SpecofferInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React, {Component, PropTypes} from 'react';

export default class SpecofferInfo extends Component {
render() {
return <div>SpecofferInfo</div>;
}
}
97 changes: 97 additions & 0 deletions src/modules/specoffers/containers/SpecoffersListPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {createSelector} from 'reselect';
import LinkContainer from 'react-router-bootstrap/lib/LinkContainer';
import {Table, Column, Cell} from 'fixed-data-table';

import Loading from 'loading';
import {loadSpecoffersList} from './../actions';
import {isDataForSpecoffersLoaded, decodeSpecoffers} from './../helpers';
import * as dictConst from '../../dictionaries/constants';
import loadDictionaries from '../../dictionaries/actions';
import {SPECOFFERS_REDUCER, FIELD_NAMES} from './../constants';

class MyLinkCell extends Component {
render() {
const {rowIndex, field, data, ...props} = this.props;
let path = `/specoffers/enrolments?specOfferId=${data[rowIndex][field]}`;
return (
<Cell {...props}>
<LinkContainer to={{ pathname: path}}>
<a>{data[rowIndex][field]}</a>
</LinkContainer>
</Cell>
);
}
}

class SpecoffersListPage extends Component {
constructor(props) {
super(props);
}

componentDidMount() {
const {timePeriodId, limit} = this.props;
this.props.loadDictionaries([dictConst.DEPARTMENTS]);
this.props.loadSpecoffersList({timePeriodId, limit});
}

render() {
if (!isDataForSpecoffersLoaded(SPECOFFERS_REDUCER)) {
return <Loading/>;
}

let {decodedSpecoffers} = this.props;

let cells = FIELD_NAMES.map((item) => {
return <Column
header={<Cell>{item.name}</Cell>}
cell={props => (
<Cell {...props}>
{decodedSpecoffers[props.rowIndex][item.field]}
</Cell>
)
}
flexGrow={1}
width={20}
/>
});

return (
<Table
rowsCount={decodedSpecoffers.length}
rowHeight={50}
headerHeight={50}
width={950}
height={420}>
<Column
header={<Cell></Cell>}
cell={
<MyLinkCell
data={decodedSpecoffers}
field="id"
/>
}
width={40}
/>
{cells}
</Table>
);
}
}

const mapStateToSpecoffers = createSelector(
(state) => state.specoffers,
(state) => state.dictionaries,
(state, ownProps) => ownProps.location.query,
(specoffers, listOfDict, query) => ({
decodedSpecoffers: decodeSpecoffers(specoffers, listOfDict),
timePeriodId: query.timePeriodId,
limit: query.limit
})
);

export default connect(
mapStateToSpecoffers,
{loadSpecoffersList, loadDictionaries}
)(SpecoffersListPage);
50 changes: 50 additions & 0 deletions src/modules/specoffers/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import store from 'store';
import * as dictConstants from '../dictionaries/constants';
import {isDictLoaded} from '../dictionaries/helpers';

let {
DEPARTMENTS,
ENROLMENTS_TYPES,
ENROLMENTS_STATUS_TYPES
} = dictConstants;

/**
* check if data is loaded
* @param entityData
* @returns {boolean}
*/
export function isEntityDataLoaded(entityData) {
return !entityData.isLoading && (entityData.resources && !!entityData.resources.length);
}

/**
* check if specoffers loaded && dictionaries (used only inside specoffers list container)
* @param reducerName
* @returns {*|boolean}
*/
export function isDataForSpecoffersLoaded(reducerName) {
let state = store.getState();
return isDictLoaded([DEPARTMENTS], state.dictionaries)
&& isEntityDataLoaded(state[reducerName]);
}

/**
*
* @param rowSpecoffers - list of row specoffers
* @returns {Array} - array of decoded specoffers
*/
export function decodeSpecoffers(rowSpecoffers, dictionaries) {
return rowSpecoffers.resources.map((item)=> {
return decodeOneSpecoffer(item, dictionaries);
});
}

export function decodeOneSpecoffer(item, dictionaries) {
if (!item) return {};

let {DEPARTMENTS} = dictionaries;

return Object.assign({}, item, {
departmentId: DEPARTMENTS.resourcesMap[item.departmentId]
});
}
57 changes: 57 additions & 0 deletions src/modules/specoffers/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as types from './constants';
import {LOCATION_CHANGE} from 'react-router-redux';
import { ignoreActions } from 'redux-ignore';
import {combineReducers} from 'redux';

import lcache from '../../system/lcache';
import { TIMEPERIODID_CHANGED } from '../settings/widget';

const defaultState = {
isLoading: false,
resources: [],
timePeriodId: lcache.get('timePeriodId') || 8,
limit: 300,
error: null
};

export function specoffers(state = defaultState, action = {}) {
switch (action.type) {

case types.LOAD_ALL_SPECOFFERS_START:
return Object.assign({}, state, {isLoading: true, resources: []});

case types.LOAD_ALL_SPECOFFERS_SUCCESS:
return Object.assign({}, state,
{
isLoading: false
},
action.response
);

case types.LOAD_ALL_SPECOFFERS_FAIL:
return Object.assign({}, state, {isLoading: false}, {error: action.error.message});

case TIMEPERIODID_CHANGED:
return Object.assign({}, state,
{
isLoading: false,
resources: []
}
);

case LOCATION_CHANGE: // listen to query parameters changes
//if (action.payload.pathname !== '/specoffers') return state;
let {
timePeriodId = state.timePeriodId,
limit = state.limit } = action.payload.query;
return Object.assign({}, state, {timePeriodId, limit});

default:
return state;
}
}

export default combineReducers({
specoffers: ignoreActions(specoffers,
(action) => action.type === LOCATION_CHANGE && action.payload.pathname !== '/specoffers')
});
2 changes: 2 additions & 0 deletions src/system/reducers/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {default as statistics} from './../../modules/statistics/reducer';
import {default as auth} from '../../modules/auth/reducer';
import {default as rating} from '../../modules/rating/reducer';
import {settings} from '../../modules/settings/widget';
import {specoffers} from '../../modules/specoffers/reducer';
import config from './configReducer';
import {routerReducer} from 'react-router-redux';
import {combineReducers} from 'redux';
Expand All @@ -16,6 +17,7 @@ const rootReducer = combineReducers({
config,
rating,
settings,
specoffers,
routing: routerReducer
});

Expand Down
5 changes: 5 additions & 0 deletions src/system/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import Statistics from '../modules/statistics/components/Statistics';
import StatisticsIndex from '../modules/statistics/components/StatisticsIndex';
import Chart from '../modules/statistics/containers/Chart';
import SpecofferChooser from '../modules/rating/container/SpecofferChooser';
import SpecoffersListPage from '../modules/specoffers/containers/SpecoffersListPage';
import SpecofferInfo from '../modules/specoffers/containers/SpecofferInfo';

//configure permissions
export const routes = {
Expand All @@ -40,6 +42,9 @@ export default (
<IndexRoute component={StatisticsIndex}/>
<Route path="chart/:chartId" component={Chart}/>
</Route>
<Route path="specoffers" component={SpecoffersListPage}>
<Route path='enrolments' component={SpecofferInfo}/>
</Route>
<Route path="persons" component={AuthContainer} AuthComponent={PersonsPage}/>
<Route path="rating" component={AuthContainer} AuthComponent={SpecofferChooser}/>
<Route path="login" component={LoginPage}/>
Expand Down

0 comments on commit d0eaffd

Please sign in to comment.