Merge branch 'task-41-feature/project_list_front' into 'development'

Task 41 feature/project list front

See merge request !33
parents 343bd71c 394e029b
...@@ -16,6 +16,7 @@ router.get('/',async (req:Request, res:Response): Promise<Response>=> { ...@@ -16,6 +16,7 @@ router.get('/',async (req:Request, res:Response): Promise<Response>=> {
router.post('/', async (req:Request, res:Response): Promise<Response> => { router.post('/', async (req:Request, res:Response): Promise<Response> => {
if (!req.body) return res.status(400).send({Message:'problem in incoming req.body'}) if (!req.body) return res.status(400).send({Message:'problem in incoming req.body'})
const token = req.get('Authorization'); const token = req.get('Authorization');
console.log("token:" + token)
const {title, dateDue,color,department,workers,tasks}= req.body; const {title, dateDue,color,department,workers,tasks}= req.body;
const user = await dataSource const user = await dataSource
.createQueryBuilder() .createQueryBuilder()
...@@ -36,7 +37,7 @@ router.post('/', async (req:Request, res:Response): Promise<Response> => { ...@@ -36,7 +37,7 @@ router.post('/', async (req:Request, res:Response): Promise<Response> => {
return res.send({project}) return res.send({project})
}) })
router.get("/id/:id",async (req:Request, res:Response): Promise<Response> => { router.get("/:id",async (req:Request, res:Response): Promise<Response> => {
const project = await dataSource const project = await dataSource
.createQueryBuilder() .createQueryBuilder()
.select("project") .select("project")
......
...@@ -7,6 +7,9 @@ import Login from './containers/Login/Login'; ...@@ -7,6 +7,9 @@ import Login from './containers/Login/Login';
import Register from './containers/Register/Register'; import Register from './containers/Register/Register';
import MonthCalendar from './containers/MonthCalendar/MonthCalendar'; import MonthCalendar from './containers/MonthCalendar/MonthCalendar';
import ForgottenPassword from "./containers/ForgottenPassword/ForgottenPassword"; import ForgottenPassword from "./containers/ForgottenPassword/ForgottenPassword";
import Projects from "./containers/Projects/Projects";
import FullProject from "./containers/FullProject/FullProject";
import NewProject from "./containers/NewProject/NewProject";
const ProtectedRoute = ({isAllowed, roles, redirectUrl, children}) => { const ProtectedRoute = ({isAllowed, roles, redirectUrl, children}) => {
const user = useSelector(state => state.users?.user); const user = useSelector(state => state.users?.user);
...@@ -32,7 +35,30 @@ const App = () => { ...@@ -32,7 +35,30 @@ const App = () => {
</main> </main>
</> </>
}> }>
<Route path={"/projects"} element={
<ProtectedRoute
isAllowed={user}
redirectUrl={"/sign-in"}
>
<Projects/>
</ProtectedRoute>
}/>
<Route path={"/projects/:id"} element={
<ProtectedRoute
isAllowed={user}
redirectUrl={"/sign-in"}
>
<FullProject/>
</ProtectedRoute>
}/>
<Route path={"/projects/add"} element={
<ProtectedRoute
isAllowed={user}
redirectUrl={"/sign-in"}
>
<NewProject/>
</ProtectedRoute>
}/>
<Route path={"/"} element={ <Route path={"/"} element={
<ProtectedRoute <ProtectedRoute
isAllowed={user} isAllowed={user}
......
...@@ -23,6 +23,14 @@ const AdminMenu = () => { ...@@ -23,6 +23,14 @@ const AdminMenu = () => {
handleClose() handleClose()
} }
return <> return <>
<Button
component={NavLink}
to="/projects"
color="inherit"
size="large"
>
Проекты
</Button>
<Button <Button
component={NavLink} component={NavLink}
to="/week" to="/week"
......
import { Button, Menu, MenuItem } from "@mui/material";
import { useState } from "react";
import { useDispatch } from "react-redux";
import { NavLink, useNavigate } from "react-router-dom";
import { logoutUser } from "../../../store/actions/usersActions";
import HasAccess from "../../UI/HasAccess/HasAccess";
const UserMenu = ({ user }) => {
const dispatch = useDispatch();
const navigate = useNavigate()
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const logout = () => {
dispatch(logoutUser(navigate));
handleClose()
}
return <>
<Button
color="inherit"
onClick={handleClick}
>
Hello, {user?.displayName}
</Button>
{/* <HasAccess roles={["admin"]}>
<Button
component={NavLink}
color="inherit"
to="/admin"
>
Admin panel
</Button>
</HasAccess> */}
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={logout}>Logout</MenuItem>
</Menu>
</>
};
export default UserMenu;
\ No newline at end of file
...@@ -23,6 +23,14 @@ const WorkerMenu = () => { ...@@ -23,6 +23,14 @@ const WorkerMenu = () => {
handleClose() handleClose()
} }
return <> return <>
<Button
component={NavLink}
to="/projects"
color="inherit"
size="large"
>
Проекты
</Button>
<Button <Button
component={NavLink} component={NavLink}
to="/week" to="/week"
......
import {Button, Grid} from "@mui/material";
import {useState} from "react";
import { useSelector } from "react-redux";
import FormElement from "../UI/Form/FormElement/FormElement";
const ProjectForm = ({onSubmit}) => {
const users = useSelector(state => state.users)
console.log(users)
const [state, setState] = useState({
title: "",
color: ""
});
const submitFormHandler = (e) => {
e.preventDefault();
let project = {title: state.title, color: state.color}
console.log(project);
onSubmit(project);
};
const inputChangeHandler = (e) => {
const {name, value} = e.target;
setState(prevState => {
return {...prevState, [name]: value};
});
};
return <form onSubmit={submitFormHandler}>
<Grid container direction="column" spacing={2}>
<FormElement
onChange={inputChangeHandler}
name={"title"}
label={"Title"}
state={state}
/>
<FormElement
onChange={inputChangeHandler}
name={"color"}
label={"Color"}
state={state}
/>
<Grid item>
<Button
type="submit"
color="primary"
variant="contained"
>
Create
</Button>
</Grid>
</Grid>
</form>
};
export default ProjectForm;
\ No newline at end of file
import { Card, CardActions, CardContent, Grid, IconButton } from "@mui/material";
import { Link } from "react-router-dom";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
import { useDispatch, useSelector } from "react-redux";
const ProjectItem = ({ title, tasks, id }) => {
const user = useSelector(state => state.users.user);
const dispatch = useDispatch();
console.log(tasks)
return <>
<Grid item xs={12} sm={12} md={6} lg={4}>
<Card>
<CardContent>
<strong>
<br></br>
Название проекта: {title}
</strong>
<strong>
<br></br>
{/* Задачи: {tasks} */}
</strong>
</CardContent>
<CardActions>
<IconButton component={Link} to={"/projects/" + id}>
<ArrowForwardIcon />
</IconButton>
</CardActions>
</Card>
</Grid>
</>
};
export default ProjectItem;
import {Grid} from "@mui/material";
import ProjectItem from "../ProjectItem/ProjectItem";
const ProjectsList = ({projects}) => {
return (
<Grid item container direction="row" spacing={1}>
{projects?.map(project => {
return <ProjectItem
tasks={project.tasks}
workers={project.workers}
title={project.title}
createdAt={project.createdAt}
dateDue={project.dateDue}
admin={project.admin}
id={project.id}
key={project.id}
/>
})}
</Grid>
);
};
export default ProjectsList;
\ No newline at end of file
...@@ -5,7 +5,7 @@ const FormElement = ({ name, label, state, error, onChange, select, options, typ ...@@ -5,7 +5,7 @@ const FormElement = ({ name, label, state, error, onChange, select, options, typ
let inputChildren = null let inputChildren = null
if (select) { if (select) {
inputChildren = options.map(option => { inputChildren = options?.map(option => {
return <MenuItem key={option._id} value={option._id}> return <MenuItem key={option._id} value={option._id}>
{option.name} {option.name}
</MenuItem> </MenuItem>
......
import { Card, CardActions, CardContent, Grid } from "@mui/material";
import { useLocation, useParams } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { useEffect } from "react";
import { fetchProject } from "../../store/actions/projectsActions";
const FullProject = () => {
const { projects, project } = useSelector(state => state.projects);
const dispatch = useDispatch();
console.log(projects);
let location = useLocation();
let point = location.pathname
console.log(point.includes("admin"))
const params = useParams()
useEffect(() => {
dispatch(fetchProject(params.id))
}, [params.id, dispatch]);
console.log(project)
return <>
<Grid item xs={12} sm={12} md={6} lg={4}>
<Card>
<h2>Проект - {project?.project?.title}</h2>
<CardContent>
<strong>
<br></br>
Дата создания проекта: {project?.project?.createdAt}
</strong>
<strong>
<br></br>
Цвет: {project?.project?.color}
</strong>
<strong>
<br></br>
Админ проекта: {project?.project?.admin.name}
</strong>
<strong>
<br></br>
Задачи: {projects?.projects?.tasks?.map(task => {
return <>
<strong><br></br>Задача проекта: {task.title} </strong>
</>
})}
</strong>
</CardContent>
<CardActions>
</CardActions>
</Card>
</Grid>
</>
};
export default FullProject;
import {useNavigate} from "react-router-dom";
import {useDispatch, useSelector} from "react-redux";
import {Typography} from "@mui/material";
import { useEffect } from "react";
import ProjectForm from "../../components/ProjectForm/ProjectForm";
import { createProject, fetchProjects } from "../../store/actions/projectsActions";
const NewProject = () => {
const dispatch = useDispatch();
const projects = useSelector(state => state.projects.projects);
const navigate = useNavigate();
const onSubmit = async (projectData) => {
await dispatch(createProject(projectData, navigate));
};
useEffect(()=> {
dispatch(fetchProjects());
}, [dispatch])
return (
<>
<Typography variant="h2">New project</Typography>
<ProjectForm projects={projects} onSubmit={onSubmit} />
</>
);
};
export default NewProject;
\ No newline at end of file
import { Grid, Typography, Button } from "@mui/material";
import { Link } from "react-router-dom";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import Loader from "../../components/UI/Loader/Loader";
import HasAccess from "../../components/UI/HasAccess/HasAccess";
import { fetchProjects } from "../../store/actions/projectsActions";
import ProjectsList from "../../components/ProjectsList/ProjectsList";
const Projects = () => {
const dispatch = useDispatch();
const { projects, loading } = useSelector(state => state.projects.projects);
console.log(projects)
console.log(loading)
useEffect(() => {
dispatch(fetchProjects())
}, [dispatch]);
return <>
{projects?.length > 0 ? (<>
<Grid container direction="column" spacing={2}>
<Grid
container
item
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Typography variant="h4">
Projects
</Typography>
</Grid>
<HasAccess roles={["superuser", "admin", "user"]} >
<Grid item>
<Button component={Link} to="/projects/add">
Add project
</Button>
</Grid>
</HasAccess>
</Grid>
<Loader loading={loading} />
<ProjectsList projects={projects} />
</Grid>
</>) :
<h1>Созданных проектов нет</h1>
}
</>
};
export default Projects;
\ No newline at end of file
...@@ -7,6 +7,7 @@ import { Provider } from 'react-redux'; ...@@ -7,6 +7,7 @@ import { Provider } from 'react-redux';
import usersReducer from './store/reducers/usersReducer'; import usersReducer from './store/reducers/usersReducer';
import tasksReducer from './store/reducers/tasksReducer'; import tasksReducer from './store/reducers/tasksReducer';
import axios from './axiosPlanner'; import axios from './axiosPlanner';
import projectsReducer from './store/reducers/projectsReducer';
const localStorageMiddleware = ({getState}) => (next) => (action) => { const localStorageMiddleware = ({getState}) => (next) => (action) => {
const result = next(action); const result = next(action);
...@@ -25,7 +26,8 @@ const loadFromLocalStorage = () => { ...@@ -25,7 +26,8 @@ const loadFromLocalStorage = () => {
const store = configureStore({ const store = configureStore({
reducer: { reducer: {
users: usersReducer, users: usersReducer,
tasks: tasksReducer tasks: tasksReducer,
projects: projectsReducer
}, },
preloadedState: loadFromLocalStorage(), preloadedState: loadFromLocalStorage(),
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(localStorageMiddleware) middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(localStorageMiddleware)
......
export const FETCH_PROJECTS_REQUEST = "FETCH_PROJECTS_REQUEST";
export const FETCH_PROJECTS_SUCCESS = "FETCH_PROJECTS_SUCCESS";
export const FETCH_PROJECTS_ERROR = "FETCH_PROJECTS_ERROR";
export const FETCH_PROJECT_SUCCESS = "FETCH_PROJECT_SUCCESS";
export const CREATE_PROJECT_SUCCESS = "CREATE_PROJECT_SUCCESS";
import axios from "../../axiosPlanner";
import { CREATE_PROJECT_SUCCESS, FETCH_PROJECTS_ERROR, FETCH_PROJECTS_REQUEST, FETCH_PROJECTS_SUCCESS, FETCH_PROJECT_SUCCESS } from "../actionTypes/projectsActionTypes";
import { showNotification } from "./commonActions";
const fetchProjectsRequest = () => {
return {type: FETCH_PROJECTS_REQUEST}
};
const fetchProjectsSuccess = (projects) => {
return {type: FETCH_PROJECTS_SUCCESS, projects};
};
const fetchProjectSuccess = (project) => {
return {type: FETCH_PROJECT_SUCCESS, project};
};
const fetchProjectsError = (error) => {
return {type: FETCH_PROJECTS_ERROR, error};
}
const createProjectSuccess = () => {
return {type: CREATE_PROJECT_SUCCESS};
};
export const fetchProjects = () => {
return async dispatch => {
dispatch(fetchProjectsRequest());
try {
const response = await axios.get("/projects");
dispatch(fetchProjectsSuccess(response.data));
} catch(e) {
dispatch(fetchProjectsError(e));
}
};
};
export const fetchProject = (id) => {
return async dispatch => {
dispatch(fetchProjectsRequest());
try {
const response = await axios.get("/projects/" + id);
dispatch(fetchProjectSuccess(response.data));
} catch (e) {
dispatch(fetchProjectsError(e));
}
}
};
export const createProject = (projectData, navigate) => {
return async (dispatch) => {
try {
await axios.post("/projects", projectData);
dispatch(createProjectSuccess());
navigate("/");
dispatch(showNotification("Проект успешно создан"))
} catch (e) {
console.log(e);
dispatch(showNotification("Не удалось создать проект", "error"))
}
};
}
\ No newline at end of file
import axios from "../../axiosPlanner"; import axios from "../../axiosPlanner";
import { LOGIN_USER_FAILURE, LOGIN_USER_SUCCESS, LOGOUT_USER_FAILURE, LOGOUT_USER_SUCCESS, REGISTER_USER_FAILURE, REGISTER_USER_REQUEST, REGISTER_USER_SUCCESS } from "../actionTypes/actionTypes" import { LOGIN_USER_FAILURE, LOGIN_USER_SUCCESS, LOGOUT_USER_FAILURE, LOGOUT_USER_SUCCESS, REGISTER_USER_FAILURE, REGISTER_USER_REQUEST, REGISTER_USER_SUCCESS } from "../actionTypes/userActionTypes"
import { showNotification } from "./commonActions"; import { showNotification } from "./commonActions";
const registerUserRequest = () => { const registerUserRequest = () => {
......
import {FETCH_PROJECTS_ERROR, FETCH_PROJECTS_REQUEST, FETCH_PROJECTS_SUCCESS, FETCH_PROJECT_SUCCESS } from "../actionTypes/projectsActionTypes";
const initialState = {
projects: [],
project: "",
loading: false,
error: null
};
const projectsReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_PROJECTS_REQUEST:
return {...state, loading: true};
case FETCH_PROJECTS_SUCCESS:
return {...state, loading: false, projects: action.projects};
case FETCH_PROJECTS_ERROR:
return {...state, loading: false, error: action.error};
case FETCH_PROJECT_SUCCESS:
return {...state, loading: false, project: action.project}
default:
return state;
}
};
export default projectsReducer;
\ No newline at end of file
import { REGISTER_USER_REQUEST, REGISTER_USER_SUCCESS, REGISTER_USER_FAILURE, LOGIN_USER_SUCCESS, LOGIN_USER_FAILURE, LOGOUT_USER_SUCCESS } from "../actionTypes/actionTypes"; import { REGISTER_USER_REQUEST, REGISTER_USER_SUCCESS, REGISTER_USER_FAILURE, LOGIN_USER_SUCCESS, LOGIN_USER_FAILURE, LOGOUT_USER_SUCCESS } from "../actionTypes/userActionTypes";
const initialState = { const initialState = {
user: null, user: null,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment