Last active
April 28, 2021 19:38
-
-
Save ocommaj/ed165b5ea072948b92642363d64ee6e4 to your computer and use it in GitHub Desktop.
Hiring Process Challenge for Ender Property Management - React Todo List
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState } from "https://cdn.skypack.dev/react@v17.0.1"; | |
import ReactDOM from "https://cdn.skypack.dev/react-dom@v17.0.1"; | |
import { v4 } from "https://cdn.skypack.dev/uuid@v8.3.1"; | |
// use "generateId()" function to create a unique id | |
const generateId = v4; | |
function TodoForm(props) { | |
const { createTodo } = props; | |
const [inputValue, setInputValue] = useState(); | |
return ( | |
<form className="create-todo-form"> | |
<h2>Create a New Todo</h2> | |
<input | |
name="todo-name" | |
id="todoName" | |
type="text" | |
placeholder="Enter todo description" | |
autoComplete="off" | |
onChange={handleChange} | |
value={inputValue} | |
autoFocus | |
/> | |
<button className="submit-btn" type="submit" onClick={handleSubmit}> | |
Create Todo | |
</button> | |
</form> | |
); | |
function handleChange(value) { | |
setInputValue(event.target.value); | |
} | |
function handleSubmit(event) { | |
console.log(inputValue); | |
!!inputValue && createTodo(inputValue); | |
event.preventDefault(); | |
setInputValue(""); | |
} | |
} | |
function TodoListItem(props) { | |
const { | |
eventHandler, | |
item: { id, name, isCompleted } | |
} = props; | |
const doAction = (action) => eventHandler(id, action); | |
return ( | |
<div className="todo-list__item" data-completion-status={isCompleted}> | |
<span onClick={() => doAction("deleteTodo")}>{name}</span> | |
<button onClick={() => doAction("toggleTodo")}>X</button> | |
</div> | |
); | |
} | |
function TodoList(props) { | |
const { title, todos, ...itemProps } = props; | |
return ( | |
<div className="todo-list"> | |
<h2>{title}:</h2> | |
{todos.map((td) => ( | |
<TodoListItem {...{ item: { ...td }, ...itemProps }} /> | |
))} | |
</div> | |
); | |
} | |
class App extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
todos: [ | |
{ | |
id: "asdfv", | |
name: "Practice Guitar", | |
isCompleted: false | |
}, | |
{ | |
id: "gsdfw", | |
name: "Take out Recycling", | |
isCompleted: false | |
}, | |
{ | |
id: "23gsd", | |
name: "Exercise", | |
isCompleted: false | |
}, | |
{ | |
id: "6ghsd", | |
name: "Buy Groceries", | |
isCompleted: false | |
}, | |
{ | |
id: "dfg83", | |
name: "Send Birthday Cards", | |
isCompleted: true | |
}, | |
{ | |
id: "y84cd", | |
name: "Sweep Porch", | |
isCompleted: true | |
} | |
] | |
}; | |
} | |
createTodo(name) { | |
const { todos } = this.state; | |
this.setState({ | |
todos: [...todos, { name, isCompleted: false, id: generateId() }] | |
}); | |
} | |
itemEvents(id, action, sublist) { | |
const [...todos] = this.state[sublist]; | |
try { | |
const toModify = todos.find((todo) => todo.id === id); | |
const todo = todos.splice(todos.indexOf(toModify), 1)[0]; | |
this[action](toModify, todos); | |
} catch (error) { | |
console.error(error); | |
} | |
} | |
itemEvents(id, action) { | |
const { todos } = this.state; | |
const needsUpdate = todos.find((todo) => todo.id === id); | |
const toModify = todos.splice(todos.indexOf(needsUpdate), 1)[0]; | |
try { | |
this[action](toModify, todos); | |
} catch (error) { | |
console.error(error); | |
} | |
} | |
deleteTodo(todo, todos) { | |
console.log(`deleting todo ${todo.name} (id: ${todo.id}`); | |
this.setState({ todos: todos }); | |
} | |
toggleTodo(todo, todos) { | |
todo.isCompleted = !todo.isCompleted; | |
this.setState({ todos: [...todos, todo] }); | |
} | |
render() { | |
return ( | |
<div className="content"> | |
<h1 className="title">Todo App</h1> | |
<TodoForm createTodo={(value) => this.createTodo(value)} /> | |
<div className="lists-wrapper"> | |
<TodoList | |
title="Todo" | |
todos={[...this.state.todos.filter((todo) => !todo.isCompleted)]} | |
eventHandler={(id, action) => this.itemEvents(id, action)} | |
/> | |
<TodoList | |
title="Completed" | |
todos={[...this.state.todos.filter((todo) => !!todo.isCompleted)]} | |
eventHandler={(id, action) => this.itemEvents(id, action)} | |
/> | |
</div> | |
</div> | |
); | |
} | |
} | |
ReactDOM.render(<App />, document.getElementById("root")); | |
/* | |
componentDidMount() { | |
this.sortTodos() | |
} | |
sortTodos() { | |
const { tbd, done } = todosReducer(this.state.todos); | |
this.setState({ stillTodo: [...tbd] }); | |
this.setState({ completed: [...done] }); | |
} | |
function todosReducer(sourceList) { | |
return sourceList.reduce(sorter, { tbd: [], done: [] }); | |
function sorter(acc, todo) { | |
const target = !!todo.isCompleted ? acc.done : acc.tbd; | |
target.push(todo); | |
return acc; | |
} | |
}*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$purple: #512ed7; | |
$lavender: #8377b0; | |
$white: #fff; | |
$sage: #77b0a4; | |
$red: #ff7181; | |
$gray: #eeeff4; | |
@mixin transition { | |
transition: background-color 0.4s ease, color 0.4s ease; | |
} | |
:root { | |
--purple: $purple; | |
--lavender: $lavender; | |
--white: $white; | |
--sage: $sage; | |
--red: $red; | |
--gray: $gray; | |
font-family: sans-serif; | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
background-color: $gray; | |
} | |
h1, | |
h2 { | |
text-align: center; | |
user-select: none; | |
-moz-user-select: none; | |
-ms-user-select: none; | |
-webkit-user-select: none; | |
} | |
h1 { | |
font-size: 3rem; | |
font-weight: bolder; | |
} | |
h2 { | |
font-size: 2rem; | |
font-weight: bold; | |
margin: 1rem; | |
} | |
.create-todo-form { | |
margin: auto; | |
width: 25vw; | |
min-width: 360px; | |
padding: 1rem; | |
padding-top: 0.5rem; | |
border-radius: 0.25rem; | |
background-color: $white; | |
box-shadow: 1px 2px 0.25rem 0.25rem rgba($lavender, 0.125); | |
input, | |
button { | |
display: block; | |
margin: auto; | |
font-family: inherit; | |
font-size: 125%; | |
line-height: 1.2; | |
padding: 0.5rem; | |
padding-bottom: 0.25rem; | |
border-radius: 0.25rem; | |
border: none; | |
} | |
.submit-btn { | |
background-color: $purple; | |
color: $gray; | |
cursor: pointer; | |
@include transition; | |
} | |
.submit-btn:hover, | |
.submit-btn:focus { | |
background-color: darken($purple, 15%); | |
color: $white; | |
} | |
button:focus, | |
input:focus { | |
outline: solid 1px rgba(#000, 0.6); | |
-moz-outline-radius: 0.25rem; | |
} | |
:last-child { | |
margin-top: 1rem; | |
} | |
} | |
.lists-wrapper { | |
display: flex; | |
justify-content: space-around; | |
align-items: stretch; | |
margin: 2rem 0; | |
} | |
.todo-list { | |
background-color: $white; | |
height: inherit; | |
flex-basis: 25vw; | |
min-width: 360px; | |
display: flex; | |
flex-direction: column; | |
align-items: stretch; | |
border-radius: 0.25rem; | |
padding: 2rem; | |
padding-top: 0.5rem; | |
box-shadow: 2px 2px 4px rgba($lavender, 0.125); | |
} | |
.todo-list:nth-child(1) { | |
margin-left: auto; | |
margin-right: 3vw; | |
} | |
.todo-list:nth-child(2) { | |
margin-right: auto; | |
margin-left: 3vw; | |
} | |
.todo-list__item { | |
background-color: $lavender; | |
margin-bottom: 0.5rem; | |
border-radius: 0.25rem; | |
color: $gray; | |
padding: 0.75rem 0.5rem; | |
font-size: 1.25rem; | |
box-shadow: 2px 2px 4px 2px rgba($lavender, 0.2); | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
animation: 0.4s ease-in 0s 1 item-appear; | |
} | |
.todo-list__item { | |
button { | |
appearance: none; | |
border: none; | |
background-color: $red; | |
color: $white; | |
font-size: 1.125rem; | |
padding: 0.125rem 0.5rem; | |
border-radius: 0.25rem; | |
width: auto; | |
cursor: pointer; | |
box-shadow: 1px 1px 2px 1px rgba($gray, 0.1); | |
@include transition; | |
} | |
button:hover { | |
background-color: darken($red, 25%); | |
} | |
span { | |
cursor: not-allowed; | |
transition: color 0.4s ease; | |
} | |
span:hover { | |
color: $red; | |
} | |
} | |
.todo-list__item[data-completion-status="true"] { | |
background-color: $sage; | |
} | |
@keyframes item-appear { | |
0% { | |
transform: scaleY(0%); | |
} | |
100% { | |
transform: scaleY(100%); | |
} | |
} | |
@media (max-width: 600px) { | |
.lists-wrapper { | |
flex-direction: column; | |
} | |
.todo-list { | |
margin-left: auto; | |
margin-right: auto; | |
padding: 1rem; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment