Skip to content
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
4 changes: 4 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"preset": ["es2015", "react"],
"plugins": ["transform-object-rest-spread"],
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
47 changes: 47 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "26-react-redux",
"version": "1.0.0",
"description": "![cf](https://i.imgur.com/7v5ASc8.png) 26: React & Redux ======",
"main": "main.js",
"scripts": {
"build": "webpack",
"watch": "webpack-dev-server --inline --hot"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Loaye/26-react-redux.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/Loaye/26-react-redux/issues"
},
"homepage": "https://github.com/Loaye/26-react-redux#readme",
"dependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^0.1.16",
"css-loader": "^0.28.5",
"dotenv": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^0.11.2",
"html-webpack-plugin": "^2.30.1",
"node-sass": "^4.5.3",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
"react-test-renderer": "^15.6.1",
"redux": "^3.7.2",
"sass-loader": "^6.0.6",
"superagent": "^3.6.0",
"uglifyjs-webpack-plugin": "^0.4.6",
"url-loader": "^0.5.9",
"webpack": "^3.5.5",
"webpack-dev-server": "^2.7.1"
}
}
22 changes: 22 additions & 0 deletions src/action/category-action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import uuid from'uuid/v1';

export const categoryCreate = (category) => {
category.id = uuid();
category.timestamp = new Date();
return {
type: 'CATEGORY_CREATE',
payload: category
}
}

export const categoryUpdate = (category) => ({
type: 'CATEGORY_CREATE',
payload: category
})

export const categoryDelete = (category) => ({
type: 'CATEGORY_DELETE',
payload: category
})

export const categoryReset = () => ({type: 'CATEGORY_RESET'})
31 changes: 31 additions & 0 deletions src/component/app/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import {Provider} from 'react-redux';
import {BroswerRouter} from 'react-router-dom';
import createAppStore from '../../lib/store.js';
import DashboardContainer from '../dashboard-container'

const store = createAppStore();

class App extends React.Component{
componentDidMount(){
store.subscribe(() => {
console.log('--STATE--', store.getState())
});

store.dispatch({type: null});
}

render(){
return(
<section>
<Provider>
<BroswerRouter>
<Route exact path='/' component={DashboardContainer} />
</BroswerRouter>
</Provider>
</section>
)
}
}

export default App;
39 changes: 39 additions & 0 deletions src/component/category-form/category-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';

class CategoryForm extends React.Component{
constructor(props){
super(props);

this.state = {
title: props.category ? props.category.title : ''
}

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(e){
this.setState({title: e.target.value});
}

handleSubmit(e){
e.preventDefault();
this.props.onComplete(Object.assign({}, this.state))
}

render(){
return(
<form className='category-form' onSubmit={this.handleSubmit}>
<input
name='title'
type='text'
placeholder='title'
value={this.state.title}
onChange={this.handleChange}
/>

<button type='submit'>{this.props.buttonText}</button>
</form>
)
}
}
52 changes: 52 additions & 0 deletions src/component/dashboard-container/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import {connect} from 'react-redux';

import{
categoryCreate,
categoryUpdate,
categoryDelete,
} from '../../action/category-actions.js';

import categoryForm from '../category-form';

class DashboardContainer extends React.Component{
componentDidMount(){
this.props.categoryCreate({title: 'some category'});
this.props.categoryCreate({title: 'another category'});
this.props.categoryCreate({title: 'cool category'});
}

render(){
return(
<main className='dashboard-container'>
<h2>Dashboard</h2>

<CategoryForm
buttonText='create category'
onComplete={this.props.categoryCreate} />

{this.props.categories.map((item) =>
<div key={item.id}>
<h3>{item.title}</h3>
</div>
)}
</main>
)
}
}

const mapStateToProps = (state) => {
return{
categories: state
}
}

const mapDispatchToProps = (dispatch, getState) => {
return{
categoryCreate: (category) => dispatch(categoryCreate(category)),
categoryUpdate: (category) => dispatch(categoryUpdate(category)),
categoryDelete: (category) => dispatch(categoryDelete(category)),
}
}

export default connect(mapStateToProps, mapDispatchToProps)(DashboardContainer);
9 changes: 9 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Budget Tracker</title>
</head>
<body>
<div id='root'></div>
</body>
</html>
4 changes: 4 additions & 0 deletions src/lib/store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import {createStore} from 'redux';
import reducer from '../reducer/category.js';

export default () => createStore(reducer);
4 changes: 4 additions & 0 deletions src/lib/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const renderIf = (test, component) => test ? component : undefined;

export const classToggler = (options) =>
Object.keys(options).filter(key => !!options[key]).join(' ');
5 changes: 5 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';
import ReactDom from 'react-dom';
import App from './component/app';

ReactDom.render(<App />, document.getElementById('root'));
23 changes: 23 additions & 0 deletions src/reducer/category.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
let initialState = [];

export default (state=initialState, action) => {
let {type, payload} = action;

switch(type){
case 'CATEGORY_CREATE':
return [...state, payload]

case 'CATEGORY_UPDATE':
return state.map(category =>
category.id === payload.id ? payload : category)

case 'CATEGORY_DELETE':
return state.filter(category => category.id !== payload.id)

case 'CATEGORY_RESET':
return initialState

default:
return state
}
}
85 changes: 85 additions & 0 deletions webpack-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict';

require('dotenv').config({ path: `${__dirname}/.dev.env` });
const production = process.env.NODE_ENV === 'production';

const {DefinePlugin, EnvironmentPlugin} = require('webpack');
const HtmlPlugin = require('html-webpack-plugin');
const CleanPlugin = require('clean-webpack-plugin');
const UglifyPlugin = require('uglifyjs-webpack-plugin');
const ExtractPlugin = require('extract-text-webpack-plugin');

let plugins = [
new EnvironmentPlugin(['NODE_ENV']),
new ExtractPlugin('bundle-[hash].css'),
new HtmlPlugin({ template: `${__dirname}/src/index.html` }),
new DefinePlugin({
__DEBUG__: JSON.stringify(!production)
})
]

if (production) {
plugins = plugins.concat([ new CleanPlugin(), new UglifyPlugin() ]);
}

module.exports = {
plugins,
entry: `${__dirname}/src/main.js`,
devServer: {
historyApiFallback: true
},
devtool: production ? undefined : 'eval',
output: {
path: `${__dirname}/build`,
filename: 'bundle-[hash].js',
publicPath: process.env.CDN_URL
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.scss$/,
loader: ExtractPlugin.extract(['css-loader', 'sass-loader'])
},
{
test: /\.(woff|woff2|ttf|eot|glyph|\.svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
name: 'font/[name].[ext]'
}
}
]
},
{
test: /\.(jpg|jpeg|gif|png|tiff|svg)$/,
exclude: /\.glyph.svg/,
use: [
{
loader: 'url-loader',
options: {
limit: 6000,
name: 'image/[name].[ext]'
}
}
]
},
{
test: /\.(mp3|aac|aiff|wav|flac|m4a|mp4|ogg)$/,
exclude: /\.glyph.svg/,
use: [
{
loader: 'file-loader',
options: { name: 'audio/[name].[ext]' }
}
]
}
]
}
}