The assignment was the following:
- Users can add at a new Item/Card with Title and Description. Everytime an item is created, it is automatically placed in the TODO column
- At any point in time items can be moved from TODO column to In Progress but they cannot be moved back.
- At any point in time items can be moved from In Progress to Done.
- Items description and title can be edited only while they are in TODO or In Progress state.
- Items that have been DONE can no longer be moved or edited.
- At most 30 items can be created
In the following I present you my solution. It works as it should. However, I am still thankful for any head-up you can give me. Be it optically, functional, and for effectiveness.
import logo from './logo.svg';
import './App.css';
import Wrapper from "./components/Wrapper";
import React from 'react';
function App() {
return (
<div className="App">
<Wrapper />
</div>
);
}
export default App;
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
import "./Wrapper.css";
import React, { useState } from "react";
import Column from "./Column";
function Wrapper(props) {
const [tasks1, setTasks1] = useState([]);
const [tasks2, setTasks2] = useState([]);
const [tasks3, setTasks3] = useState([]);
const deleteTicketbyId = (inputID, state) => {
switch (state) {
case "1":
var newTasks = tasks1.filter(entry => entry.id !== inputID);
setTasks1(newTasks);
break;
case "2":
var newTasks = tasks2.filter(entry => entry.id !== inputID);
setTasks2(newTasks);
break;
case "3":
var newTasks = tasks3.filter(entry => entry.id !== inputID);
setTasks3(newTasks);
break;
}
}
function getTicketByID(inputID, state) {
switch (state) {
case "1":
return tasks1.find(x => x.id ===inputID);
break;
case "2":
return tasks2.find(x => x.id ===inputID);
break;
case "3":
return tasks3.find(x => x.id ===inputID);
break;
}
}
function advanceTicket(inputID, state, i_Title, i_Content) {
const currElement = getTicketByID(inputID, state);
const newState = String(Number(state)+1);
currElement.title = i_Title;
currElement.content = i_Content;
addTicket(currElement, newState);
deleteTicketbyId(inputID, state);
}
function addTicket(newElement, state) {
switch (state) {
case "1":
setTasks1([...tasks1, newElement]);
break;
case "2":
setTasks2([...tasks2, newElement]);
break;
case "3":
setTasks3([...tasks3, newElement]);
break;
}
}
const addEmptyTicket = () => {
console.log(tasks1.length + " " + tasks2.length + " " + tasks3.length);
if((tasks1.length + tasks2.length + tasks3.length) < 30){
const newTask = {
id: Date.now(),
title: "Enter title here",
content: "enter description here",
};
setTasks1([...tasks1, newTask]);
}else{
alert("A new ticket can not be created. The limit is set at 30!");
}
};
return(<div className="wrapper">
<h1>Welcome to Andy's To-Do App</h1>
<hr />
<div className="wrapperChildren">
<Column title="To-Do" state="1" isFirst="true" newEmpty={ addEmptyTicket } deleteFrom={deleteTicketbyId} tasks={ tasks1 } advancveTo={ advanceTicket } />
<Column title="In Progress" state="2" tasks={ tasks2 } deleteFrom={deleteTicketbyId} advancveTo={ advanceTicket } />
<Column title="Done" state="3" tasks={ tasks3 } deleteFrom={deleteTicketbyId} advancveTo={ advanceTicket } />
</div>
</div>);
}
export default Wrapper;
.wrapper {
height: 100%;
background-color: #35465c;
border: 10px inset silver;
}
.wrapper h1 {
font-weight: bold;
color: #bfbfbf;
font-style: favorit;
}
.wrapperChildren {
display: flex;
height: 100vh;
}
hr {
margin: 5px;
}
import "./Column.css";
import React, { useState } from "react";
import Ticket from "./Ticket";
function Column(props){
return (
<div className="column">
<p>{props.title}</p>
<hr />
<div className="taskList">
{
props.tasks.map((task) => (
<Ticket
key={task.id}
id={task.id}
title={task.title}
content={task.content}
deleteFrom={props.deleteFrom}
advancveTo={ props.advancveTo }
state={ props.state }
/>
))
}
</div>
{ props.isFirst && <div className="buttonSection"><button className="columnButton"
onClick={props.newEmpty}>
+ New Task
</button></div>
}
</div>);
}
export default Column;
.column {
background-color: #f3f5f7;
height: auto;
width: 300px;
margin: 10px;
border-radius: 5px;
border: 2px solid gray;
position:relative;
}
.column p {
font-size: 18px;
font-weight: bold;
font-style: favorit;
color: #304261;
}
.column .taskList {
overflow-y: 'scroll';
height: 400px;
}
.buttonSection {
position: absolute;
bottom: 0;
justify-content: center;
width: calc(100% - 10px);
padding: 5px;
background-color: #f3f5f7;
}
.column .columnButton {
width: 240px;
background: rgba(0,0,0,0.0);
border: 1px solid grey;
border-radius: 3px;
font-size: 16px;
width: 240px;
}
import React, { useState, useEffect } from "react";
import "./Ticket.css";
function Ticket(props){
const [title, setTitle] = useState(props.title);
const [content, setContent] = useState(props.content);
const updateTitle = (e) => {
const newTitle = e.target.value;
setTitle(newTitle);
}
const updateContent = (e) => {
const newContent = e.target.value;
setContent(newContent);
}
const deleteTicket = () => {
props.deleteFrom(props.id, props.state);
}
const advanceTicket = () => {
props.advancveTo(props.id, props.state, title, content);
}
return (
<div className="ticket">
<input type="text" id="ticketInput" defaultValue={props.title} name="title" onChange={ updateTitle } readOnly={props.state==="3"} />
<hr />
<textarea id="ticketArea" name="w3review" defaultValue={props.content} onChange={ updateContent } readOnly={props.state==="3"} />
<div className="buttonPanel">
<button className="ticketButton" onClick={ deleteTicket }>Delete</button>
{props.state !== "3" && <button className="ticketButton" onClick={ advanceTicket }> Advance </button>}
</div>
</div>
);
}
export default Ticket;
.ticket {
background-color: #2b2d2f;
color: #000000;
margin: 0 5px 10px;
border-radius: 5px;
padding: 5px;
}
input {
border: none;
background: rgba(0,0,0,0.0);
color: #ffffff;
width: auto;
}
textarea {
border: none;
background: rgba(0,0,0,0.0);
color: #ffffff;
width: 100%;
margin: 0 0 5px;
display: inline-block;
}
.buttonPanel{
display: flex;
justify-content: center;
}
.ticketButton {
background: rgba(0,0,0,0.0);
color: red;
border: 1px solid red;
display: inline-block;
position: relative;
margin: 5px;
}