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 Header from "./components/Header";
import styled from "styled-components"; import styled from "styled-components";
import SideDrawer from "./components/SideDrawer"; import SideDrawer from "./components/SideDrawer";
@ -6,13 +6,14 @@ import { Switch, Route } from "react-router-dom";
import Login from "./components/Login"; import Login from "./components/Login";
import CreateTask from "./components/CreateTask"; import CreateTask from "./components/CreateTask";
import TodoList from "./components/TodoList"; import TodoList from "./components/TodoList";
import { connect } from "react-redux";
const StyledApp = styled.div` const StyledApp = styled.div`
height: 100vh; height: 100vh;
background-color: #eee; background-color: #eee;
`; `;
function App() { const App = (props: any)=> {
const [sideDrawer, setSiderDrawer] = useState<boolean>(false); const [sideDrawer, setSiderDrawer] = useState<boolean>(false);
const toggleSideDrawerHandler = () => { const toggleSideDrawerHandler = () => {
@ -23,6 +24,10 @@ function App() {
setSiderDrawer(false); setSiderDrawer(false);
} }
useEffect(()=>{
props.error !== null && alert(props.error);
}, [props.error])
return ( return (
<StyledApp> <StyledApp>
<Header sideDrawer={toggleSideDrawerHandler} isOpen={sideDrawer} /> <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 { connect } from "react-redux";
import * as actions from "../store/actions/actions"; import * as actions from "../store/actions/actions";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import dayjs from'dayjs'; import dayjs from "dayjs";
const StyledTasks = styled.div` const StyledTasks = styled.div`
width: 100%; width: 100%;
@ -33,13 +33,13 @@ const CreateTask = (props: any) => {
const [isImportant, setIsImportant] = useState<boolean>(false); const [isImportant, setIsImportant] = useState<boolean>(false);
const [date, setDate] = useState<string>(""); 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 addTaskHandler = () => {
const isValid = formValidator(); const isValid = formValidator();
if (isValid) { if (isValid) {
props.isAuth if (props.isAuth) {
? props.addToDo( props.addToDo(
taskName, taskName,
taskDesc, taskDesc,
taskType, taskType,
@ -47,9 +47,20 @@ const CreateTask = (props: any) => {
date, date,
props.idToken, props.idToken,
props.userId props.userId
) );
: props.history.push("/login"); clearFormHandler();
} else {
props.history.push("/login");
} }
}
};
const clearFormHandler = () => {
setTaskName("");
setTaskDesc("");
setTaskType("bussiness");
setIsImportant(false);
setDate("");
}; };
const radioButtonsHandler = (type: string) => { const radioButtonsHandler = (type: string) => {
@ -61,8 +72,11 @@ const CreateTask = (props: any) => {
}; };
const formValidator = () => { const formValidator = () => {
if (taskName.length > 1 && taskDesc.length > 1 && date.length > 1) if (taskName.length > 1 && taskDesc.length > 1 && date.length > 1) {
return true; return true;
} else {
alert("Fill in inputs!!!");
}
}; };
return ( return (
@ -160,7 +174,7 @@ const CreateTask = (props: any) => {
interface RootState { interface RootState {
isAuth: boolean; isAuth: boolean;
idToken: string; idToken: string;
userId: string userId: string;
} }
const mapStateToProps = (state: RootState) => { const mapStateToProps = (state: RootState) => {
@ -180,8 +194,11 @@ const mapDispatchToProps = (dispatch: any) => {
important: boolean, important: boolean,
date: string, date: string,
token: string, token: string,
userId: string, userId: string
) => dispatch(actions.addToDo(name, desc, type, important, date, token, userId)), ) =>
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 "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import { connect } from "react-redux"; import { connect } from "react-redux";
import styled from "styled-components"; import styled from "styled-components";
import sadFace from "../assets/images/sadFace.png";
import { withRouter } from "react-router-dom"; import { withRouter } from "react-router-dom";
import dayjs from "dayjs";
interface TODOtype { interface TODOtype {
date: string; date: string;
@ -14,6 +14,7 @@ interface TODOtype {
taskType: string; taskType: string;
hash: string; hash: string;
isFinished: boolean; isFinished: boolean;
isCanceled: boolean;
} }
const TodosStyled = styled.div` const TodosStyled = styled.div`
@ -21,7 +22,7 @@ const TodosStyled = styled.div`
height: 92%; height: 92%;
overflow: scroll; overflow: scroll;
justify-content: center; justify-content: center;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
.description { .description {
height: 100px; height: 100px;
overflow: scroll; overflow: scroll;
@ -34,23 +35,20 @@ const TodoList = (props: any) => {
}, []); }, []);
const taskType = props.match.path.substring(1); const taskType = props.match.path.substring(1);
console.log(taskType);
const finishTaskHandler = (hash: string, idToken: string) => { const finishTaskHandler = (hash: string, idToken: string) =>
console.log("update");
props.finishToDo(hash, idToken, props.todos); props.finishToDo(hash, idToken, props.todos);
};
const deleteTaskHandler = (hash: string, idToken: string) => { const deleteTaskHandler = (hash: string, idToken: string) =>
console.log("delete");
props.deleteToDo(hash, idToken, props.todos); props.deleteToDo(hash, idToken, props.todos);
};
const cancelTaskHandler = (hash: string, idToken: string) =>
props.cancelToDo(hash, idToken, props.todos);
let cards = []; let cards = [];
if (props.isLoading === false && props.isAuth === true) { if (props.isLoading === false && props.isAuth === true) {
cards = props.todos.map((todo: TODOtype) => { cards = props.todos.map((todo: TODOtype) => {
console.log(todo); if (taskType === "finished" && todo.isFinished === true) {
if (todo.isFinished === true && taskType === "finished") {
return ( return (
<div <div
className="card bg-light" className="card bg-light"
@ -79,12 +77,12 @@ const TodoList = (props: any) => {
className="btn btn-danger" className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)} onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
> >
Delete <i className="fas fa-minus-circle"></i> Delete <i className="fas fa-trash"></i>
</button> </button>
</div> </div>
</div> </div>
); );
} else if (taskType === "all" && todo.isFinished === false ) { } else if (taskType === "all" && todo.isFinished === false) {
return ( return (
<div <div
className="card bg-light" className="card bg-light"
@ -122,8 +120,17 @@ const TodoList = (props: any) => {
className="btn btn-danger" className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)} onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
> >
Delete <i className="fas fa-minus-circle"></i> Delete <i className="fas fa-trash"></i>
</button> </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>
</div> </div>
); );
@ -163,8 +170,17 @@ const TodoList = (props: any) => {
className="btn btn-danger" className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)} onClick={() => deleteTaskHandler(todo.hash, props.idToken)}
> >
Delete <i className="fas fa-minus-circle"></i> Delete <i className="fas fa-trash"></i>
</button> </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>
</div> </div>
); );
@ -208,7 +224,105 @@ const TodoList = (props: any) => {
className="btn btn-danger" className="btn btn-danger"
onClick={() => deleteTaskHandler(todo.hash, props.idToken)} 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> </button>
</div> </div>
</div> </div>
@ -252,6 +366,8 @@ const mapDispatchToProps = (dispatch: any) => {
dispatch(actions.finishToDo(hash, idToken, todoList)), dispatch(actions.finishToDo(hash, idToken, todoList)),
deleteToDo: (hash: string, idToken: string, todoList: []) => deleteToDo: (hash: string, idToken: string, todoList: []) =>
dispatch(actions.deleteToDo(hash, idToken, todoList)), dispatch(actions.deleteToDo(hash, idToken, todoList)),
cancelToDo: (hash: string, idToken: string, todoList: []) =>
dispatch(actions.cancelToDo(hash, idToken, todoList)),
}; };
}; };

View File

@ -12,3 +12,5 @@ export const UPDATE_SUCCESS = 'UPDATE_SUCCESS';
export const UPDATE_FAIL = 'UPDATE_FAIL'; export const UPDATE_FAIL = 'UPDATE_FAIL';
export const DELETE_SUCCESS = 'DELETE_SUCCESS'; 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; type: string;
userId: string; userId: string;
hash: string; hash: string;
isCanceled: boolean;
}; };
export const loadToDos = (userId: string, token: string) => { export const loadToDos = (userId: string, token: string) => {
@ -36,6 +37,7 @@ export const loadToDos = (userId: string, token: string) => {
date: data[key].date, date: data[key].date,
userId: data[key].userId, userId: data[key].userId,
isFinished: data[key].isFinished, isFinished: data[key].isFinished,
isCanceled: data[key].isCanceled,
}); });
} }
dispatch({ dispatch({
@ -118,6 +120,7 @@ export const addToDo = (
date: date, date: date,
userId: userId, userId: userId,
isFinished: false, isFinished: false,
isCanceled: false
}; };
let url = `https://to-do-studia.firebaseio.com/todos.json?auth=${token}`; 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, ...state,
error: action.error, error: action.error,
}; };
case actionType.CANCEL_SUCCESS:
return {
...state,
todos: action.todos,
};
case actionType.CANCEL_FAIL:
return {
...state,
error: action.error,
};
default: default:
return state; return state;
} }