├── .env
├── public
├── index.css
├── logo.ico
├── robots.txt
├── logo192.png
├── logo512.png
├── fonts
│ ├── CircularStd-Bold.otf
│ ├── CircularStd-Book.otf
│ ├── CircularStd-Medium.otf
│ └── index.css
├── manifest.json
└── index.html
├── src
├── pages
│ ├── Task
│ │ ├── index.js
│ │ └── Task.js
│ └── ToDo
│ │ ├── index.js
│ │ └── ToDo.js
├── containers
│ ├── Layout
│ │ ├── index.js
│ │ └── Layout.js
│ └── AppContainer.js
├── redux
│ ├── api
│ │ ├── request.js
│ │ └── apiCall.js
│ ├── modules
│ │ ├── todo
│ │ │ ├── constants.js
│ │ │ ├── selectors.js
│ │ │ ├── actions.js
│ │ │ └── reducers.js
│ │ └── task
│ │ │ ├── constants.js
│ │ │ ├── selectors.js
│ │ │ ├── actions.js
│ │ │ └── reducers.js
│ ├── reducers.js
│ ├── sagas
│ │ ├── index.js
│ │ ├── todo.js
│ │ └── task.js
│ └── store.js
├── index.js
├── routes
│ └── index.js
└── serviceWorker.js
├── .eslintrc
├── config
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── pnpTs.js
├── getHttpsConfig.js
├── paths.js
├── env.js
├── modules.js
├── webpackDevServer.config.js
└── webpack.config.js
├── .gitignore
├── README.md
├── scripts
├── test.js
├── start.js
└── build.js
└── package.json
/.env:
--------------------------------------------------------------------------------
1 | NODE_PATH = "src/"
2 |
--------------------------------------------------------------------------------
/public/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: lightgray;
3 | }
--------------------------------------------------------------------------------
/src/pages/Task/index.js:
--------------------------------------------------------------------------------
1 | import Task from './Task'
2 |
3 | export default Task
4 |
--------------------------------------------------------------------------------
/src/pages/ToDo/index.js:
--------------------------------------------------------------------------------
1 | import ToDo from './ToDo'
2 |
3 | export default ToDo
4 |
--------------------------------------------------------------------------------
/public/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelaltao/Cohen-Company-Test/HEAD/public/logo.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/containers/Layout/index.js:
--------------------------------------------------------------------------------
1 | import Layout from './Layout'
2 |
3 | export default Layout
4 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelaltao/Cohen-Company-Test/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelaltao/Cohen-Company-Test/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/public/fonts/CircularStd-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelaltao/Cohen-Company-Test/HEAD/public/fonts/CircularStd-Bold.otf
--------------------------------------------------------------------------------
/public/fonts/CircularStd-Book.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelaltao/Cohen-Company-Test/HEAD/public/fonts/CircularStd-Book.otf
--------------------------------------------------------------------------------
/public/fonts/CircularStd-Medium.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaelaltao/Cohen-Company-Test/HEAD/public/fonts/CircularStd-Medium.otf
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "react-app",
3 | "rules": {
4 | "semi" : [2, "never"],
5 | "max-len": [2, 140, 2],
6 | "react/no-unused-prop-types": 0
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/redux/api/request.js:
--------------------------------------------------------------------------------
1 | export const requestPending = type => `${type}/pending`
2 |
3 | export const requestSuccess = type => `${type}/success`
4 |
5 | export const requestFail = type => `${type}/fail`
6 |
--------------------------------------------------------------------------------
/src/redux/modules/todo/constants.js:
--------------------------------------------------------------------------------
1 | export const GET_TODO_LIST = 'GET_TODO_LIST'
2 | export const ADD_TODO = 'ADD_TODO'
3 | export const DELETE_TODO = 'DELETE_TODO'
4 | export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE'
5 |
--------------------------------------------------------------------------------
/src/redux/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import todo from './modules/todo/reducers'
3 | import task from './modules/task/reducers'
4 |
5 | export default combineReducers({
6 | todo,
7 | task,
8 | })
9 |
--------------------------------------------------------------------------------
/src/redux/sagas/index.js:
--------------------------------------------------------------------------------
1 | import { all } from 'redux-saga/effects'
2 | import todo from './todo'
3 | import task from './task'
4 |
5 | export default function* rootSaga() {
6 | yield all([
7 | todo(),
8 | task()
9 | ])
10 | }
11 |
--------------------------------------------------------------------------------
/src/containers/AppContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Provider } from 'react-redux'
3 | import store from 'redux/store'
4 | import Routes from 'routes'
5 |
6 | export default () => (
7 |
8 |
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/src/containers/Layout/Layout.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Box } from '@mui/material'
3 |
4 | const Layout = ({ children }) => {
5 | return (
6 |
7 | {children}
8 |
9 | )
10 | }
11 |
12 | export default Layout
13 |
--------------------------------------------------------------------------------
/src/redux/modules/todo/selectors.js:
--------------------------------------------------------------------------------
1 | import { get } from 'lodash'
2 |
3 | export const todoStateSelector = (state) =>
4 | get(state, 'todo')
5 |
6 | export const todoListSelector = (state) =>
7 | get(state, 'todo.todos')
8 |
9 | export const errorMessageSelector = (state) =>
10 | get(state, 'todo.error')
11 |
--------------------------------------------------------------------------------
/src/redux/modules/task/constants.js:
--------------------------------------------------------------------------------
1 | export const GET_TASK_LIST = 'GET_TASK_LIST'
2 | export const DELETE_TASK = 'DELETE_TASK'
3 | export const ADD_TASK = 'ADD_TASK'
4 | export const GET_TASK = 'GET_TASK'
5 | export const UPDATE_TASK = 'UPDATE_TASK'
6 | export const GET_ALL_TASKS = 'GET_ALL_TASKS'
7 | export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE'
8 |
--------------------------------------------------------------------------------
/src/redux/modules/todo/actions.js:
--------------------------------------------------------------------------------
1 | import { createAction } from 'redux-actions'
2 | import * as CONSTANTS from './constants'
3 |
4 | export const getTodoList = createAction(CONSTANTS.GET_TODO_LIST)
5 | export const addTodo = createAction(CONSTANTS.ADD_TODO)
6 | export const deleteTodo = createAction(CONSTANTS.DELETE_TODO)
7 | export const setErrorMessage = createAction(CONSTANTS.SET_ERROR_MESSAGE)
8 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/redux/modules/task/selectors.js:
--------------------------------------------------------------------------------
1 | import { get } from 'lodash'
2 |
3 | export const taskStateSelector = (state) =>
4 | get(state, 'task')
5 |
6 | export const taskListSelector = (state) =>
7 | get(state, 'task.tasks')
8 |
9 | export const taskSelector = (state) =>
10 | get(state, 'task.task')
11 |
12 | export const allTasksSelector = (state) =>
13 | get(state, 'task.allTasks')
14 |
15 | export const errorMessageSelector = (state) =>
16 | get(state, 'task.error')
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | #IDE
4 | .idea
5 |
6 | # dependencies
7 | /node_modules
8 | /.pnp
9 | .pnp.js
10 |
11 | # testing
12 | /coverage
13 |
14 | # production
15 | /build
16 |
17 | # misc
18 | .DS_Store
19 | .env.local
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 |
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | package-lock.json
29 | yarn.lock
30 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './containers/AppContainer'
4 | import * as serviceWorker from './serviceWorker'
5 |
6 | ReactDOM.render(
7 |
8 | ,
9 | document.getElementById('root'))
10 |
11 | // If you want your app to work offline and load faster, you can change
12 | // unregister() to register() below. Note this comes with some pitfalls.
13 | // Learn more about service workers: https://bit.ly/CRA-PWA
14 | serviceWorker.unregister()
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Cohen
4 |
5 | ## Quick start
6 |
7 | ## 1.install
8 |
9 | ### Steps
10 |
11 | 1. Type following commands:
12 | ``` console
13 | npm install
14 | or
15 | yarn install
16 | ```
17 |
18 | 2. Serve your app:
19 | ``` console
20 | npm start
21 | or
22 | yarn start
23 | ```
24 | ## 2.Build
25 | ```sh
26 | npm run build or yarn build
27 | ```
28 |
29 | Builds the app for production to the `build` folder.
30 |
31 | Your app is ready to be deployed.
32 |
33 | ## 3.User Guide
34 |
35 | soon..
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Cohen",
3 | "name": "Cohen Company",
4 | "icons": [
5 | {
6 | "src": "logo.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png"
13 | },
14 | {
15 | "src": "logo512.png",
16 | "type": "image/png"
17 | }
18 | ],
19 | "start_url": ".",
20 | "display": "standalone",
21 | "theme_color": "#000000",
22 | "background_color": "#ffffff"
23 | }
24 |
--------------------------------------------------------------------------------
/public/fonts/index.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'CircularStd';
3 | font-weight: 400;
4 | font-style: normal;
5 | src: local('CircularStd'), url('CircularStd-Book.otf') format('opentype');
6 | }
7 | @font-face {
8 | font-family: 'CircularStd';
9 | font-weight: 500;
10 | font-style: normal;
11 | src: local('CircularStd'), url('CircularStd-Medium.otf') format('opentype');
12 | }
13 | @font-face {
14 | font-family: 'CircularStd';
15 | font-weight: 700;
16 | font-style: normal;
17 | src: local('CircularStd'), url('CircularStd-Bold.otf') format('opentype');
18 | }
19 |
--------------------------------------------------------------------------------
/src/redux/modules/task/actions.js:
--------------------------------------------------------------------------------
1 | import { createAction } from 'redux-actions'
2 | import * as CONSTANTS from './constants'
3 |
4 | export const getTasks = createAction(CONSTANTS.GET_TASK_LIST)
5 | export const deleteTask = createAction(CONSTANTS.DELETE_TASK)
6 | export const addTask = createAction(CONSTANTS.ADD_TASK)
7 | export const getTask = createAction(CONSTANTS.GET_TASK)
8 | export const updateTask = createAction(CONSTANTS.UPDATE_TASK)
9 | export const getAllTasks = createAction(CONSTANTS.GET_ALL_TASKS)
10 | export const setErrorMessage = createAction(CONSTANTS.SET_ERROR_MESSAGE)
11 |
--------------------------------------------------------------------------------
/src/routes/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
3 | import ToDo from 'pages/ToDo'
4 | import Task from 'pages/Task'
5 | import Layout from 'containers/Layout'
6 | import ScrollToTop from 'react-router-scroll-top'
7 |
8 | const routes = () => (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | )
20 |
21 | export default (routes)
22 |
--------------------------------------------------------------------------------
/config/pnpTs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { resolveModuleName } = require('ts-pnp');
4 |
5 | exports.resolveModuleName = (
6 | typescript,
7 | moduleName,
8 | containingFile,
9 | compilerOptions,
10 | resolutionHost
11 | ) => {
12 | return resolveModuleName(
13 | moduleName,
14 | containingFile,
15 | compilerOptions,
16 | resolutionHost,
17 | typescript.resolveModuleName
18 | );
19 | };
20 |
21 | exports.resolveTypeReferenceDirective = (
22 | typescript,
23 | moduleName,
24 | containingFile,
25 | compilerOptions,
26 | resolutionHost
27 | ) => {
28 | return resolveModuleName(
29 | moduleName,
30 | containingFile,
31 | compilerOptions,
32 | resolutionHost,
33 | typescript.resolveTypeReferenceDirective
34 | );
35 | };
36 |
--------------------------------------------------------------------------------
/src/redux/sagas/todo.js:
--------------------------------------------------------------------------------
1 | import { takeLatest } from 'redux-saga/effects'
2 | import * as CONSTANTS from '../modules/todo/constants'
3 | import apiCall from '../api/apiCall'
4 |
5 | const doGetTodoList = apiCall({
6 | type: CONSTANTS.GET_TODO_LIST,
7 | method: 'get',
8 | path: 'todos',
9 | })
10 |
11 | const doAddTodo = apiCall({
12 | type: CONSTANTS.ADD_TODO,
13 | method: 'post',
14 | path: 'todos',
15 | })
16 |
17 | const doDeleteTodo = apiCall({
18 | type: CONSTANTS.DELETE_TODO,
19 | method: 'delete',
20 | path: ({ payload }) => `/todos/${payload.id}`,
21 | })
22 |
23 | export default function* rootSaga() {
24 | yield takeLatest(CONSTANTS.GET_TODO_LIST, doGetTodoList)
25 | yield takeLatest(CONSTANTS.ADD_TODO, doAddTodo)
26 | yield takeLatest(CONSTANTS.DELETE_TODO, doDeleteTodo)
27 | }
28 |
--------------------------------------------------------------------------------
/src/redux/modules/todo/reducers.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 | import { requestSuccess, requestFail } from 'redux/api/request'
3 | import * as CONSTANTS from './constants'
4 |
5 | const getInitialState = () => {
6 | return {
7 | todos: [],
8 | error: '',
9 | }
10 | }
11 |
12 | export default handleActions({
13 | [requestSuccess(CONSTANTS.GET_TODO_LIST)]: (state, { payload }) => ({
14 | ...state,
15 | todos: payload,
16 | }),
17 | [requestSuccess(CONSTANTS.ADD_TODO)]: (state, { payload }) => ({
18 | ...state,
19 | todos: payload,
20 | }),
21 | [requestFail(CONSTANTS.ADD_TODO)]: (state, { payload }) => ({
22 | ...state,
23 | error: payload.data.message,
24 | }),
25 | [requestSuccess(CONSTANTS.DELETE_TODO)]: (state, { payload }) => ({
26 | ...state,
27 | todos: payload,
28 | }),
29 | [(CONSTANTS.SET_ERROR_MESSAGE)]: (state, { payload }) => ({
30 | ...state,
31 | error: payload,
32 | })
33 |
34 | }, getInitialState())
35 |
--------------------------------------------------------------------------------
/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux'
2 | import { routerMiddleware } from 'react-router-redux'
3 | import createSagaMiddleware from 'redux-saga'
4 | import { createBrowserHistory as createHistory } from 'history'
5 | import rootReducer from './reducers'
6 | import sagas from './sagas'
7 |
8 | export const history = createHistory()
9 |
10 | const initialState = {}
11 | const enhancers = []
12 |
13 | const sagaMiddleware = createSagaMiddleware()
14 |
15 | const middleware = [
16 | sagaMiddleware,
17 | routerMiddleware(history)
18 | ]
19 |
20 | if (process.env.NODE_ENV === 'development') {
21 | const devToolsExtension = window.devToolsExtension
22 |
23 | if (typeof devToolsExtension === 'function') {
24 | enhancers.push(devToolsExtension())
25 | }
26 | }
27 |
28 | const composedEnhancers = compose(
29 | applyMiddleware(...middleware),
30 | ...enhancers
31 | )
32 |
33 | const store = createStore(
34 | rootReducer,
35 | initialState,
36 | composedEnhancers
37 | )
38 |
39 | sagaMiddleware.run(sagas)
40 |
41 | export default store
42 |
--------------------------------------------------------------------------------
/src/redux/modules/task/reducers.js:
--------------------------------------------------------------------------------
1 | import { handleActions } from 'redux-actions'
2 | import { requestSuccess, requestFail } from 'redux/api/request'
3 | import * as CONSTANTS from './constants'
4 |
5 | const getInitialState = () => {
6 | return {
7 | tasks: [],
8 | task: {},
9 | allTasks: [],
10 | error: '',
11 | }
12 | }
13 |
14 | export default handleActions({
15 | [requestSuccess(CONSTANTS.GET_TASK_LIST)]: (state, { payload }) => ({
16 | ...state,
17 | tasks: payload,
18 | }),
19 | [requestSuccess(CONSTANTS.DELETE_TASK)]: (state, { payload }) => ({
20 | ...state,
21 | tasks: payload,
22 | }),
23 | [requestSuccess(CONSTANTS.ADD_TASK)]: (state, { payload }) => ({
24 | ...state,
25 | tasks: payload,
26 | }),
27 | [requestFail(CONSTANTS.ADD_TASK)]: (state, { payload }) => ({
28 | ...state,
29 | error: payload.data.message,
30 | }),
31 | [requestSuccess(CONSTANTS.GET_TASK)]: (state, { payload }) => ({
32 | ...state,
33 | task: payload,
34 | }),
35 | [requestSuccess(CONSTANTS.UPDATE_TASK)]: (state, { payload }) => ({
36 | ...state,
37 | tasks: payload,
38 | }),
39 | [requestSuccess(CONSTANTS.GET_ALL_TASKS)]: (state, { payload }) => ({
40 | ...state,
41 | allTasks: payload,
42 | }),
43 | [(CONSTANTS.SET_ERROR_MESSAGE)]: (state, { payload }) => ({
44 | ...state,
45 | error: payload,
46 | })
47 |
48 | }, getInitialState())
49 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const camelcase = require('camelcase');
5 |
6 | // This is a custom Jest transformer turning file imports into filenames.
7 | // http://facebook.github.io/jest/docs/en/webpack.html
8 |
9 | module.exports = {
10 | process(src, filename) {
11 | const assetFilename = JSON.stringify(path.basename(filename));
12 |
13 | if (filename.match(/\.svg$/)) {
14 | // Based on how SVGR generates a component name:
15 | // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
16 | const pascalCaseFilename = camelcase(path.parse(filename).name, {
17 | pascalCase: true,
18 | });
19 | const componentName = `Svg${pascalCaseFilename}`;
20 | return `const React = require('react');
21 | module.exports = {
22 | __esModule: true,
23 | default: ${assetFilename},
24 | ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
25 | return {
26 | $$typeof: Symbol.for('react.element'),
27 | type: 'svg',
28 | ref: ref,
29 | key: null,
30 | props: Object.assign({}, props, {
31 | children: ${assetFilename}
32 | })
33 | };
34 | }),
35 | };`;
36 | }
37 |
38 | return `module.exports = ${assetFilename};`;
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/src/redux/sagas/task.js:
--------------------------------------------------------------------------------
1 | import { takeLatest } from 'redux-saga/effects'
2 | import * as CONSTANTS from '../modules/task/constants'
3 | import apiCall from '../api/apiCall'
4 |
5 | const doGetTaskList = apiCall({
6 | type: CONSTANTS.GET_TASK_LIST,
7 | method: 'get',
8 | path: ({ payload }) => `task/${payload.id}`,
9 | })
10 |
11 | const doDeleteTask = apiCall({
12 | type: CONSTANTS.DELETE_TASK,
13 | method: 'delete',
14 | path: ({ payload }) => `task/${payload.id}/${payload.task_id}`
15 | })
16 |
17 | const doAddTask = apiCall({
18 | type: CONSTANTS.ADD_TASK,
19 | method: 'post',
20 | path: ({ payload }) => `task/${payload.id}`,
21 | })
22 |
23 | const doGetTask = apiCall({
24 | type: CONSTANTS.GET_TASK,
25 | method: 'get',
26 | path: ({ payload }) => `tasks/${payload.task_id}`,
27 | })
28 |
29 | const doUpdateTask = apiCall({
30 | type: CONSTANTS.UPDATE_TASK,
31 | method: 'put',
32 | path: ({ payload }) => `task/${payload.id}/${payload.task_id}`,
33 | })
34 |
35 | const doGetAllTasks = apiCall({
36 | type: CONSTANTS.GET_ALL_TASKS,
37 | method: 'get',
38 | path: 'task'
39 | })
40 |
41 | export default function* rootSaga() {
42 | yield takeLatest(CONSTANTS.GET_TASK_LIST, doGetTaskList)
43 | yield takeLatest(CONSTANTS.DELETE_TASK, doDeleteTask)
44 | yield takeLatest(CONSTANTS.ADD_TASK, doAddTask)
45 | yield takeLatest(CONSTANTS.GET_TASK, doGetTask)
46 | yield takeLatest(CONSTANTS.UPDATE_TASK, doUpdateTask)
47 | yield takeLatest(CONSTANTS.GET_ALL_TASKS, doGetAllTasks)
48 | }
49 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI or explicitly running all tests
42 | if (
43 | !process.env.CI &&
44 | argv.indexOf('--watchAll') === -1 &&
45 | argv.indexOf('--watchAll=false') === -1
46 | ) {
47 | // https://github.com/facebook/create-react-app/issues/5210
48 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
49 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
50 | }
51 |
52 |
53 | jest.run(argv);
54 |
--------------------------------------------------------------------------------
/src/redux/api/apiCall.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { call, put } from 'redux-saga/effects'
3 | import { get } from 'lodash'
4 | import { requestFail, requestPending, requestSuccess } from './request'
5 |
6 | export default ({
7 | type,
8 | method, // one of 'get', 'post', 'put', 'delete'
9 | path,
10 | success,
11 | fail,
12 | payloadOnSuccess,
13 | payloadOnFail
14 | }) => function* (action) {
15 | const {
16 | body,
17 | params,
18 | success: successCallback,
19 | fail: failCallback
20 | } = (action.payload || {})
21 |
22 | axios.defaults.baseURL = 'http://localhost:3000'
23 |
24 | let headers = {
25 | 'Accept': '*/*',
26 | 'Content-Type': 'application/json',
27 | }
28 |
29 | try {
30 | yield put({
31 | type: requestPending(type)
32 | })
33 |
34 | const res = yield call(axios.request, {
35 | url: typeof path === 'function' ? path(action) : path,
36 | method: method.toLowerCase(),
37 | headers: Object.assign({}, headers),
38 | data: body,
39 | params
40 | })
41 |
42 | success && success(res, action)
43 | successCallback && successCallback(res)
44 |
45 | yield put({
46 | type: requestSuccess(type),
47 | payload: payloadOnSuccess ? payloadOnSuccess(res.data, action) : res.data
48 | })
49 | } catch (err) {
50 | const errRes = get(err, 'response', err)
51 |
52 | if (errRes.data && (errRes.data.code === 401 && errRes.data.message === 'Please authenticate')) {
53 | yield put({
54 | type: 'IS_UNAUTHORIZED',
55 | })
56 | }
57 |
58 | fail && fail(errRes)
59 | failCallback && failCallback(errRes)
60 |
61 | yield put({
62 | type: requestFail(type),
63 | payload: payloadOnFail ? payloadOnFail(errRes, action) : errRes
64 | })
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
26 |
27 | Cohen Company Assessment
28 |
29 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/config/getHttpsConfig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 | const crypto = require('crypto');
6 | const chalk = require('react-dev-utils/chalk');
7 | const paths = require('./paths');
8 |
9 | // Ensure the certificate and key provided are valid and if not
10 | // throw an easy to debug error
11 | function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
12 | let encrypted;
13 | try {
14 | // publicEncrypt will throw an error with an invalid cert
15 | encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
16 | } catch (err) {
17 | throw new Error(
18 | `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
19 | );
20 | }
21 |
22 | try {
23 | // privateDecrypt will throw an error with an invalid key
24 | crypto.privateDecrypt(key, encrypted);
25 | } catch (err) {
26 | throw new Error(
27 | `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
28 | err.message
29 | }`
30 | );
31 | }
32 | }
33 |
34 | // Read file and throw an error if it doesn't exist
35 | function readEnvFile(file, type) {
36 | if (!fs.existsSync(file)) {
37 | throw new Error(
38 | `You specified ${chalk.cyan(
39 | type
40 | )} in your env, but the file "${chalk.yellow(file)}" can't be found.`
41 | );
42 | }
43 | return fs.readFileSync(file);
44 | }
45 |
46 | // Get the https config
47 | // Return cert files if provided in env, otherwise just true or false
48 | function getHttpsConfig() {
49 | const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
50 | const isHttps = HTTPS === 'true';
51 |
52 | if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
53 | const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
54 | const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
55 | const config = {
56 | cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
57 | key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
58 | };
59 |
60 | validateKeyAndCerts({ ...config, keyFile, crtFile });
61 | return config;
62 | }
63 | return isHttps;
64 | }
65 |
66 | module.exports = getHttpsConfig;
67 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebook/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
13 | // "public path" at which the app is served.
14 | // webpack needs to know it to put the right