diff --git a/step1-05/final/src/App.tsx b/step1-05/final/src/TodoApp.tsx
similarity index 100%
rename from step1-05/final/src/App.tsx
rename to step1-05/final/src/TodoApp.tsx
diff --git a/step1-05/final/src/index.tsx b/step1-05/final/src/index.tsx
index 3289ec8..ccf63e2 100644
--- a/step1-05/final/src/index.tsx
+++ b/step1-05/final/src/index.tsx
@@ -1,4 +1,4 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import { TodoApp } from "./App";
-ReactDOM.render( , document.getElementById("app"));
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { TodoApp } from './TodoApp';
+ReactDOM.render( , document.getElementById('app'));
diff --git a/step1-06/README.md b/step1-06/README.md
index 696dc0c..8825198 100644
--- a/step1-06/README.md
+++ b/step1-06/README.md
@@ -1,13 +1,20 @@
-already done
-itemCount filtering
+## demo
-demo
-
-add state
+add state to AppTodo
pass to header and list
-add filter class stuff
-controlled components (header) with consolelog
-exercise
+### header
+
+- pull filter from props
+- update buttons with classNames for active
+- Add value/onChange to input
+
+### TodoList
+
+- write map to loop through items, pass key and todos
+
+## exercise
+
update footer to include todos
add item count and item(s)
+update ListItem, pull in props, use label/completed already passed in
diff --git a/step1-06/demo/index.html b/step1-06/demo/index.html
new file mode 100644
index 0000000..de2c99d
--- /dev/null
+++ b/step1-06/demo/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/step1-06/demo/src/TodoApp.tsx b/step1-06/demo/src/TodoApp.tsx
new file mode 100644
index 0000000..0f6e89a
--- /dev/null
+++ b/step1-06/demo/src/TodoApp.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { TodoFooter } from './components/TodoFooter';
+import { TodoHeader } from './components/TodoHeader';
+import { TodoList } from './components/TodoList';
+
+export class TodoApp extends React.Component {
+ render() {
+ return (
+
+
+
+
+
+ );
+ }
+}
diff --git a/step1-06/demo/src/components/TodoFooter.tsx b/step1-06/demo/src/components/TodoFooter.tsx
new file mode 100644
index 0000000..c936963
--- /dev/null
+++ b/step1-06/demo/src/components/TodoFooter.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+
+export const TodoFooter = (props: any) => {
+ const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
+ return (
+
+ 4 items left
+ Clear Completed
+
+ );
+};
diff --git a/step1-06/demo/src/components/TodoHeader.tsx b/step1-06/demo/src/components/TodoHeader.tsx
new file mode 100644
index 0000000..b5622ea
--- /dev/null
+++ b/step1-06/demo/src/components/TodoHeader.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+export class TodoHeader extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { labelInput: '' };
+ }
+
+ render() {
+ return (
+
+ );
+ }
+
+ _onChange = evt => {
+ this.setState({ labelInput: evt.target.value });
+ };
+}
diff --git a/step1-06/demo/src/components/TodoList.tsx b/step1-06/demo/src/components/TodoList.tsx
new file mode 100644
index 0000000..29ca769
--- /dev/null
+++ b/step1-06/demo/src/components/TodoList.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { TodoListItem } from './TodoListItem';
+
+export class TodoList extends React.Component {
+ render() {
+ const { filter, todos } = this.props;
+
+ const filteredTodos = Object.keys(todos).filter(id => {
+ return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
+ });
+ return (
+
+ );
+ }
+}
diff --git a/step1-06/demo/src/components/TodoListItem.tsx b/step1-06/demo/src/components/TodoListItem.tsx
new file mode 100644
index 0000000..1bff0f7
--- /dev/null
+++ b/step1-06/demo/src/components/TodoListItem.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+
+export class TodoListItem extends React.Component {
+ render() {
+ return (
+
+
+ Todo 1
+
+
+ );
+ }
+}
diff --git a/step1-06/src/index.tsx b/step1-06/demo/src/index.tsx
similarity index 100%
rename from step1-06/src/index.tsx
rename to step1-06/demo/src/index.tsx
diff --git a/step1-06/src/style.css b/step1-06/demo/src/style.css
similarity index 100%
rename from step1-06/src/style.css
rename to step1-06/demo/src/style.css
diff --git a/step1-06/exercise/index.html b/step1-06/exercise/index.html
new file mode 100644
index 0000000..de2c99d
--- /dev/null
+++ b/step1-06/exercise/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/step1-06/src/TodoApp.tsx b/step1-06/exercise/src/TodoApp.tsx
similarity index 100%
rename from step1-06/src/TodoApp.tsx
rename to step1-06/exercise/src/TodoApp.tsx
diff --git a/step1-06/exercise/src/components/TodoFooter.tsx b/step1-06/exercise/src/components/TodoFooter.tsx
new file mode 100644
index 0000000..c936963
--- /dev/null
+++ b/step1-06/exercise/src/components/TodoFooter.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+
+export const TodoFooter = (props: any) => {
+ const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
+ return (
+
+ 4 items left
+ Clear Completed
+
+ );
+};
diff --git a/step1-06/exercise/src/components/TodoHeader.tsx b/step1-06/exercise/src/components/TodoHeader.tsx
new file mode 100644
index 0000000..408a91c
--- /dev/null
+++ b/step1-06/exercise/src/components/TodoHeader.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+
+export class TodoHeader extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { labelInput: '' };
+ }
+
+ render() {
+ const { filter } = this.props;
+
+ return (
+
+ );
+ }
+ _onChange = evt => {
+ this.setState({ labelInput: evt.target.value });
+ };
+}
diff --git a/step1-06/src/components/TodoList.tsx b/step1-06/exercise/src/components/TodoList.tsx
similarity index 100%
rename from step1-06/src/components/TodoList.tsx
rename to step1-06/exercise/src/components/TodoList.tsx
diff --git a/step1-06/exercise/src/components/TodoListItem.tsx b/step1-06/exercise/src/components/TodoListItem.tsx
new file mode 100644
index 0000000..6bdcb59
--- /dev/null
+++ b/step1-06/exercise/src/components/TodoListItem.tsx
@@ -0,0 +1,13 @@
+import React from 'react';
+
+export class TodoListItem extends React.Component {
+ render() {
+ return (
+
+
+ Todo 1
+
+
+ );
+ }
+}
diff --git a/step1-07/src/index.tsx b/step1-06/exercise/src/index.tsx
similarity index 100%
rename from step1-07/src/index.tsx
rename to step1-06/exercise/src/index.tsx
diff --git a/step1-07/src/style.css b/step1-06/exercise/src/style.css
similarity index 100%
rename from step1-07/src/style.css
rename to step1-06/exercise/src/style.css
diff --git a/step1-06/final/index.html b/step1-06/final/index.html
new file mode 100644
index 0000000..de2c99d
--- /dev/null
+++ b/step1-06/final/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/step1-06/final/src/TodoApp.tsx b/step1-06/final/src/TodoApp.tsx
new file mode 100644
index 0000000..b0dbfba
--- /dev/null
+++ b/step1-06/final/src/TodoApp.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import { TodoFooter } from './components/TodoFooter';
+import { TodoHeader } from './components/TodoHeader';
+import { TodoList } from './components/TodoList';
+
+export class TodoApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ todos: {
+ '04': {
+ label: 'Todo 4',
+ completed: true
+ },
+ '03': {
+ label: 'Todo 3',
+ completed: false
+ },
+ '02': {
+ label: 'Todo 2',
+ completed: false
+ },
+ '01': {
+ label: 'Todo 1',
+ completed: false
+ }
+ },
+ filter: 'all'
+ };
+ }
+ render() {
+ const { filter, todos } = this.state;
+ return (
+
+
+
+
+
+ );
+ }
+}
diff --git a/step1-06/src/components/TodoFooter.tsx b/step1-06/final/src/components/TodoFooter.tsx
similarity index 100%
rename from step1-06/src/components/TodoFooter.tsx
rename to step1-06/final/src/components/TodoFooter.tsx
diff --git a/step1-06/src/components/TodoHeader.tsx b/step1-06/final/src/components/TodoHeader.tsx
similarity index 100%
rename from step1-06/src/components/TodoHeader.tsx
rename to step1-06/final/src/components/TodoHeader.tsx
diff --git a/step1-06/final/src/components/TodoList.tsx b/step1-06/final/src/components/TodoList.tsx
new file mode 100644
index 0000000..a92faa0
--- /dev/null
+++ b/step1-06/final/src/components/TodoList.tsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import { TodoListItem } from './TodoListItem';
+
+export class TodoList extends React.Component {
+ render() {
+ const { filter, todos } = this.props;
+
+ const filteredTodos = Object.keys(todos).filter(id => {
+ return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
+ });
+ return (
+
+ {filteredTodos.map(id => (
+
+ ))}
+
+ );
+ }
+}
diff --git a/step1-06/src/components/TodoListItem.tsx b/step1-06/final/src/components/TodoListItem.tsx
similarity index 100%
rename from step1-06/src/components/TodoListItem.tsx
rename to step1-06/final/src/components/TodoListItem.tsx
diff --git a/step1-06/final/src/index.tsx b/step1-06/final/src/index.tsx
new file mode 100644
index 0000000..ccf63e2
--- /dev/null
+++ b/step1-06/final/src/index.tsx
@@ -0,0 +1,4 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { TodoApp } from './TodoApp';
+ReactDOM.render( , document.getElementById('app'));
diff --git a/step1-06/final/src/style.css b/step1-06/final/src/style.css
new file mode 100644
index 0000000..4163a3a
--- /dev/null
+++ b/step1-06/final/src/style.css
@@ -0,0 +1,49 @@
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ width: 400px;
+ margin: 20px auto;
+}
+
+h1 {
+ text-align: center;
+}
+
+.addTodo {
+ display: flex;
+}
+
+.textfield {
+ flex-grow: 1;
+ margin-right: 10px;
+}
+
+.submit {
+ border: none;
+ padding: 5px 10px;
+}
+
+.filter {
+ margin: 10px 0 0;
+}
+
+.filter button {
+ background: transparent;
+ border: none;
+}
+
+.filter .active {
+ border-bottom: 2px solid blue;
+}
+
+.todos {
+ list-style: none;
+ padding: 0;
+}
+
+footer {
+ display: flex;
+}
+
+footer span {
+ flex-grow: 1;
+}
diff --git a/step1-06/index.html b/step1-06/index.html
index de2c99d..dadfb94 100644
--- a/step1-06/index.html
+++ b/step1-06/index.html
@@ -1,9 +1,15 @@
-
-
-
-
+
+
+
+
+
+
+
-
-
diff --git a/step1-07/README.md b/step1-07/README.md
index 6b9c40d..75c4945 100644
--- a/step1-07/README.md
+++ b/step1-07/README.md
@@ -5,14 +5,33 @@ TodoApp methods
filteredTodos in List
demo
+
+## app
+
Add Types to TodoApp
-change 'filter' value
-Types in List
-pass complete to List - show types, change complete to boolean/, filter
+change 'filter' state value to demonstrate
+
+## List (open list next to app)
+
+Demo TodoApp.types
+Add Types in List
+
+## App
+
+Back to App, add complete={this.\_complete} to Todolist - show types, change complete to 'false', filter
+
+## List
+
add complete, pass to item (prop drilling)
+
+## List Item (move List Item into App window)
+
TodoListItemProps, extend, id, complete (possible abstraction)
-Demo how you can't add random things to TodoListItem or this.props now
-add complete to List item
+add props, add complete to List item
+
+## List
+
+Demo how you can't add random things to TodoListItem or item's this.props now
exercise
@@ -20,4 +39,5 @@ Add types to footer
Add onClick to button
Add types to header
Add setFilter to filter buttons
-add 'addTodo' to onAdd function
+write onAdd function
+place onAdd to submit button
diff --git a/step1-07/demo/index.html b/step1-07/demo/index.html
new file mode 100644
index 0000000..c8c2823
--- /dev/null
+++ b/step1-07/demo/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/step1-07/demo/src/TodoApp.tsx b/step1-07/demo/src/TodoApp.tsx
new file mode 100644
index 0000000..759b8eb
--- /dev/null
+++ b/step1-07/demo/src/TodoApp.tsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { TodoFooter } from './components/TodoFooter';
+import { TodoHeader } from './components/TodoHeader';
+import { TodoList } from './components/TodoList';
+import { Todos, FilterTypes } from './TodoApp.types';
+
+let index = 0;
+
+export class TodoApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ todos: {},
+ filter: 'all'
+ };
+ }
+
+ render() {
+ const { filter, todos } = this.state;
+ return (
+
+
+
+
+
+ );
+ }
+
+ // business logic
+
+ private _addTodo = label => {
+ const { todos } = this.state;
+ const id = index++;
+
+ this.setState({
+ todos: { ...todos, [id]: { label, completed: false } }
+ });
+ };
+
+ private _complete = id => {
+ const newTodos = { ...this.state.todos };
+ newTodos[id].completed = !newTodos[id].completed;
+
+ this.setState({
+ todos: newTodos
+ });
+ };
+
+ private _clear = () => {
+ const { todos } = this.state;
+ const newTodos = {};
+
+ Object.keys(this.state.todos).forEach(id => {
+ if (!todos[id].completed) {
+ newTodos[id] = todos[id];
+ }
+ });
+
+ this.setState({
+ todos: newTodos
+ });
+ };
+
+ private _setFilter = filter => {
+ this.setState({
+ filter: filter
+ });
+ };
+}
diff --git a/step1-07/src/TodoApp.types.ts b/step1-07/demo/src/TodoApp.types.ts
similarity index 100%
rename from step1-07/src/TodoApp.types.ts
rename to step1-07/demo/src/TodoApp.types.ts
diff --git a/step1-07/demo/src/components/TodoFooter.tsx b/step1-07/demo/src/components/TodoFooter.tsx
new file mode 100644
index 0000000..0d86f55
--- /dev/null
+++ b/step1-07/demo/src/components/TodoFooter.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { Todos } from '../TodoApp.types';
+
+export const TodoFooter = (props: any) => {
+ const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
+ return (
+
+
+ {itemCount} item{itemCount > 1 ? 's' : ''} left
+
+ Clear Completed
+
+ );
+};
diff --git a/step1-07/demo/src/components/TodoHeader.tsx b/step1-07/demo/src/components/TodoHeader.tsx
new file mode 100644
index 0000000..7db8f6e
--- /dev/null
+++ b/step1-07/demo/src/components/TodoHeader.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { FilterTypes } from '../TodoApp.types';
+
+export class TodoHeader extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { labelInput: '' };
+ }
+
+ render() {
+ const { filter } = this.props;
+ return (
+
+ );
+ }
+
+ _onChange = evt => {
+ this.setState({ labelInput: evt.target.value });
+ };
+}
diff --git a/step1-07/demo/src/components/TodoList.tsx b/step1-07/demo/src/components/TodoList.tsx
new file mode 100644
index 0000000..a281227
--- /dev/null
+++ b/step1-07/demo/src/components/TodoList.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { TodoListItem } from './TodoListItem';
+import { FilterTypes, Todos } from '../TodoApp.types';
+
+export class TodoList extends React.Component {
+ render() {
+ const { filter, todos, complete } = this.props;
+
+ const filteredTodos = Object.keys(todos).filter(id => {
+ return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
+ });
+
+ return (
+
+ {filteredTodos.map(id => (
+
+ ))}
+
+ );
+ }
+}
diff --git a/step1-07/demo/src/components/TodoListItem.tsx b/step1-07/demo/src/components/TodoListItem.tsx
new file mode 100644
index 0000000..b4b49b6
--- /dev/null
+++ b/step1-07/demo/src/components/TodoListItem.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { TodoItem } from '../TodoApp.types';
+
+export class TodoListItem extends React.Component {
+ render() {
+ const { label, completed } = this.props;
+
+ return (
+
+
+ {label}
+
+
+ );
+ }
+}
diff --git a/step1-07/demo/src/index.tsx b/step1-07/demo/src/index.tsx
new file mode 100644
index 0000000..ccf63e2
--- /dev/null
+++ b/step1-07/demo/src/index.tsx
@@ -0,0 +1,4 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { TodoApp } from './TodoApp';
+ReactDOM.render( , document.getElementById('app'));
diff --git a/step1-07/demo/src/style.css b/step1-07/demo/src/style.css
new file mode 100644
index 0000000..4163a3a
--- /dev/null
+++ b/step1-07/demo/src/style.css
@@ -0,0 +1,49 @@
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ width: 400px;
+ margin: 20px auto;
+}
+
+h1 {
+ text-align: center;
+}
+
+.addTodo {
+ display: flex;
+}
+
+.textfield {
+ flex-grow: 1;
+ margin-right: 10px;
+}
+
+.submit {
+ border: none;
+ padding: 5px 10px;
+}
+
+.filter {
+ margin: 10px 0 0;
+}
+
+.filter button {
+ background: transparent;
+ border: none;
+}
+
+.filter .active {
+ border-bottom: 2px solid blue;
+}
+
+.todos {
+ list-style: none;
+ padding: 0;
+}
+
+footer {
+ display: flex;
+}
+
+footer span {
+ flex-grow: 1;
+}
diff --git a/step1-07/exercise/index.html b/step1-07/exercise/index.html
new file mode 100644
index 0000000..de2c99d
--- /dev/null
+++ b/step1-07/exercise/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/step1-07/src/TodoApp.tsx b/step1-07/exercise/src/TodoApp.tsx
similarity index 100%
rename from step1-07/src/TodoApp.tsx
rename to step1-07/exercise/src/TodoApp.tsx
diff --git a/step1-07/exercise/src/TodoApp.types.ts b/step1-07/exercise/src/TodoApp.types.ts
new file mode 100644
index 0000000..eb5437e
--- /dev/null
+++ b/step1-07/exercise/src/TodoApp.types.ts
@@ -0,0 +1,10 @@
+export type FilterTypes = 'all' | 'active' | 'completed';
+
+export interface TodoItem {
+ label: string;
+ completed: boolean;
+}
+
+export interface Todos {
+ [id: string]: TodoItem;
+}
diff --git a/step1-07/exercise/src/components/TodoFooter.tsx b/step1-07/exercise/src/components/TodoFooter.tsx
new file mode 100644
index 0000000..fdda04a
--- /dev/null
+++ b/step1-07/exercise/src/components/TodoFooter.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import { Todos } from '../TodoApp.types';
+
+export const TodoFooter = props => {
+ const itemCount = Object.keys(props.todos).filter(id => !props.todos[id].completed).length;
+ return (
+
+
+ {itemCount} item{itemCount > 1 ? 's' : ''} left
+
+ Clear Completed
+
+ );
+};
diff --git a/step1-07/exercise/src/components/TodoHeader.tsx b/step1-07/exercise/src/components/TodoHeader.tsx
new file mode 100644
index 0000000..7db8f6e
--- /dev/null
+++ b/step1-07/exercise/src/components/TodoHeader.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { FilterTypes } from '../TodoApp.types';
+
+export class TodoHeader extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { labelInput: '' };
+ }
+
+ render() {
+ const { filter } = this.props;
+ return (
+
+ );
+ }
+
+ _onChange = evt => {
+ this.setState({ labelInput: evt.target.value });
+ };
+}
diff --git a/step1-07/src/components/TodoList.tsx b/step1-07/exercise/src/components/TodoList.tsx
similarity index 100%
rename from step1-07/src/components/TodoList.tsx
rename to step1-07/exercise/src/components/TodoList.tsx
diff --git a/step1-07/src/components/TodoListItem.tsx b/step1-07/exercise/src/components/TodoListItem.tsx
similarity index 100%
rename from step1-07/src/components/TodoListItem.tsx
rename to step1-07/exercise/src/components/TodoListItem.tsx
diff --git a/step1-07/exercise/src/index.tsx b/step1-07/exercise/src/index.tsx
new file mode 100644
index 0000000..ccf63e2
--- /dev/null
+++ b/step1-07/exercise/src/index.tsx
@@ -0,0 +1,4 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { TodoApp } from './TodoApp';
+ReactDOM.render( , document.getElementById('app'));
diff --git a/step1-07/exercise/src/style.css b/step1-07/exercise/src/style.css
new file mode 100644
index 0000000..4163a3a
--- /dev/null
+++ b/step1-07/exercise/src/style.css
@@ -0,0 +1,49 @@
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ width: 400px;
+ margin: 20px auto;
+}
+
+h1 {
+ text-align: center;
+}
+
+.addTodo {
+ display: flex;
+}
+
+.textfield {
+ flex-grow: 1;
+ margin-right: 10px;
+}
+
+.submit {
+ border: none;
+ padding: 5px 10px;
+}
+
+.filter {
+ margin: 10px 0 0;
+}
+
+.filter button {
+ background: transparent;
+ border: none;
+}
+
+.filter .active {
+ border-bottom: 2px solid blue;
+}
+
+.todos {
+ list-style: none;
+ padding: 0;
+}
+
+footer {
+ display: flex;
+}
+
+footer span {
+ flex-grow: 1;
+}
diff --git a/step1-07/final/index.html b/step1-07/final/index.html
new file mode 100644
index 0000000..de2c99d
--- /dev/null
+++ b/step1-07/final/index.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/step1-07/final/src/TodoApp.tsx b/step1-07/final/src/TodoApp.tsx
new file mode 100644
index 0000000..b24da27
--- /dev/null
+++ b/step1-07/final/src/TodoApp.tsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { TodoFooter } from './components/TodoFooter';
+import { TodoHeader } from './components/TodoHeader';
+import { TodoList } from './components/TodoList';
+import { Todos, FilterTypes } from './TodoApp.types';
+
+let index = 0;
+
+export class TodoApp extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ todos: {},
+ filter: 'all'
+ };
+ }
+
+ render() {
+ const { filter, todos } = this.state;
+ return (
+
+
+
+
+
+ );
+ }
+
+ // business logic
+
+ private _addTodo = label => {
+ const { todos } = this.state;
+ const id = index++;
+
+ this.setState({
+ todos: { ...todos, [id]: { label, completed: false } }
+ });
+ };
+
+ private _complete = id => {
+ const newTodos = { ...this.state.todos };
+ newTodos[id].completed = !newTodos[id].completed;
+
+ this.setState({
+ todos: newTodos
+ });
+ };
+
+ private _clear = () => {
+ const { todos } = this.state;
+ const newTodos = {};
+
+ Object.keys(this.state.todos).forEach(id => {
+ if (!todos[id].completed) {
+ newTodos[id] = todos[id];
+ }
+ });
+
+ this.setState({
+ todos: newTodos
+ });
+ };
+
+ private _setFilter = filter => {
+ this.setState({
+ filter: filter
+ });
+ };
+}
diff --git a/step1-07/final/src/TodoApp.types.ts b/step1-07/final/src/TodoApp.types.ts
new file mode 100644
index 0000000..eb5437e
--- /dev/null
+++ b/step1-07/final/src/TodoApp.types.ts
@@ -0,0 +1,10 @@
+export type FilterTypes = 'all' | 'active' | 'completed';
+
+export interface TodoItem {
+ label: string;
+ completed: boolean;
+}
+
+export interface Todos {
+ [id: string]: TodoItem;
+}
diff --git a/step1-07/src/components/TodoFooter.tsx b/step1-07/final/src/components/TodoFooter.tsx
similarity index 100%
rename from step1-07/src/components/TodoFooter.tsx
rename to step1-07/final/src/components/TodoFooter.tsx
diff --git a/step1-07/src/components/TodoHeader.tsx b/step1-07/final/src/components/TodoHeader.tsx
similarity index 100%
rename from step1-07/src/components/TodoHeader.tsx
rename to step1-07/final/src/components/TodoHeader.tsx
diff --git a/step1-07/final/src/components/TodoList.tsx b/step1-07/final/src/components/TodoList.tsx
new file mode 100644
index 0000000..e6419e9
--- /dev/null
+++ b/step1-07/final/src/components/TodoList.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { TodoListItem } from './TodoListItem';
+import { FilterTypes, Todos } from '../TodoApp.types';
+
+interface TodoListProps {
+ complete: (id: string) => void;
+ todos: Todos;
+ filter: FilterTypes;
+}
+
+export class TodoList extends React.Component {
+ render() {
+ const { filter, todos, complete } = this.props;
+
+ const filteredTodos = Object.keys(todos).filter(id => {
+ return filter === 'all' || (filter === 'completed' && todos[id].completed) || (filter === 'active' && !todos[id].completed);
+ });
+
+ return (
+
+ {filteredTodos.map(id => (
+
+ ))}
+
+ );
+ }
+}
diff --git a/step1-07/final/src/components/TodoListItem.tsx b/step1-07/final/src/components/TodoListItem.tsx
new file mode 100644
index 0000000..3f359ce
--- /dev/null
+++ b/step1-07/final/src/components/TodoListItem.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { TodoItem } from '../TodoApp.types';
+
+interface TodoListItemProps extends TodoItem {
+ id: string;
+ complete: (id: string) => void;
+}
+
+export class TodoListItem extends React.Component {
+ render() {
+ const { label, completed, complete, id } = this.props;
+
+ return (
+
+
+ complete(id)} /> {label}
+
+
+ );
+ }
+}
diff --git a/step1-07/final/src/index.tsx b/step1-07/final/src/index.tsx
new file mode 100644
index 0000000..ccf63e2
--- /dev/null
+++ b/step1-07/final/src/index.tsx
@@ -0,0 +1,4 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { TodoApp } from './TodoApp';
+ReactDOM.render( , document.getElementById('app'));
diff --git a/step1-07/final/src/style.css b/step1-07/final/src/style.css
new file mode 100644
index 0000000..4163a3a
--- /dev/null
+++ b/step1-07/final/src/style.css
@@ -0,0 +1,49 @@
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ width: 400px;
+ margin: 20px auto;
+}
+
+h1 {
+ text-align: center;
+}
+
+.addTodo {
+ display: flex;
+}
+
+.textfield {
+ flex-grow: 1;
+ margin-right: 10px;
+}
+
+.submit {
+ border: none;
+ padding: 5px 10px;
+}
+
+.filter {
+ margin: 10px 0 0;
+}
+
+.filter button {
+ background: transparent;
+ border: none;
+}
+
+.filter .active {
+ border-bottom: 2px solid blue;
+}
+
+.todos {
+ list-style: none;
+ padding: 0;
+}
+
+footer {
+ display: flex;
+}
+
+footer span {
+ flex-grow: 1;
+}
diff --git a/step1-07/index.html b/step1-07/index.html
index de2c99d..dadfb94 100644
--- a/step1-07/index.html
+++ b/step1-07/index.html
@@ -1,9 +1,15 @@
-
-
-
-
+
+
+
+
+
+
+
-
-