Skip to content

Instantly share code, notes, and snippets.

@ocommaj
Last active April 28, 2021 19:38
Show Gist options
  • Save ocommaj/ed165b5ea072948b92642363d64ee6e4 to your computer and use it in GitHub Desktop.
Save ocommaj/ed165b5ea072948b92642363d64ee6e4 to your computer and use it in GitHub Desktop.
Hiring Process Challenge for Ender Property Management - React Todo List
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;
}
}*/
$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