Skip to content

Commit a1a13db

Browse files
authored
Merge pull request #33 from FBDevCLagos/feature/#16/proverbs-CRUD
Feature/#16/proverbs crud
2 parents 8db6f86 + 92483e0 commit a1a13db

30 files changed

+809
-160
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ node_modules
66
bower_components
77
buildcoverage
88
coverage
9+
.idea

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@
3939
"react-redux": "4.4.5",
4040
"react-router": "2.4.0",
4141
"react-router-redux": "4.0.4",
42+
"react-select": "^1.0.0-rc.1",
4243
"react-static-container": "^1.0.1",
4344
"react-tinymce": "^0.5.1",
4445
"redux": "3.5.2",
4546
"redux-thunk": "2.0.1",
4647
"sinon": "^1.17.4",
4748
"toastr": "2.1.2",
4849
"twitter-bootstrap-wizard": "^1.2.0",
50+
"underscore": "^1.8.3",
4951
"url-loader": "^0.5.6",
5052
"webpack": "^1.12.2"
5153
},

src/css/4-pages/proverbs.scss

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,30 @@
3333
}
3434

3535
.fav-buttons {
36+
display: flex;
3637
float: right;
3738
margin: 0 10px;
3839
}
3940
}
4041
}
42+
43+
.proverb-form {
44+
.proverb-form-control {
45+
padding: 0 15px;
46+
47+
.btn {
48+
outline: none;
49+
}
50+
}
51+
}
52+
53+
.translation-form {
54+
border: 1px solid $gray1;
55+
border-radius: 5px;
56+
padding: 10px;
57+
58+
.close-form {
59+
color: $primary-color;
60+
cursor: pointer;
61+
}
62+
}

src/css/main.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
@import "../../node_modules/react-select/dist/react-select.css";
2+
@import "../../node_modules/toastr/build/toastr.css";
3+
14
@import "1-tools/-tools-index";
25
@import "2-base/-base-index";
36
@import "3-modules/-modules-index";

src/js/actions/actionTypes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ export default {
33
LOAD_PROVERBS_SUCCESS : 'LOAD_PROVERBS_SUCCESS',
44
LOAD_PROVERB_SUCCESS : 'LOAD_PROVERB_SUCCESS',
55
UPDATE_PROVERB_SUCCESS : 'UPDATE_PROVERB_SUCCESS',
6+
CREATE_PROVERB_SUCCESS : 'CREATE_PROVERB_SUCCESS',
67
LOAD_TRANSLATIONS_SUCCESS : 'LOAD_TRANSLATIONS_SUCCESS',
78
LOAD_TRANSLATION_SUCCESS : 'LOAD_TRANSLATION_SUCCESS',
8-
UPDATE_TRANSLATION_SUCCESS: 'UPDATE_TRANSLATION_SUCCESS'
9+
UPDATE_TRANSLATION_SUCCESS: 'UPDATE_TRANSLATION_SUCCESS',
10+
SAVE_PROVERB_ERROR : 'SAVE_PROVERB_ERROR'
911
};

src/js/actions/proverbActions.js

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,40 @@ import webAPI from '../utils/webAPI';
33
import mockProverbApi from '../api/mockProverbsApi';
44

55
/**
6-
* @param {Object} proverbs: object
6+
* @param array of proverbs
77
* @return {Object} containing the action type and data
88
*/
99
export function loadProverbsSuccess(proverbs) {
1010
return { type: types.LOAD_PROVERBS_SUCCESS, proverbs };
1111
}
1212

1313
/**
14-
* @param {Object} proverbs: object
15-
* @return {Object} containing the action type and data
16-
*/
17-
export function loadProverbSuccess(proverb) {
18-
return { type: types.LOAD_PROVERB_SUCCESS, proverb };
19-
}
20-
21-
/**
22-
* @param {Object} proverbs: key proverbs and value is an array of questions
14+
* @param {Object} proverb
2315
* @return {Object} containing the action type and proverb
2416
*/
2517
export function updateProverbSuccess(proverb) {
2618
return { type: types.UPDATE_PROVERB_SUCCESS, proverb };
2719
}
2820

