├── .babelrc
├── .gitignore
├── .jshintrc
├── .eslintrc
├── README.md
├── scripts
├── AppFlux.js
├── index.js
├── TaskList.js
├── App.js
├── Task.js
├── TaskForm.js
├── TaskStore.js
└── TaskActions.js
├── index.html
├── server.js
├── package.json
├── webpack.config.js
└── LICENSE
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "stage": 0
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "newcap": false
6 | }
7 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "node": true
5 | },
6 | "parser": "babel-eslint",
7 | "rules": {
8 | "quotes": [2, "single"],
9 | "strict": [2, "never"]
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Atelier Djangocong 2015
2 | =======================
3 |
4 | ### Setup
5 |
6 | ```
7 | $ git clone https://github.com/n1k0/atelier-djangocong-2015.git
8 | $ cd atelier-djangocong-2015
9 | $ npm install
10 | $ npm start
11 | $ open http://localhost:3000
12 | ```
13 |
--------------------------------------------------------------------------------
/scripts/AppFlux.js:
--------------------------------------------------------------------------------
1 | import { Flux } from "flummox";
2 | import TaskActions from "./TaskActions";
3 | import TaskStore from "./TaskStore";
4 |
5 | export default class AppFlux extends Flux {
6 | constructor() {
7 | super();
8 |
9 | this.createActions("tasks", TaskActions);
10 | this.createStore("tasks", TaskStore, this);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/scripts/index.js:
--------------------------------------------------------------------------------
1 | import "babel-core/polyfill";
2 | import React from "react";
3 | import FluxComponent from "flummox/component";
4 | import App from "./App";
5 | import AppFlux from "./AppFlux";
6 |
7 | const flux = new AppFlux();
8 |
9 | React.render(
10 |
11 |
12 | ,
13 | document.getElementById("root"));
14 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sample App
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/scripts/TaskList.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Task from './Task';
3 |
4 | export default class TaskList extends React.Component {
5 | render() {
6 | return (
7 | {
8 | this.props.tasks.map((task, key) => {
9 | return ;
10 | })
11 | }
12 | );
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var WebpackDevServer = require('webpack-dev-server');
3 | var config = require('./webpack.config');
4 |
5 | new WebpackDevServer(webpack(config), {
6 | publicPath: config.output.publicPath,
7 | hot: true,
8 | historyApiFallback: true
9 | }).listen(3000, 'localhost', function (err, result) {
10 | if (err) {
11 | console.log(err);
12 | }
13 |
14 | console.log('Listening at localhost:3000');
15 | });
16 |
--------------------------------------------------------------------------------
/scripts/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TaskForm from "./TaskForm";
3 | import TaskList from "./TaskList";
4 | import FluxComponent from "flummox/component";
5 |
6 | export default class App extends React.Component {
7 | componentDidMount() {
8 | this.props.flux.getActions("tasks").categories();
9 | this.props.flux.getActions("tasks").list();
10 | }
11 |
12 | render() {
13 | return (
14 |
15 |
Tasks
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "atelier-djangocong-2015",
3 | "version": "1.0.0",
4 | "description": "Atelier Djangocong 2015",
5 | "scripts": {
6 | "start": "node server.js"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/n1k0/atelier-djangocong-2015.git"
11 | },
12 | "author": "Nicolas Perriault ",
13 | "license": "MIT",
14 | "devDependencies": {
15 | "babel-core": "^5.1.11",
16 | "babel-loader": "^5.0.0",
17 | "react-hot-loader": "^1.2.2",
18 | "webpack": "^1.7.2",
19 | "webpack-dev-server": "^1.7.0"
20 | },
21 | "dependencies": {
22 | "flummox": "^4.0.0-alpha2",
23 | "react": "^0.13.*"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: [
7 | 'webpack-dev-server/client?http://localhost:3000',
8 | 'webpack/hot/only-dev-server',
9 | './scripts/index'
10 | ],
11 | output: {
12 | path: path.join(__dirname, 'build'),
13 | filename: 'bundle.js',
14 | publicPath: '/scripts/'
15 | },
16 | plugins: [
17 | new webpack.HotModuleReplacementPlugin(),
18 | new webpack.NoErrorsPlugin()
19 | ],
20 | resolve: {
21 | extensions: ['', '.js', '.jsx']
22 | },
23 | module: {
24 | loaders: [{
25 | test: /\.jsx?$/,
26 | loaders: ['react-hot', 'babel'],
27 | include: path.join(__dirname, 'scripts')
28 | }]
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/scripts/Task.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import TaskActions from "./TaskActions";
3 |
4 | export default class Task extends React.Component {
5 | onCheckboxClick() {
6 | this.props.flux.getActions("tasks").done(this.props.task);
7 | }
8 |
9 | onDeleteClick(event) {
10 | event.preventDefault();
11 | this.props.flux.getActions("tasks").delete(this.props.task.id);
12 | }
13 |
14 | render() {
15 | return (
16 |
17 |
22 | {" "}
23 | [x]
24 |
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Dan Abramov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/scripts/TaskForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class TaskForm extends React.Component {
4 | onFormSubmit(event) {
5 | event.preventDefault();
6 | this.props.flux.getActions("tasks").create({
7 | owner: "niko",
8 | name: event.target.name.value,
9 | categories: [event.target.category.value]
10 | });
11 | }
12 |
13 | render() {
14 | return (
15 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/scripts/TaskStore.js:
--------------------------------------------------------------------------------
1 | import { Store } from 'flummox';
2 |
3 | export default class TaskStore extends Store {
4 | constructor(flux) {
5 | super();
6 |
7 | const taskActionIds = flux.getActionIds('tasks');
8 | this.register(taskActionIds.categories, this.handleGetCategories);
9 | this.register(taskActionIds.create, this.handleCreateTask);
10 | this.register(taskActionIds.delete, this.handleDeleteTask);
11 | this.register(taskActionIds.list, this.handleListTasks);
12 | this.register(taskActionIds.done, this.handleDoneTask);
13 |
14 | this.state = {
15 | categories: [],
16 | tasks: [],
17 | };
18 | }
19 |
20 | handleGetCategories(categories) {
21 | this.setState({categories: categories});
22 | }
23 |
24 | handleCreateTask(task) {
25 | this.setState({tasks: this.state.tasks.concat(task)});
26 | }
27 |
28 | handleDeleteTask(id) {
29 | this.setState({tasks: this.state.tasks.filter(task => task.id !== id)});
30 | }
31 |
32 | handleListTasks(tasks) {
33 | this.setState({tasks: tasks});
34 | }
35 |
36 | handleDoneTask(task) {
37 | this.setState({tasks: this.state.tasks.map(oTask => {
38 | if (oTask.id === task.id)
39 | oTask.done = task.done;
40 | return oTask;
41 | })});
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/scripts/TaskActions.js:
--------------------------------------------------------------------------------
1 | import { Actions } from 'flummox';
2 |
3 | var url = "http://localhost:8000";
4 |
5 | export default class TaskActions extends Actions {
6 | get defaultHeaders() {
7 | return {
8 | "Accept": "application/json",
9 | "Content-Type": "application/json",
10 | };
11 | }
12 |
13 | _wrap(promise) {
14 | return promise.then(res => {
15 | if (res.status !== 204)
16 | return res.json();
17 | return null;
18 | }).catch(err => {
19 | alert('failed ' + err);
20 | throw err;
21 | });
22 | }
23 |
24 | categories() {
25 | return this._wrap(fetch(`${url}/category/`, {
26 | headers: this.defaultHeaders
27 | }));
28 | }
29 |
30 | list() {
31 | return this._wrap(fetch(`${url}/task/`, {
32 | headers: this.defaultHeaders
33 | }));
34 | }
35 |
36 | create(task) {
37 | return this._wrap(fetch(`${url}/task/`, {
38 | method: "POST",
39 | headers: this.defaultHeaders,
40 | body: JSON.stringify(task)
41 | }));
42 | }
43 |
44 | delete(id) {
45 | return this._wrap(fetch(`${url}/task/${id}/`, {
46 | method: "DELETE",
47 | headers: this.defaultHeaders
48 | })).then(() => id);
49 | }
50 |
51 | done(task) {
52 | return this._wrap(fetch(`${url}/task/${task.id}/`, {
53 | method: "PUT",
54 | headers: this.defaultHeaders,
55 | body: JSON.stringify(Object.assign({}, task, {done: !task.done}))
56 | }));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------