added improvements, validation

This commit is contained in:
Tomasz Kasprowicz 2020-12-25 19:13:54 +01:00
parent 76667e8a19
commit 4a0d62d419
7 changed files with 259 additions and 38 deletions

View File

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import Header from "./components/Header";
import styled from "styled-components";
import SideDrawer from "./components/SideDrawer";
@ -6,13 +6,14 @@ import { Switch, Route } from "react-router-dom";
import Login from "./components/Login";
import CreateTask from "./components/CreateTask";
import TodoList from "./components/TodoList";
import { connect } from "react-redux";
const StyledApp = styled.div`
height: 100vh;
background-color: #eee;
`;
function App() {
const App = (props: any)=> {
const [sideDrawer, setSiderDrawer] = useState<boolean>(false);
const toggleSideDrawerHandler = () => {
@ -23,6 +24,10 @@ function App() {
setSiderDrawer(false);
}
useEffect(()=>{
props.error !== null && alert(props.error);
}, [props.error])
return (
<StyledApp>
<Header sideDrawer={toggleSideDrawerHandler} isOpen={sideDrawer} />
@ -60,4 +65,19 @@ function App() {
);
}
export default App;
interface RootState {
isAuth: boolean;
todos: [];
userId: boolean;
idToken: string;
isLoading: boolean;
error:string;
}
const mapStateToProps = (state: RootState) => {
return {
error: state.error,
};
};
export default connect(mapStateToProps, null)(App);

View File

@ -4,7 +4,7 @@ import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import { connect } from "react-redux";
import * as actions from "../store/actions/actions";
import { withRouter } from "react-router-dom";
import dayjs from'dayjs';
import dayjs from "dayjs";
const StyledTasks = styled.div`
width: 100%;
@ -33,25 +33,36 @@ const CreateTask = (props: any) => {
const [isImportant, setIsImportant] = useState<boolean>(false);
const [date, setDate] = useState<string>("");
const currDate = dayjs(new Date()).format('YYYY-MM-DD');
const currDate = dayjs(new Date()).format("YYYY-MM-DD");
const addTaskHandler = () => {
const isValid = formValidator();
if (isValid) {
props.isAuth
? props.addToDo(
taskName,
taskDesc,
taskType,
isImportant,
date,
props.idToken,
props.userId
)
: props.history.push("/login");
if (props.isAuth) {
props.addToDo(
taskName,
taskDesc,
taskType,
isImportant,
date,
props.idToken,
props.userId
);
clearFormHandler();
} else {
props.history.push("/login");
}
}
};
const clearFormHandler = () => {
setTaskName("");
setTaskDesc("");
setTaskType("bussiness");
setIsImportant(false);
setDate("");
};
const radioButtonsHandler = (type: string) => {
setTaskType(type);
};
@ -61,8 +72,11 @@ const CreateTask = (props: any) => {
};
const formValidator = () => {
if (taskName.length > 1 && taskDesc.length > 1 && date.length > 1)
if (taskName.length > 1 && taskDesc.length > 1 && date.length > 1) {
return true;
} else {
alert("Fill in inputs!!!");
}
};
return (
@ -160,7 +174,7 @@ const CreateTask = (props: any) => {
interface RootState {
isAuth: boolean;
idToken: string;
userId: string
userId: string;
}
const mapStateToProps = (state: RootState) => {
@ -180,8 +194,11 @@ const mapDispatchToProps = (dispatch: any) => {
important: boolean,
date: string,
token: string,
userId: string,
) => dispatch(actions.addToDo(name, desc, type, important, date, token, userId)),
userId: string
) =>
dispatch(
actions.addToDo(name, desc, type, important, date, token, userId)
),
};
};

View File

@ -3,8 +3,8 @@ import * as actions from "../store/actions/actions";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import { connect } from "react-redux";
import styled from "styled-components";
import sadFace from "../assets/images/sadFace.png";
import { withRouter } from "react-router-dom";
import dayjs from "dayjs";
interface TODOtype {
date: string;
@ -14,6 +14,7 @@ interface TODOtype {
taskType: string;
hash: string;
isFinished: boolean;
isCanceled: boolean;
}
const TodosStyled = styled.div`
@ -21,7 +22,7 @@ const TodosStyled = styled.div`
height: 92%;
overflow: scroll;
justify-content: center;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
.description {
height: 100px;
overflow: scroll;
@ -34,23 +35,20 @@ const TodoList = (props: any) => {
}, []);
const taskType = props.match.path.substring(1);
console.log(taskType);
const finishTaskHandler = (hash: string, idToken: string) => {
console.log("update");
const finishTaskHandler = (hash: string, idToken: string) =>
props.finishToDo(hash, idToken, props.todos);
};
const deleteTaskHandler = (hash: string, idToken: string) => {
console.log("delete");
const deleteTaskHandler = (hash: string, idToken: string) =>
props.deleteToDo(hash, idToken, props.todos);
};
const cancelTaskHandler = (hash: string, idToken: string) =>
props.cancelToDo(hash, idToken, props.todos);
let cards = [];
if (props.isLoading === false && props.isAuth === true) {
cards = props.todos.map((todo: TODOtype) => {
console.log(todo);
if (todo.isFinished === true && taskType === "finished") {
if (taskType === "finished" && todo.isFinished === true) {
return (
<div
className="card bg-light"
@ -79,12 +77,12 @@ const TodoList = (props: any) => {
className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
>
Delete <i className="fas fa-minus-circle"></i>
Delete <i className="fas fa-trash"></i>
</button>
</div>
</div>
);
} else if (taskType === "all" && todo.isFinished === false ) {
} else if (taskType === "all" && todo.isFinished === false) {
return (
<div
className="card bg-light"
@ -122,8 +120,17 @@ const TodoList = (props: any) => {
className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
>
Delete <i className="fas fa-minus-circle"></i>
Delete <i className="fas fa-trash"></i>
</button>
{!todo.isCanceled && (
<button
onClick={() => cancelTaskHandler(todo.hash, props.idToken)}
type="button"
className="btn btn-warning"
>
Cancel <i className="fas fa-minus-circle"></i>
</button>
)}
</div>
</div>
);
@ -163,8 +170,17 @@ const TodoList = (props: any) => {
className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
>
Delete <i className="fas fa-minus-circle"></i>
Delete <i className="fas fa-trash"></i>
</button>
{!todo.isCanceled && (
<button
onClick={() => cancelTaskHandler(todo.hash, props.idToken)}
type="button"
className="btn btn-warning"
>
Cancel <i className="fas fa-minus-circle"></i>
</button>
)}
</div>
</div>
);
@ -208,7 +224,105 @@ const TodoList = (props: any) => {
className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
>
Delete <i className="fas fa-minus-circle"></i>
Delete <i className="fas fa-trash"></i>
</button>
{!todo.isCanceled && (
<button
onClick={() => cancelTaskHandler(todo.hash, props.idToken)}
type="button"
className="btn btn-warning"
>
Cancel <i className="fas fa-minus-circle"></i>
</button>
)}
</div>
</div>
);
} else if (
todo.isFinished === false &&
(dayjs(new Date()).diff(todo.date) < 0 ||
dayjs(new Date()).format("YYYY-MM-DD") === todo.date) &&
taskType === "current"
) {
return (
<div
className="card bg-light"
style={{ height: "min-content" }}
key={todo.hash}
>
<div className="card-header">End Date: {todo.date}</div>
<div className="card-body">
<h5 className="card-title">Title: {todo.taskName}</h5>
<p className="card-text description">
Description: {todo.taskDesc}
</p>
<hr />
<p className="card-text">
Important:{" "}
{todo.isImportant ? (
<input type="checkbox" readOnly checked />
) : (
<input type="checkbox" readOnly checked={false} />
)}
</p>
<hr />
<p className="card-text">type: {todo.taskType}</p>
<button
onClick={() => finishTaskHandler(todo.hash, props.idToken)}
type="button"
className="btn btn-dark"
>
Finish <i className="fas fa-check"></i>
</button>
<button
type="button"
className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
>
Delete <i className="fas fa-trash"></i>
</button>
{!todo.isCanceled && (
<button
onClick={() => cancelTaskHandler(todo.hash, props.idToken)}
type="button"
className="btn btn-warning"
>
Cancel <i className="fas fa-minus-circle"></i>
</button>
)}
</div>
</div>
);
} else if (todo.isCanceled === true && taskType === "Canceled") {
return (
<div
className="card bg-light"
style={{ height: "min-content" }}
key={todo.hash}
>
<div className="card-header">End Date: {todo.date}</div>
<div className="card-body">
<h5 className="card-title">Title: {todo.taskName}</h5>
<p className="card-text description">
Description: {todo.taskDesc}
</p>
<hr />
<p className="card-text">
Important:{" "}
{todo.isImportant ? (
<input type="checkbox" readOnly checked />
) : (
<input type="checkbox" readOnly checked={false} />
)}
</p>
<hr />
<p className="card-text">type: {todo.taskType}</p>
<button
type="button"
className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
>
Delete <i className="fas fa-trash"></i>
</button>
</div>
</div>
@ -252,6 +366,8 @@ const mapDispatchToProps = (dispatch: any) => {
dispatch(actions.finishToDo(hash, idToken, todoList)),
deleteToDo: (hash: string, idToken: string, todoList: []) =>
dispatch(actions.deleteToDo(hash, idToken, todoList)),
cancelToDo: (hash: string, idToken: string, todoList: []) =>
dispatch(actions.cancelToDo(hash, idToken, todoList)),
};
};

View File

@ -11,4 +11,6 @@ export const LOAD_SUCCESS = 'LOAD_SUCCESS';
export const UPDATE_SUCCESS = 'UPDATE_SUCCESS';
export const UPDATE_FAIL = 'UPDATE_FAIL';
export const DELETE_SUCCESS = 'DELETE_SUCCESS';
export const DELETE_FAIL = 'DELETE_FAIL';
export const DELETE_FAIL = 'DELETE_FAIL';
export const CANCEL_SUCCESS = 'CANCEL_SUCCESS';
export const CANCEL_FAIL = 'CANCEL_FAIL';

View File

@ -14,6 +14,7 @@ type ToDos = {
type: string;
userId: string;
hash: string;
isCanceled: boolean;
};
export const loadToDos = (userId: string, token: string) => {
@ -36,6 +37,7 @@ export const loadToDos = (userId: string, token: string) => {
date: data[key].date,
userId: data[key].userId,
isFinished: data[key].isFinished,
isCanceled: data[key].isCanceled,
});
}
dispatch({
@ -118,6 +120,7 @@ export const addToDo = (
date: date,
userId: userId,
isFinished: false,
isCanceled: false
};
let url = `https://to-do-studia.firebaseio.com/todos.json?auth=${token}`;
@ -221,3 +224,48 @@ export const deleteToDo = (
})
};
};
export const cancelToDo = (
hash: string,
token: string,
todoList: Array<ToDos>
) => {
return (dispatch: any) => {
const data = {
isCanceled: true,
};
const newArr = todoList.filter(todo => {
if(hash === todo.hash){
todo.isCanceled = true;
}
return todo;
});
let url = `https://to-do-studia.firebaseio.com/todos/${hash}.json?auth=${token}`;
fetch(url, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then((res) => res.json())
.then((data) => {
if (data.error) {
throw new Error(data.error.message);
}
dispatch({
type: actionType.CANCEL_SUCCESS,
todos: newArr
});
})
.catch((err) => {
dispatch({
type: actionType.CANCEL_FAIL,
error: err,
});
});
};
};

View File

@ -1 +1,9 @@
export {loadToDos, addToDo, auth, authLogout, finishToDo, deleteToDo} from './actions';
export {
loadToDos,
addToDo,
auth,
authLogout,
finishToDo,
deleteToDo,
cancelToDo,
} from "./actions";

View File

@ -88,6 +88,16 @@ const reducer = (state = initialState, action: ActionType) => {
...state,
error: action.error,
};
case actionType.CANCEL_SUCCESS:
return {
...state,
todos: action.todos,
};
case actionType.CANCEL_FAIL:
return {
...state,
error: action.error,
};
default:
return state;
}