29-
export function loadProverbs() {
21+
export function loadProverbs(locale) {
22+
const defaultLocale = locale ? locale : 'en';
3023
return dispatch => {
31-
return webAPI(`/proverbs`, 'GET', '')
24+
return webAPI(`${'/' + defaultLocale + '/proverbs'}`, 'GET', '')
3225
.then(res => {
33-
dispatch(loadProverbsSuccess(res.proverbs));
26+
dispatch(loadProverbsSuccess(res));
3427
});
3528
};
3629
}
3730

38-
export function loadProverb(proverbId) {
39-
return dispatch => {
40-
return mockProverbApi.getProverb(proverbId)
41-
.then(proverb => {
42-
dispatch(loadProverbSuccess(proverb));
43-
});
31+
export function saveProverb(proverb) {
32+
const type = proverb.id ? 'PUT' : 'POST';
33+
const rootUrl = `/proverbs`;
34+
const url = proverb.id ? `${rootUrl + '/' + proverb.id}` : rootUrl;
35+
36+
return (dispatch) => {
37+
return webAPI(url, type, proverb)
38+
.then(res => {
39+
dispatch(updateProverbSuccess(res))
40+
})
4441
};
4542
}

src/js/actions/proverbActions.spec.js

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,97 @@
11
import expect from 'expect';
2-
import * as proverbActions from './proverbActions';
3-
import * as types from './actionTypes';
42
import thunk from 'redux-thunk';
53
import nock from 'nock';
64
import configureMockStore from 'redux-mock-store';
7-
5+
import { omit } from 'underscore';
86
import Config from '../config/environment';
7+
import * as proverbActions from './proverbActions';
8+
import * as types from './actionTypes';
99

10-
describe('loadProverbsSuccess', () => {
11-
describe('Create load Proverbs Action', () => {
12-
it('should create a LOAD_PROVERBS_SUCCESS action', () => {
13-
// setup
14-
const proverbs = [
15-
{id: 'A1023'}
16-
];
17-
const expectedAction = {
18-
type: types.default.LOAD_PROVERBS_SUCCESS,
19-
proverbs
20-
};
21-
22-
// actions
23-
const action = proverbActions.loadProverbsSuccess(proverbs);
24-
25-
// assertions
26-
expect(action).toEqual(expectedAction);
27-
});
28-
});
29-
});
10+
const proverb = { id: 'A1023', body: 'Testing proverb' };
11+
const proverbs = [ proverb ];
3012

31-
const middleware = [thunk];
32-
const mockStore = configureMockStore(middleware);
13+
describe('Proverb actions', () => {
3314

34-
describe('loadProverbs', function() {
35-
afterEach(() => {
36-
nock.cleanAll();
37-
});
38-
// this.timeout(15000);
39-
it('should dispatch a success action on successful API response', done => {
15+
it('should create a LOAD_PROVERBS_SUCCESS action', () => {
16+
17+
const expectedAction = {
18+
type: types.default.LOAD_PROVERBS_SUCCESS,
19+
proverbs
20+
};
21+
22+
// actions
23+
const action = proverbActions.loadProverbsSuccess(proverbs);
24+
25+
// assertions
26+
expect(action).toEqual(expectedAction);
27+
});
28+
29+
it('should create a UPDATE_PROVERB_SUCCESS action', () => {
4030
// setup
41-
const proverbs = [{id: 'A1023', name: 'first proverb'}];
31+
const expectedUpdateAction = {
32+
type: types.default.UPDATE_PROVERB_SUCCESS,
33+
proverb
34+
};
35+
36+
// actions
37+
const action = proverbActions.updateProverbSuccess(proverb);
4238

43-
nock(Config.host)
44-
.get('/proverbs/recent')
45-
.reply(200, {body: proverbs});
39+
// assertions
40+
expect(action).toEqual(expectedUpdateAction);
41+
});
4642

47-
const expectedActions = [
48-
{type: types.default.LOAD_PROVERBS_SUCCESS, proverbs}
49-
];
43+
describe('Proverb action thunks', function() {
5044

51-
// action
52-
const initialAppState = {tags: []};
53-
const store = mockStore(initialAppState, expectedActions);
45+
const middleware = [thunk];
46+
const mockStore = configureMockStore(middleware);
47+
const initialAppState = { proverbs: [], proverb: {} };
5448

55-
store.dispatch(proverbActions.loadProverbs()).then(() => {
56-
const actions = store.getActions();
57-
// expect(actions[0].type).toEqual(types.default.LOAD_PROVERBS_SUCCESS);
58-
// expect(actions[0].proverbs).toEqual(proverbs);
49+
afterEach(() => {
50+
nock.cleanAll();
5951
});
6052

61-
done();
62-
});
53+
// this.timeout(15000);
54+
describe('loadProverbs', () => {
55+
it('should dispatch a success action on successful API response', done => {
56+
57+
nock(Config.host)
58+
.get('/proverbs')
59+
.reply(200, proverbs);
60+
61+
const expectedActions = [
62+
{type: types.default.LOAD_PROVERBS_SUCCESS, proverbs}
63+
];
64+
65+
// action
66+
const store = mockStore(initialAppState, expectedActions);
67+
store.dispatch(proverbActions.loadProverbs())
68+
.then(() => {
69+
const [actions] = store.getActions();
70+
expect(actions.type).toEqual(types.default.LOAD_PROVERBS_SUCCESS);
71+
});
72+
73+
done();
74+
});
75+
})
76+
77+
describe('saveProverbs', () => {
78+
it('should dispatch a success action on successful API response', done => {
79+
80+
const expectedActions = [
81+
{type: types.default.UPDATE_PROVERB_SUCCESS, proverb}
82+
];
83+
84+
// action
85+
const store = mockStore(initialAppState, expectedActions);
86+
const [actions] = store.getActions();
87+
store.dispatch(proverbActions.saveProverb(proverb))
88+
.then(() => {
89+
expect(actions.type).toEqual(types.default.UPDATE_PROVERB_SUCCESS);
90+
});
91+
92+
done();
93+
});
94+
})
95+
});
96+
6397
});

src/js/api/data/proverbs.js

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ export const proverbs = [
1313
translations: [
1414
{
1515
id: 1,
16+
proverbId: 1,
1617
body: "Ura ga-eju onye nwuru anwu afo",
17-
language: "ig"
18+
language: "Igbo"
1819

1920
},
2021
{
21-
id: 1,
22+
id: 2,
23+
proverbId: 1,
2224
body: "Eni to ku, o ma sun dada",
23-
language: "yb"
25+
language: "Yoruba"
2426

2527
}
2628
],
@@ -37,9 +39,10 @@ export const proverbs = [
3739
],
3840
translations: [
3941
{
40-
id: 2,
42+
id: 1,
43+
proverbId: 2,
4144
body: "Gidi gidi bụ ugwu eze.",
42-
language: "ig"
45+
language: "Igbo"
4346
}
4447
],
4548
status: "approved"
@@ -54,9 +57,10 @@ export const proverbs = [
5457
],
5558
translations: [
5659
{
57-
id: 3,
60+
id: 1,
61+
proverbId: 3,
5862
body: "Chọọ ewu ojii ka chi dị",
59-
language: "ig"
63+
language: "Igbo"
6064
}
6165
],
6266
status: "approved"
@@ -70,9 +74,10 @@ export const proverbs = [
7074
],
7175
translations: [
7276
{
73-
id: 4,
77+
id: 1,
78+
proverbId: 4,
7479
body: "Ihe ehi hụrụ gbalaba oso ka okuku huru na-atụ onu",
75-
language: "ig"
80+
language: "Igbo"
7681
}
7782
],
7883
status: "approved"
@@ -88,9 +93,10 @@ export const proverbs = [
8893
],
8994
translations: [
9095
{
91-
id: 5,
96+
id: 1,
97+
proverbId: 5,
9298
body: "Ibi tí à ńlọ là ńwò, a kìí wo ibi tí a ti ṣubú",
93-
language: "yb",
99+
language: "Yoruba",
94100
literal_translation: "Focus on where you are going, not where you'd fallen"
95101
}
96102
],
@@ -106,9 +112,10 @@ export const proverbs = [
106112
],
107113
translations: [
108114
{
109-
id: 6,
115+
id: 1,
116+
proverbId: 6,
110117
body: "Tí ẹyẹ ò bá fín ẹyẹ níràn, ojú ọrun tó ẹyẹ ẹ́ fò láì fara kanra. ",
111-
language: "yb",
118+
language: "Yoruba",
112119
literal_translation: "If a bird won't seek the ill of another, the sky is wide enough for all to fly without colliding."
113120
}
114121
]
@@ -124,9 +131,10 @@ export const proverbs = [
124131
],
125132
translations: [
126133
{
127-
id: 7,
134+
id: 1,
135+
proverbId: 7,
128136
body: "Adániwáyè ò gbàgbé ẹnìkan; àìmàsìkò ló ńdààmú ẹ̀dá.",
129-
language: "yb",
137+
language: "Yoruba",
130138
literal_translation: "God hasn't forgotten anyone; ignorance of divine timing is what makes us fret."
131139
}
132140
]
@@ -140,9 +148,10 @@ export const proverbs = [
140148
],
141149
translations: [
142150
{
143-
id: 8,
151+
id: 1,
152+
proverbId: 8,
144153
body: "Ẹni tó bá da omi síwájú á tẹ'lẹ tútù.",
145-
language: "yb",
154+
language: "Yoruba",
146155
tags: [ "love", "name", "peace" ]
147156
}
148157
],

0 commit comments

Comments
 (0)