Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Here you go :) #7

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["react", "es2015", "stage-1"]
}
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
npm-debug.log
node_modules
.idea/
node_modules/
public/js/
public/css/
17 changes: 17 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
language: node_js
node_js:
- "6"

notifications:
email: false

cache:
directories:
- node_modules

install:
- npm install

script:
- npm run build-dev
- npm run build-prod
62 changes: 2 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,3 @@
# Adapt React Coding Challenge

Seems like you're trying out for a position at
[Adapt](https://adapt.dk/en) or you've found this and would like to
apply. Fork this repo and go at it ;)

Your goal is to set up a React application, where users will be able to
edit book info (and create new books, if you have enough time to
implement it). Once you are done with the challenge, please fire up a
Pull Request and we will get in touch.

## Brief

I am a user of the app and I want to create a book object and edit
previously provided info about it, so that my reading list can stay up
to date. The form should be split into three steps:

1. Choose subject (one of the two).
2. Depending on the selection in the first step, display a list of
reading material. Choose one.
3. When reading material chosen, display all the info that's available
about the book in a form (meaning that the book info can be edited).

## Requirements

* All steps should be visible on the screen and changable at all times
(when they are available -- step 1 when nothing is picked, step 1
and 2 when step 1 is picked and step 1, 2 and 3 when step 2 is
picked).

* You can use whatever libraries, task runners and build processes you
like. React and plain JavaScript are the only requirements (ES6
encouraged, but no TypeScript, CoffeeScript, etc). Redux is strongly
encouraged if you see a need for it.

### Suggested order of completion

This depends on how much time you were given to accomplish the task.
Ideally you would provide a solution for each of the outlined steps
unless they are marked as optional.

