-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial version of folder list working, displaying folder on page load
- Loading branch information
1 parent
330ec13
commit c27ede4
Showing
19 changed files
with
476 additions
and
12 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/** | ||
* helper function to transform object of key/value pairs to be sent as part of | ||
* api call URL | ||
* @param {Object} params object of query parameters | ||
*/ | ||
export const concatParams = function(params) { | ||
const paramsArr = Object.keys(params).reduce((acc, val) => { | ||
// check if array, and concat item in array if value is true | ||
if(Array.isArray(params[val])) { | ||
const valArr = params[val]; | ||
valArr.forEach(option => { | ||
if(option.checked) acc.push(val.concat('=', option.label)); | ||
}); | ||
|
||
return acc; | ||
} else if(params[val] !== undefined && params[val] !== null) { | ||
acc.push(val.concat('=', params[val])); | ||
return acc; | ||
} else { | ||
return acc; | ||
} | ||
}, []) | ||
|
||
return paramsArr.join('&'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import * as types from './types'; | ||
|
||
export const foldersFetchRequest = function(payload) { | ||
return { | ||
type: types.FOLDERS_FETCH_REQUEST, | ||
payload | ||
} | ||
} | ||
|
||
export const foldersFetchSuccess = function(payload) { | ||
return { | ||
type: types.FOLDERS_FETCH_SUCCESS, | ||
payload: payload | ||
} | ||
} | ||
|
||
export const foldersFetchFailure = function(error) { | ||
return { | ||
type: types.FOLDERS_FETCH_FAILURE, | ||
payload: error | ||
} | ||
} | ||
|
||
export const foldersFetchExit = function() { | ||
return { | ||
type: types.FOLDERS_FETCH_EXIT, | ||
} | ||
} | ||
|
||
export const foldersSetCurrentPage = function(page) { | ||
return { | ||
type: types.FOLDERS_SET_CURRENT_PAGE, | ||
payload: page | ||
} | ||
} | ||
|
||
export const foldersClearPageCache = function() { | ||
return { | ||
type: types.FOLDERS_CLEAR_PAGE_CACHE | ||
} | ||
} | ||
|
||
// this action does the same thing as fetchRequest, but adds a debounce of 2 seconds | ||
// to account for the user typing | ||
export const foldersFilterRequest = function(payload) { | ||
return { | ||
type: types.FOLDERS_FILTER_REQUEST, | ||
payload | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import axios from 'axios'; | ||
axios.defaults.withCredentials = true; | ||
import * as helpers from './_helpers'; | ||
|
||
const BASE_URL = 'http://localhost:3000/api/' | ||
const LIMIT = 25; | ||
|
||
const foldersFetch = async (params) => { | ||
try { | ||
const { page, sortKey, sortDirection, searchFilter, optionsFilter } = params; | ||
|
||
const queryString = helpers.concatParams({ | ||
page: page, | ||
limit: LIMIT, | ||
sortBy: sortKey, | ||
order: sortDirection, | ||
q: searchFilter | ||
// folder_type: optionsFilter, can be set leter via checkbox options | ||
}); | ||
|
||
console.log(queryString); | ||
const url = BASE_URL.concat('folder', '?', queryString); | ||
const response = await axios.get(url); | ||
// const totalCount = response.headers['x-total-count']; | ||
const totalCount = response.data.data.total; | ||
const lastPage = response.data.data.lastPage; | ||
const results = response.data.data.results; | ||
|
||
// FIXME: the problem is if you make an api call for a page > last page in the api, | ||
// the api will just return an empty data set but it will still be a 200 response code. | ||
return { | ||
data: results, | ||
lastPage: lastPage, | ||
currentPage: page | ||
} | ||
} catch(err) { | ||
// TODO: add better error handling for "network error": i.e. when server is not online at all | ||
// https://github.com/axios/axios#handling-errors | ||
console.log(err); | ||
throw err.response || err.message; | ||
} | ||
} | ||
|
||
export default { | ||
foldersFetch | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import reducer from './reducers'; | ||
import * as types from './types'; | ||
import * as actions from './actions'; | ||
import * as selectors from './selectors'; | ||
import { sagas } from './sagas'; | ||
|
||
export { | ||
types, | ||
actions, | ||
sagas, | ||
selectors | ||
} | ||
|
||
export default reducer; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import * as types from './types'; | ||
import { combineReducers } from 'redux'; | ||
|
||
const isLoading = (state = false, action) => { | ||
switch (action.type) { | ||
case types.FOLDERS_FETCH_REQUEST: | ||
return true; | ||
case types.FOLDERS_FETCH_SUCCESS: | ||
return false; | ||
case types.FOLDERS_FETCH_FAILURE: | ||
return false; | ||
case types.FOLDERS_FETCH_EXIT: // used to prematurely exit fetch if folders are already cached | ||
return false; | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
const error = (state = null, action) => { | ||
switch(action.type) { | ||
case types.FOLDERS_FETCH_REQUEST: | ||
return null; | ||
case types.FOLDERS_FETCH_FAILURE: | ||
return action.payload | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
// object containing all the items, with folder keys as the object properties | ||
const byId = (state = {}, action) => { | ||
console.log(action.payload); | ||
const foldersList = {}; | ||
switch(action.type) { | ||
case types.FOLDERS_FETCH_SUCCESS: | ||
for(let i = 0; i < action.payload.data.length; i++) { | ||
const folderItem = action.payload.data[i]; | ||
foldersList[folderItem.id] = folderItem; | ||
} | ||
return { | ||
...state, | ||
...foldersList | ||
} | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
// array of only the item ids | ||
const allIds = (state = [], action) => { | ||
switch(action.type) { | ||
case types.FOLDERS_FETCH_SUCCESS: | ||
return [ | ||
...state, | ||
...action.payload.data.map((item) => { | ||
return item.id; | ||
}) | ||
] | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
const currentPage = (state = 1, action) => { | ||
switch(action.type) { | ||
case types.FOLDERS_SET_CURRENT_PAGE: | ||
return action.payload; | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
const lastPage = (state = 0, action) => { | ||
switch(action.type) { | ||
case types.FOLDERS_FETCH_SUCCESS: | ||
return action.payload.lastPage | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
const pages = (state = {}, action) => { | ||
switch(action.type) { | ||
case types.FOLDERS_FETCH_SUCCESS: | ||
return { | ||
...state, | ||
[action.payload.currentPage]: action.payload.data.map((item) => item.id) | ||
} | ||
case types.FOLDERS_CLEAR_PAGE_CACHE: | ||
return {} | ||
default: | ||
return state; | ||
} | ||
} | ||
|
||
export default combineReducers({ | ||
error, | ||
isLoading, | ||
byId, | ||
allIds, | ||
currentPage, | ||
lastPage, | ||
pages | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { delay } from 'redux-saga'; | ||
import { put, takeLatest, takeEvery, all, call, fork, select} from 'redux-saga/effects' | ||
// import { push } from 'react-router-redux'; | ||
|
||
import * as actions from './actions'; | ||
import * as types from './types'; | ||
import * as selectors from './selectors' | ||
import api from './api'; | ||
|
||
const FILTER_DEBOUNCE_DELAY = 1000; | ||
|
||
|
||
export function* watchFolderFilterRequest() { | ||
yield takeLatest(types.FOLDERS_FILTER_REQUEST, handleFilterRequest); | ||
} | ||
|
||
export function* handleFilterRequest(action) { | ||
yield delay(FILTER_DEBOUNCE_DELAY); | ||
|
||
// execute folder fetch after debounce delay | ||
yield put(actions.foldersFetchRequest({ | ||
...action.payload, | ||
page: 1 // manually set page to 1 as filtered results can have less pages than current query params | ||
})); | ||
} | ||
|
||
export function* watchFolderFetchRequest() { | ||
yield takeLatest(types.FOLDERS_FETCH_REQUEST, foldersFetch); | ||
} | ||
|
||
export function* foldersFetch(action) { | ||
const { page, sortKey, sortDirection, clearCache=false, searchFilter=null, optionsFilter } = action.payload; | ||
|
||
try { | ||
if(clearCache) { | ||
yield put(actions.foldersClearPageCache()); // clear page cache to set new results | ||
} | ||
const currentPage = yield select(selectors.getCurrentPage); | ||
const pageParam = page ? page : currentPage; // get current page from payload or store if not provided | ||
const isPageCached = yield select(selectors.getIsFolderCached, pageParam); | ||
|
||
|
||
// check is page is already loaded before making api call | ||
if(!isPageCached || clearCache) { | ||
// TODO: check is page is cached on sort/filter | ||
const data = yield call(api.foldersFetch, { | ||
page: pageParam, | ||
sortKey, | ||
sortDirection, | ||
searchFilter, | ||
optionsFilter | ||
}); | ||
yield put(actions.foldersSetCurrentPage(data.currentPage)); // set current page | ||
yield put(actions.foldersFetchSuccess(data)); | ||
} else { | ||
yield put(actions.foldersSetCurrentPage(pageParam)); // set current page | ||
yield put(actions.foldersFetchExit()); | ||
} | ||
|
||
} catch(err) { | ||
// TODO: better error handling | ||
console.log('err'); | ||
console.log(err); | ||
yield put(actions.foldersFetchFailure(err)); | ||
} | ||
} | ||
|
||
// export only watcher sagas in one variable | ||
export const sagas = [ | ||
watchFolderFetchRequest, | ||
watchFolderFilterRequest | ||
]; | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// SELECTORS | ||
import { createSelector } from 'reselect'; | ||
|
||
|
||
// INPUT SELECTORS | ||
export const getCurrentPage = (state) => state.allFolders.currentPage; | ||
export const getIsFolderCached = (state, page) => state.allFolders.pages[page] !== undefined; | ||
const getFoldersById = (state) => state.allFolders.byId; | ||
const getFoldersAllIds = (state) => state.allFolders.allIds; | ||
const getFolderIdsByPage = state => { | ||
const page = state.allFolders.currentPage; | ||
const pageIds = state.allFolders.pages[page]; | ||
|
||
// check if page ids are already cached or not | ||
if (pageIds === undefined) { | ||
return []; | ||
} else { | ||
return pageIds | ||
} | ||
} | ||
|
||
// SELECTORS | ||
// get all folders by mapping the array of only ids to the object containing | ||
// all folders by their key | ||
export const getAllFolders = createSelector( | ||
[getFoldersById, getFoldersAllIds], | ||
(foldersById, foldersAllIds) => { | ||
return foldersAllIds.map((allIdsKey) => foldersById[allIdsKey]); | ||
} | ||
) | ||
|
||
export const getAllFoldersOfCurrentPage = createSelector( | ||
[getFoldersById, getFolderIdsByPage], | ||
(aById, aIdsByPage) => { | ||
return aIdsByPage.map((pageIdKey) => aById[pageIdKey]); | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const FOLDERS_FETCH_REQUEST = 'FOLDERS_FETCH_REQUEST'; | ||
export const FOLDERS_FETCH_SUCCESS = 'FOLDERS_FETCH_SUCCESS'; | ||
export const FOLDERS_FETCH_FAILURE = 'FOLDERS_FETCH_FAILURE'; | ||
export const FOLDERS_FETCH_EXIT = 'FOLDERS_FETCH_EXIT'; | ||
export const FOLDERS_SET_CURRENT_PAGE = 'FOLDERS_SET_CURRENT_PAGE'; | ||
export const FOLDERS_FILTER_REQUEST = 'FOLDERS_FILTER_REQUEST'; | ||
|
||
export const FOLDERS_CLEAR_PAGE_CACHE = 'FOLDERS_CLEAR_PAGE_CACHE'; |
Oops, something went wrong.