firebase connected GET POST

This commit is contained in:
Tomasz Kasprowicz 2020-12-24 17:39:00 +01:00
parent 800dda5e4c
commit e4e218a910
12 changed files with 514 additions and 50 deletions

1
.eslintcache Normal file

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,7 @@ import SideDrawer from "./components/SideDrawer";
import { Switch, Route } from "react-router-dom";
import Login from "./components/Login";
import CreateTask from "./components/CreateTask";
import TodoList from "./components/TodoList";
const StyledApp = styled.div`
height: 100vh;
@ -29,6 +30,27 @@ function App() {
<Route exact path="/login">
<Login />
</Route>
<Route exact path="/all">
<TodoList />
</Route>
<Route exact path="/finished">
<TodoList />
</Route>
<Route exact path="/current">
<TodoList />
</Route>
<Route exact path="/business">
<TodoList />
</Route>
<Route exact path="/personal">
<TodoList />
</Route>
<Route exact path="/important">
<TodoList />
</Route>
<Route exact path="/Canceled">
<TodoList />
</Route>
</Switch>
</StyledApp>
);

View File

@ -17,6 +17,12 @@ const StyledButton = styled.button`
&:hover {
background-color: rgba(18, 18, 18, 0.6);
}
&:focus, &:active {
border-color: inherit;
-webkit-box-shadow: none;
box-shadow: none;
outline:none;
}
`;
const Button: React.FC<Props> = (props) => {

View File

@ -1,6 +1,9 @@
import React from "react";
import React, { ReactEventHandler, useState } from "react";
import styled from "styled-components";
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";
const StyledTasks = styled.div`
width: 100%;
@ -16,13 +19,49 @@ const StyledTasks = styled.div`
color: blue !important;
cursor: pointer;
}
textarea{
resize:none;
textarea {
resize: none;
}
}
`;
const CreateTask = () => {
const CreateTask = (props: any) => {
const [taskName, setTaskName] = useState<string>("");
const [taskDesc, setTaskDesc] = useState<string>("");
const [taskType, setTaskType] = useState<string>("bussiness");
const [isImportant, setIsImportant] = useState<boolean>(false);
const [date, setDate] = useState<string>("");
const addTaskHandler = () => {
const isValid = formValidator();
if (isValid) {
props.isAuth
? props.addToDo(
taskName,
taskDesc,
taskType,
isImportant,
date,
props.idToken,
props.userId
)
: props.history.push("/login");
}
};
const radioButtonsHandler = (type: string) => {
setTaskType(type);
};
const importantButtonsHandler = (e: any) => {
if (e.target.value === "true") setIsImportant(true);
};
const formValidator = () => {
if (taskName.length > 1 && taskDesc.length > 1 && date.length > 1)
return true;
};
return (
<StyledTasks>
<form>
@ -30,7 +69,13 @@ const CreateTask = () => {
<div className="form-group">
<label>Task Name</label>
<input type="text" className="form-control" placeholder="Task Name" />
<input
type="text"
className="form-control"
placeholder="Task Name"
value={taskName}
onChange={(e) => setTaskName(e.target.value)}
/>
</div>
<div className="form-group">
@ -39,6 +84,8 @@ const CreateTask = () => {
className="form-control"
id="exampleFormControlTextarea1"
rows={4}
value={taskDesc}
onChange={(e) => setTaskDesc(e.target.value)}
></textarea>
</div>
@ -51,6 +98,7 @@ const CreateTask = () => {
value="bussiness"
checked
readOnly
onClick={() => radioButtonsHandler("bussiness")}
/>
<label className="form-check-label" htmlFor="bussiness">
Bussiness
@ -64,6 +112,7 @@ const CreateTask = () => {
id="personal"
value="personal"
readOnly
onClick={() => radioButtonsHandler("personal")}
/>
<label className="form-check-label" htmlFor="personal">
Personal
@ -73,20 +122,30 @@ const CreateTask = () => {
<input
className="form-check-input"
type="checkbox"
value=""
value="true"
id="important"
onChange={(e) => importantButtonsHandler(e)}
/>
<label className="form-check-label" htmlFor="important">
Important
</label>
</div>
<hr/>
<hr />
<div className="form-group">
<label>End Date</label>
<input type="date" className="form-control" placeholder="Date" />
<input
type="date"
className="form-control"
placeholder="Date"
onChange={(e) => setDate(e.target.value)}
/>
</div>
<button type="submit" className="btn btn-primary btn-block">
<button
onClick={addTaskHandler}
type="button"
className="btn btn-primary btn-block"
>
Create Task
</button>
</form>
@ -94,4 +153,35 @@ const CreateTask = () => {
);
};
export default CreateTask;
interface RootState {
isAuth: boolean;
idToken: string;
userId: string
}
const mapStateToProps = (state: RootState) => {
return {
isAuth: state.isAuth,
idToken: state.idToken,
userId: state.userId,
};
};
const mapDispatchToProps = (dispatch: any) => {
return {
addToDo: (
name: string,
desc: string,
type: string,
important: boolean,
date: string,
token: string,
userId: string,
) => dispatch(actions.addToDo(name, desc, type, important, date, token, userId)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(CreateTask));

View File

@ -2,6 +2,8 @@ import React from "react";
import styled from "styled-components";
import { NavLink } from "react-router-dom";
import Button from "./Button";
import * as actions from "../store/actions/actions";
import { connect } from "react-redux";
const StyledHeader = styled.div`
height: 8%;
@ -12,6 +14,7 @@ const StyledHeader = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
outline:none;
.menu-icon {
width: 1rem;
height: 1rem;
@ -32,9 +35,16 @@ const StyledHeader = styled.div`
interface Props {
sideDrawer: () => void;
isOpen: boolean;
isAuth: boolean;
logout: () => void;
}
const Header: React.FC<Props> = (props) => {
const Header: React.FC<Props> = (props: Props) => {
const logoutHandler = ()=>{
props.isAuth && props.logout();
}
return (
<StyledHeader>
<Button onClick={props.sideDrawer}>
@ -51,10 +61,27 @@ const Header: React.FC<Props> = (props) => {
</span>
<NavLink to={process.env.PUBLIC_URL + "/login"}>
<Button>Login</Button>
<Button onClick={logoutHandler}>{props.isAuth?'Logout': 'Login'}</Button>
</NavLink>
</StyledHeader>
);
};
export default Header;
interface RootState {
isAuth: boolean;
}
const mapStateToProps = (state: RootState) => {
return {
isAuth: state.isAuth,
};
};
const mapDispatchToProps = (dispatch: any) => {
return {
auth: (email: string, password: string, isSignupMode: boolean) => dispatch(actions.auth(email, password, isSignupMode)),
logout: () => dispatch(actions.authLogout())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Header);

View File

@ -1,6 +1,9 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import styled from "styled-components";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import * as actions from "../store/actions/actions";
import { Redirect } from "react-router-dom";
const StyledForm = styled.div`
width: 100%;
@ -12,26 +15,24 @@ const StyledForm = styled.div`
width: 100%;
max-width: 40rem;
padding: 2rem;
a {
.login-button {
color: blue !important;
cursor: pointer;
border: none;
}
}
`;
const Login = () => {
const Login = (props: any) => {
const [signUp, setSignUp] = useState<boolean>(false);
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const authorizationHandler = (e:React.FormEvent) => {
const authorizationHandler = (e: React.FormEvent) => {
e.preventDefault();
if (signUp) {
console.log('rejestracja', email, password);
} else {
console.log('log', email, password);
}
signUp
? props.auth(email, password, true)
: props.auth(email, password, false);
};
return (
@ -73,14 +74,38 @@ const Login = () => {
<p className="forgot-password text-right">
Already registered{" "}
{signUp ? (
<a onClick={() => setSignUp(false)}>sign in?</a>
<button className="login-button" onClick={() => setSignUp(false)}>
sign in?
</button>
) : (
<a onClick={() => setSignUp(true)}>sign up?</a>
<button className="login-button" onClick={() => setSignUp(true)}>
sign up?
</button>
)}
</p>
</form>
{props.isAuth && <Redirect to={props.redirectTo} />}
</StyledForm>
);
};
export default Login;
interface RootState {
isAuth: boolean;
redirectTo: string;
}
const mapStateToProps = (state: RootState) => {
return {
isAuth: state.isAuth,
redirectTo: state.redirectTo,
};
};
const mapDispatchToProps = (dispatch: any) => {
return {
auth: (email: string, password: string, isSignupMode: boolean) =>
dispatch(actions.auth(email, password, isSignupMode)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Login);

View File

@ -1,11 +1,12 @@
import React from "react";
import styled from "styled-components";
import { NavLink } from "react-router-dom";
const StyledSidedrawer = styled.div`
position: fixed;
top: 0;
left: 0;
z-index:8;
z-index: 8;
overflow-y: hidden;
position: absolute;
top: 0;
@ -29,14 +30,21 @@ const StyledSidedrawer = styled.div`
display: flex;
justify-content: flex-start;
align-items: center;
padding-right:1rem;
padding-right: 1rem;
i {
padding: 0 1rem;
font-size: 1rem;
}
span {
padding: 0 1rem;
display:contents;
display: contents;
}
a {
text-decoration: none;
color: #fff;
}
.active {
color: #4153af;
}
}
}
@ -50,28 +58,46 @@ const SideDrawer = () => {
<StyledSidedrawer>
<ul>
<li>
<i className="fas fa-hourglass-end"></i>
<span>Finished</span>
<NavLink to={process.env.PUBLIC_URL + "/all"}>
<i className="fas fa-box-open"></i>
<span>All</span>
</NavLink>
</li>
<li>
<i className="fas fa-stopwatch"></i>
<span>On going</span>
<NavLink to={process.env.PUBLIC_URL + "/finished"}>
<i className="fas fa-hourglass-end"></i>
<span>Finished</span>
</NavLink>
</li>
<li>
<i className="fas fa-briefcase"></i>
<span>Bussiness</span>
<NavLink to={process.env.PUBLIC_URL + "/current"}>
<i className="fas fa-stopwatch"></i>
<span>On going</span>
</NavLink>
</li>
<li>
<i className="fas fa-lock"></i>
<span>Personal</span>
<NavLink to={process.env.PUBLIC_URL + "/business"}>
<i className="fas fa-briefcase"></i>
<span>Bussiness</span>
</NavLink>
</li>
<li>
<i className="fas fa-flag"></i>
<span>Important</span>
<NavLink to={process.env.PUBLIC_URL + "/personal"}>
<i className="fas fa-lock"></i>
<span>Personal</span>
</NavLink>
</li>
<li>
<i className="fas fa-ban"></i>
<span>Canceled</span>
<NavLink to={process.env.PUBLIC_URL + "/important"}>
<i className="fas fa-flag"></i>
<span>Important</span>
</NavLink>
</li>
<li>
<NavLink to={process.env.PUBLIC_URL + "/canceled"}>
<i className="fas fa-ban"></i>
<span>Canceled</span>
</NavLink>
</li>
</ul>
</StyledSidedrawer>

View File

@ -0,0 +1,78 @@
import React, { useEffect, useState } from "react";
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";
interface TODOtype {
date: string;
taskName: string;
taskDesc: string;
isImportant: string;
taskType: string;
hash: string;
}
const TodosStyled = styled.div`
display: flex;
flex-direction:column;
height:92%;
overflow: scroll;
`;
const TodoList = (props: any) => {
useEffect(() => {
props.isAuth && props.loadToDos(props.userId, props.idToken);
}, []);
let cards = [];
if (props.isLoading === false) {
cards = props.todos.map((todo: TODOtype) => {
return (
<div
className="card text-white bg-info mb-3"
style={{ width: "100%" }}
key={todo.hash}
>
<div className="card-header">{todo.date}</div>
<div className="card-body">
<h5 className="card-title">{todo.taskName}</h5>
<p className="card-text">{todo.taskDesc}</p>
<hr />
<p className="card-text">{todo.isImportant && "important"}</p>
<hr />
<p className="card-text">{todo.taskType}</p>
</div>
</div>
);
});
}
return <TodosStyled>{cards}</TodosStyled>;
};
interface RootState {
isAuth: boolean;
todos: [];
userId: boolean;
idToken: string;
isLoading: boolean;
}
const mapStateToProps = (state: RootState) => {
return {
todos: state.todos,
userId: state.userId,
idToken: state.idToken,
isAuth: state.isAuth,
isLoading: state.isLoading,
};
};
const mapDispatchToProps = (dispatch: any) => {
return {
loadToDos: (userId: string, idToken: string) =>
dispatch(actions.loadToDos(userId, idToken)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

View File

@ -1,3 +1,10 @@
export const ADD_TODO = 'ADD_TODO';
export const DELETE_TODO = 'DELETE_TODO';
export const LOAD_TODOS = 'LOAD_TODOS';
export const LOAD_TODOS = 'LOAD_TODOS';
export const AUTH_SUCCESS = 'AUTH_SUCCESS';
export const AUTH_FAIL = 'AUTH_FAIL';
export const AUTH_LOGOUT = 'AUTH_LOGOUT';
export const ADD_SUCCESS = 'ADD_SUCCESS';
export const ADD_FAIL = 'ADD_FAIL';
export const LOAD_FAIL = 'LOAD_FAIL';
export const LOAD_SUCCESS = 'LOAD_SUCCESS';

View File

@ -1,5 +1,135 @@
export const loadToDos = ()=>{
return dispatch =>{
import * as actionType from "../actions/actionTypes";
type ILoadToDos = {
type: string;
todos: Array<object>;
};
export const loadToDos = (userId: string, token: string) => {
return (dispatch: any) => {
let url = `https://to-do-studia.firebaseio.com/todos.json?auth=${token}&orderBy="userId"&equalTo="${userId}"`;
fetch(url)
.then((res) => res.json())
.then((data) => {
if (data.error) {
throw new Error(data.error.message);
}
let todos = [];
for (let key in data) {
todos.push({
hash: key,
taskName: data[key].name,
taskDesc: data[key].desc,
taskType: data[key].type,
isImportant: data[key].important,
date: data[key].date,
userId: data[key].userId,
});
}
dispatch({
type: actionType.LOAD_SUCCESS,
todos: todos,
});
})
.catch((err) => {
dispatch({
type: actionType.LOAD_FAIL,
error: err,
});
});
};
};
export const auth = (
email: string,
password: string,
isSignupMode: boolean
) => {
return (dispatch: any) => {
const data = {
email: email,
password: password,
returnSecureToken: true,
};
let url = `https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyBq4PiZ3S-qQnXK3LBw_8CvfBMSkQU6obY`;
if (!isSignupMode) {
url = `https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyBq4PiZ3S-qQnXK3LBw_8CvfBMSkQU6obY`;
}
}
fetch(url, {
method: "POST",
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.AUTH_SUCCESS,
idToken: data.idToken,
userId: data.localId,
});
})
.catch((error) => {
dispatch({ type: actionType.AUTH_FAIL, error: error.message });
});
};
};
export const authLogout = () => {
return {
type: actionType.AUTH_LOGOUT,
};
};
export const addToDo = (
name: string,
desc: string,
type: string,
important: boolean,
date: string,
token: string,
userId: string
) => {
return (dispatch: any) => {
const data = {
name: name,
desc: desc,
type: type,
important: important,
date: date,
userId: userId,
};
let url = `https://to-do-studia.firebaseio.com/todos.json?auth=${token}`;
fetch(url, {
method: "POST",
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.ADD_SUCCESS,
});
})
.catch((err) => {
dispatch({
type: actionType.ADD_FAIL,
error: err,
});
});
};
};

View File

@ -0,0 +1 @@
export {loadToDos, addToDo, auth, authLogout} from './actions';

View File

@ -1,21 +1,72 @@
import * as actionType from "../actions/actionTypes";
import produce from "immer"
import produce from "immer";
const initialState = {
isLoading: true,
todos: [],
isAuth: false,
apiError: null,
error: null,
userId: null,
idToken: null,
redirectTo: "",
};
interface ActionType{
type:string
interface ActionType {
type: any;
userId: any;
idToken: any;
error: any;
todos: [];
}
const reducer = (state = initialState, action:ActionType) => {
const reducer = (state = initialState, action: ActionType) => {
switch (action.type) {
case actionType.LOAD_TODOS: {
return state;
}
case actionType.AUTH_SUCCESS:
return {
...state,
isAuth: true,
error: null,
userId: action.userId,
idToken: action.idToken,
redirectTo: "/",
};
case actionType.AUTH_FAIL:
return {
...state,
error: action.error,
};
case actionType.ADD_SUCCESS:
return {
...state,
};
case actionType.ADD_FAIL:
return {
...state,
error: action.error,
};
case actionType.AUTH_LOGOUT:
return {
...state,
isAuth: false,
userId: null,
idToken: null,
rateData: null,
};
case actionType.LOAD_SUCCESS:
return {
...state,
todos: action.todos,
isLoading: false,
};
case actionType.LOAD_FAIL:
return {
...state,
error: action.error,
};
default:
return state;
}