Skip to content
This repository was archived by the owner on Sep 1, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"presets": ["env", "react"]
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [
["@babel/transform-runtime"]
]
}
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
indent_size = 2
indent_style = space
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
13 changes: 13 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports = {
"parser": "babel-eslint",
"extends": "eslint-config-airbnb",
"plugins": [
"react",
"react-hooks"
],
"rules": {
"react/jsx-uses-react": 1,
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
};
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"eslint.autoFixOnSave": true
}
27 changes: 22 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,34 @@
"build": "webpack --mode production"
},
"dependencies": {
"react": "^16.3.1",
"react-dom": "^16.3.1"
"@babel/runtime": "^7.6.3",
"date-fns": "^2.6.0",
"prop-types": "^15.7.2",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-redux": "^7.1.1",
"redux": "^4.0.4",
"redux-saga": "^1.1.1",
"styled-components": "^4.4.0"
},
"devDependencies": {
"babel-core": "6.26.*",
"babel-loader": "7.1.*",
"babel-preset-env": "1.7.0",
"@babel/core": "^7.6.4",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.6.3",
"@babel/preset-react": "^7.6.3",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"babel-preset-react": "6.24.*",
"css-loader": "2.1.*",
"eslint": "^6.6.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-hooks": "^2.2.0",
"html-loader": "0.5.*",
"html-webpack-plugin": "3.2.*",
"redux-devtools-extension": "^2.13.8",
"style-loader": "0.23.*",
"webpack": "4.29.*",
"webpack-cli": "3.2.*",
Expand Down
33 changes: 25 additions & 8 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';

import Header from './Header.jsx';
import Feed from './pages/Feed.jsx';

const App = () => {
return (
<div className='container'>
<Header/>
<div className="container-view">Some activities should be here</div>
import reducers from './reducers';
import saga from './sagas';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(
reducers,
composeWithDevTools(applyMiddleware(sagaMiddleware)),
);
sagaMiddleware.run(saga);

const App = () => (
<Provider store={store}>
<div className="container">
<Header />
<div className="container-view">
<Feed />
</div>
</div>
);
};
</Provider>
);

ReactDOM.render(<App/>, document.getElementById('app'));
ReactDOM.render(<App />, document.getElementById('app'));

export default App;
56 changes: 56 additions & 0 deletions src/actions/feed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export const FETCH_ACTIVITIES = 'FETCH_ACTIVITIES';
export const RECEIVE_ACTIVITIES = 'RECEIVE_ACTIVITIES';

export const FETCH_CALL_DETAILS = 'FETCH_CALL_DETAILS';
export const RECEIVE_CALL_DETAILS = 'RECEIVE_CALL_DETAILS';

export const ARCHIVE_CALL = 'ARCHIVE_CALL';
export const UPDATE_ARCHIVED_CALL = 'UPDATE_ARCHIVED_CALL';

export const RESET_ALL = 'RESET_ALL';

export function fetchActivities() {
return { type: FETCH_ACTIVITIES };
}

export function receiveActivities(payload) {
return {
type: RECEIVE_ACTIVITIES,
payload,
};
}

export function fetchCallDetails(callId) {
return {
type: FETCH_CALL_DETAILS,
callId,
};
}

export function receiveCallDetails(callId, payload) {
return {
type: RECEIVE_CALL_DETAILS,
callId,
payload,
};
}

export function archiveCall(callId) {
return {
type: ARCHIVE_CALL,
callId,
};
}

export function updateArchivedCall(callId) {
return {
type: UPDATE_ARCHIVED_CALL,
callId,
};
}

export function resetAll() {
return {
type: RESET_ALL,
};
}
31 changes: 31 additions & 0 deletions src/apis/baseAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const BASE_URL = 'https://aircall-job.herokuapp.com/';

export function getResource(
path = '',
{ base = BASE_URL } = {},
) {
const requestURL = `${base}${path}`;
const requestData = {
method: 'GET',
};

return fetch(requestURL, requestData)
.then((res) => res.json());
}

export function postResource(
path = '',
{ payload, base = BASE_URL } = {},
) {
const requestURL = `${base}${path}`;
const requestData = {
body: JSON.stringify(payload),
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
};

return fetch(requestURL, requestData)
.then((res) => res.json());
}
19 changes: 19 additions & 0 deletions src/apis/feedAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getResource, postResource } from './baseAPI';

export function getFeeds() {
return getResource('activities');
}

export function getDetail(activityId) {
return getResource(`activities/${activityId}`);
}

export function getResetAll(activityId) {
return getResource('reset');
}

export function archiveCall(activityId) {
return postResource(`activities/${activityId}`, {
payload: { is_archived: true },
});
}
71 changes: 71 additions & 0 deletions src/components/CallItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { format } from 'date-fns';

const BORDER_COLOR = {
missed: '#f98798',
answered: '#28c420',
default: '#ececeb',
};

const Wrapper = styled.div`
position: relative;
background: #fff;
border: 1px solid ${({ callType }) => BORDER_COLOR[callType] || BORDER_COLOR.default};
border-radius: 0.5rem;
padding: 0.5rem 1rem;
margin-bottom: 0.8rem;
cursor: pointer;

h5{
font-size: 1.3rem;
margin-block-start: 0.5rem;
margin-block-end: 0.5rem;
}
`;

const DateBox = styled.div`
position: absolute;
top: 0;
right: 0;
top: 1.2rem;
right: 0.6rem;
color: #8a8888;
`;

const CallItem = ({
to,
from,
via,
time,
callType,
direction,
onClick,
}) => {
const directionIcon = direction === 'outbound' ? '↖️' : '↘️';

return (
<Wrapper callType={callType} onClick={onClick}>
<h5>{`${directionIcon} ${to || 'Unknown'}`}</h5>
<p>{`${from} - ${via}`}</p>
<DateBox>{format(new Date(time), 'dd/MM HH:mm')}</DateBox>
</Wrapper>
);
};

CallItem.propTypes = {
to: PropTypes.string,
from: PropTypes.string.isRequired,
via: PropTypes.string.isRequired,
time: PropTypes.string.isRequired,
callType: PropTypes.string.isRequired,
direction: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
};

CallItem.defaultProps = {
to: undefined,
};

export default CallItem;
4 changes: 3 additions & 1 deletion src/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
height: 666px;
z-index: 100;

background: white;
background: #fdfdfc;
border-radius: 3px;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, .9);
position: relative;
overflow-y: scroll;
}

#app .container-view {
Expand Down
Loading