1. Data fetching from the api.
2. Form steps logic.
3. (optional) Saving the data.
4. (optional) Styling (minor for a 2-3 h challenge, more if there's more time).

## API Usage

API can be launched using `npm start`. You will need to run `npm
install` once starting working on the project to install dependencies.

| Endpoint | Result |
|------------------------------|-----------------------------------------------------|
| /books?subjects_like=Fiction | Lists all books that contain "Fiction" as a subject |
| /subjects | Lists all available subjects |

---

More info about API usage can be found at the [json-server
repo](https://github.com/typicode/json-server).
packes are installed with 'yarn install'

"react-hot-loader": "^3.0.0" is beta, so you need to select it when installing with yarn
74 changes: 67 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,74 @@
{
"name": "react-coding-challenge",
"name": "redux-minimal",
"version": "1.0.0",
"description": "A package containing a simple dummy book API, which should be used in the challenge",
"description": "Start building complex react-redux apps today, with this minimalist easy to understand starter kit (boilerplate)",
"keywords": [
"react",
"redux",
"minimal",
"starter kit",
"boilerplate"
],
"main": "index.js",
"homepage": "http://redux-minimal.js.org/",
"repository": {
"type": "git",
"url": "https://github.com/catalin-luntraru/redux-minimal"
},
"scripts": {
"start": "json-server --port 3010 --watch api.json"
"start": "webpack-dev-server --inline --hot --history-api-fallback --host localhost --port 8080",
"build-dev": "webpack --config webpack.dev.config.js",
"build-prod": "webpack -p --config webpack.prod.config.js",
"test": "mocha --recursive --compilers js:babel-register --require babel-polyfill --require ignore-styles",
"test-watch": "npm test -- --watch"
},
"babel": {
"presets": [
"es2015",
"react",
"stage-3"
]
},
"author": "Catalin Luntraru",
"license": "MIT",
"dependencies": {
"material-ui": "^0.18.7",
"react": "^15.4.2",
"react-dom": "^15.4.2",
"react-hot-loader": "^3.0.0",
"react-modal": "^2.2.2",
"react-redux": "^5.0.2",
"react-router": "^3.0.1",
"react-router-redux": "^4.0.7",
"react-tap-event-plugin": "^2.0.1",
"redux": "^3.6.0",
"redux-form": "^6.4.3",
"redux-saga": "^0.14.3",
"typeface-roboto": "0.0.31"
},
"keywords": ["react challenge", "tryouts", "react developer", "hiring"],
"author": "Adapt A/S",
"license": "ISC",
"devDependencies": {
"json-server": "0.10.1"
"axios": "^0.16.2",
"babel-core": "^6.21.0",
"babel-loader": "^6.2.10",
"babel-polyfill": "^6.20.0",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-1": "^6.24.1",
"babel-preset-stage-3": "^6.17.0",
"babel-runtime": "^6.20.0",
"clean-webpack-plugin": "^0.1.15",
"css-loader": "^0.26.1",
"enzyme": "^2.7.0",
"extract-text-webpack-plugin": "^1.0.1",
"ignore-styles": "^5.0.1",
"mocha": "^3.2.0",
"node-sass": "^4.3.0",
"react-addons-test-utils": "^15.4.2",
"redux-freeze": "^0.1.5",
"sass-loader": "^4.1.1",
"style-loader": "^0.13.1",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2",
"whatwg-fetch": "^2.0.1"
}
}
Binary file added public/favicon.ico
Binary file not shown.
21 changes: 21 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Simple users app built with redux-minimal</title>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
<link rel="stylesheet" href="/css/main.css">
</head>
<body>

<div id="app"></div>
<script src="/js/bundle.js"></script>

</body>
</html>
14 changes: 14 additions & 0 deletions public/media/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/pattern.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import axios from 'axios'
axios.defaults.headers.common['Content-Type'] = 'application/json'
import {bookFormatToSubmit} from '../helpers/bookFormat'

export function getSubjects() {
return axios.get('http://localhost:3010/subjects')
}

export function getBooksBySubject(subject) {
return axios.get(`http://localhost:3010/books?subjects_like=${subject}`)
}

export function updateBook(book) {
const newBook = bookFormatToSubmit(book)
return axios.put(`http://localhost:3010/books/${book.id}`, {...newBook})
}

export function createBook(book) {
const newBook = bookFormatToSubmit(book)
return axios.post(`http://localhost:3010/books`, {...newBook})
}
17 changes: 17 additions & 0 deletions src/components/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react";
import "../stylesheets/main.scss";
import { connect } from "react-redux";

// app component
class App extends React.Component {
// render
render() {
return (
<div className="container">
{this.props.children}
</div>
);
}
}

export default connect()(App);
35 changes: 35 additions & 0 deletions src/components/Book.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, {PropTypes} from 'react'
import RaisedButton from 'material-ui/RaisedButton';
import {
TableRow,
TableRowColumn,
} from 'material-ui/Table'

const style = {
margin: 12,
};

const Book = ({book, editBook}) => {
return(
<TableRow>
<TableRowColumn><strong>{book.title}</strong></TableRowColumn>
<TableRowColumn>
<RaisedButton
label="Edit"
labelPosition="before"
primary={true}
icon={<i className="material-icons">mode_edit</i>}
style={style}
onTouchTap={() => { editBook(book) }}
/>
</TableRowColumn>
</TableRow>
)
}

Book.propTypes = {
book: PropTypes.object,
editBook: PropTypes.func,
}

export default Book
54 changes: 54 additions & 0 deletions src/components/BookForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react'
import { Field, FieldArray, reduxForm } from 'redux-form'
import RaisedButton from 'material-ui/RaisedButton'
import validate from '../helpers/bookFormValidate'
import {integersOnly} from '../helpers/normalize'
import renderTextField from './forms/bookForm/renderTextField'
import renderAuthors from './forms/bookForm/renderAuthors'
import renderSingle from './forms/bookForm/renderSingle'
import renderFormats from './forms/bookForm/renderFormats'

const BookForm = props => {
const {handleSubmit, pristine, reset, submitting, method, type} = props
const id_disabled = type && type === 'edit' ? true : false
return(
<form className="bookForm" onSubmit={handleSubmit(method)}>
<div>
<Field disabled={id_disabled} normalize={integersOnly} name="id" component={renderTextField} label="ID"/>
</div>
<div>
<Field name="title" component={renderTextField} label="Title"/>
</div>
<div>
<Field normalize={integersOnly} name="download_count" component={renderTextField} label="Download count"/>
</div>
<div>
<Field name="media_type" component={renderTextField} label="Media Type"/>
</div>
<div>
<FieldArray title={'Subjects'} props={{name: 'Subject'}} name="subjects" component={renderSingle}/>
</div>
<div>
<FieldArray title={'Authors'} name="authors" component={renderAuthors}/>
</div>
<div>
<FieldArray title={'Bookshelves'} props={{name: 'Bookshelf'}} name="bookshelves" component={renderSingle}/>
</div>
<div>
<FieldArray title={'Formats'} name="formats" component={renderFormats}/>
</div>
<div>
<FieldArray title={'Languages'} props={{name: 'Language'}} name="languages" component={renderSingle}/>
</div>
<div>
<RaisedButton type="submit" disabled={submitting} label="Save" primary={true} style={{margin: 12}} />
</div>
</form>
)
}

export default reduxForm({
form: 'BookForm2',
enableReinitialize: true,
validate,
})(BookForm)
26 changes: 26 additions & 0 deletions src/components/BookModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, {PropTypes} from 'react'
import ReactModal from 'react-modal'
import BookForm from './BookForm'

const BookModal = ({show, close, bookFormProps}) => {
return(
<div>
<ReactModal
contentLabel={'Book edit form'}
isOpen={show}
style={{overlay: {zIndex: 3}}}
onRequestClose={() => close()}
>
<BookForm {...bookFormProps}/>
</ReactModal>
</div>
)
}

BookModal.propTypes = {
show: PropTypes.bool,
close: PropTypes.func,
bookFormProps: PropTypes.object,
}

export default BookModal
Loading