Commit 812e601b authored by Ermolaev Timur's avatar Ermolaev Timur

Merge branch 'development' of…

Merge branch 'development' of ssh://git.attractor-school.com:30022/apollo64/crm-team-one into task-54-feature/resize_tasks
parents 41b2f23a 87084c37
......@@ -16,6 +16,7 @@ router.get('/',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'})
const token = req.get('Authorization');
console.log("token:" + token)
const {title, dateDue,color,department,workers,tasks}= req.body;
const user = await dataSource
.createQueryBuilder()
......@@ -36,7 +37,7 @@ router.post('/', async (req:Request, res:Response): Promise<Response> => {
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
.createQueryBuilder()
.select("project")
......
......@@ -2,6 +2,7 @@ import express,{Router, Request, Response} from 'express';
import {Task} from '../models/Task';
import {myDataSource} from '../app-data-source';
import { User } from '../models/User';
import { Project } from '../models/Project';
const router:Router = express.Router();
const dataSource = myDataSource;
......@@ -37,7 +38,7 @@ router.post('/', async(req:Request, res:Response):Promise<Response>=>{
return res.send({newTask})
})
router.get('/userId/:userId', async (req: Request, res: Response):Promise<Response>=>{
router.get('/:userId', async (req: Request, res: Response):Promise<Response>=>{
const userId = req.params.userId;
const tasks = await dataSource
.getRepository(Task)
......@@ -111,4 +112,53 @@ router.put('/',async(req:Request, res:Response)=> {
})
router.post('/project',async (req: Request, res: Response):Promise<Response>=>{
let projectArray :string[]= req.body;
console.log('projectArray ', projectArray)
if (projectArray.length===0) {
const rawTasks = await
dataSource
.getRepository(Task)
.find({
relations:{
executors:true,
author:true,
project:true
}
})
const tasks:object[]= rawTasks.filter(task=>task.project===null)
return res.send({tasks})
}
const tasks = await
dataSource
.getRepository(Task)
.createQueryBuilder('task')
.innerJoinAndSelect( 'task.project', 'project')
.where('task.project IN(:...projects)', {projects:projectArray})
.getMany()
return res.send({tasks})
})
// router.post('/projects',async (req: Request, res: Response):Promise<Response>=>{
// let projectArray :string[]= req.body;
// console.log('projectArray ', projectArray)
// if (projectArray.length===0) {
// const tasks = await
// dataSource
// .getRepository(Task)
// .createQueryBuilder('task')
// // .innerJoinAndSelect('task.executors', 'user')
// .innerJoinAndSelect('task.executors AND task.author', 'user')
// .where('task.project IS NULL')
// .getMany()
// return res.send({tasks})
// }
// ///ssome code
// return res.send({message:"some other staff"})
// })
export default router;
......@@ -7,6 +7,9 @@ import Login from './containers/Login/Login';
import Register from './containers/Register/Register';
import MonthCalendar from './containers/MonthCalendar/MonthCalendar';
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 user = useSelector(state => state.users?.user);
......@@ -32,7 +35,30 @@ const App = () => {
</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={
<ProtectedRoute
isAllowed={user}
......
......@@ -23,6 +23,14 @@ const AdminMenu = () => {
handleClose()
}
return <>
<Button
component={NavLink}
to="/projects"
color="inherit"
size="large"
>
Проекты
</Button>
<Button
component={NavLink}
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 = () => {
handleClose()
}
return <>
<Button
component={NavLink}
to="/projects"
color="inherit"
size="large"
>
Проекты
</Button>
<Button
component={NavLink}
to="/week"
......
import { Grid } from "@mui/material";
import { memo, useMemo } from "react";
import { memo, useEffect, useMemo, useState } from "react";
import CalendarStandartCell from "../CalendarStandartCell.js/CalendarStandartCell";
import CalendarTask from "../CalendarTask/CalendarTask";
import EmptyBox from "./EmptyBox/EmptyBox";
const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, handleOpen, modal, setCurrentTask, year, month, tasks, day, hourFormat}) => {
const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, handleOpen, modal, setCurrentTask, year, month, tasks, day, hourFormat, setCurrentLine, currentLine, dragTaskHandler}) => {
const hours = useMemo(()=>{
return hoursInDay.map((hour)=>parseInt(hour.split(':')[0]))},
......@@ -25,8 +26,8 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
const sortedTasks = useMemo(() => {
if (availableTasks.length) {
const newSortedArr = [...availableTasks].sort(function(a,b){
const durattionFirstDate = new Date(a.dateTimeDue).getTime() - new Date(a.dateTimeStart).getTime()
const durattionSecondDate = new Date(b.dateTimeDue).getTime() - new Date(b.dateTimeStart).getTime()
const durattionFirstDate = a.infoForCell.endHour - a.infoForCell.startHour
const durattionSecondDate = b.infoForCell.endHour - b.infoForCell.startHour
return durattionSecondDate - durattionFirstDate;
})
return newSortedArr
......@@ -44,6 +45,7 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
hourDiff = 2
hourDiffEnd = 1
}
if (availableTasks.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
for (let k = 0; k < sortedTasks.length; k++) {
......@@ -73,11 +75,15 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
}
}
if (!havePlace) {
if (j + 1 === lines.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
}
havePlace = true
break;
}
line[i] += `-${k}`
if ((task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour + hourDiffEnd) || (!hourFormat && task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour)) {
if ((task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour + hourDiffEnd)
|| (!hourFormat && task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour)) {
skipLine = true
break;
}
......@@ -101,11 +107,11 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
const boxes = []
for (let i = 0; i < line.length; i++) {
if (!isNaN(line[i])) {
if (boxes[boxes.length -1]?.task === null) {
boxes[boxes.length -1].xs += xs
} else {
boxes.push({xs: xs, task: null})
}
// if (boxes[boxes.length -1]?.task === null) {
// boxes[boxes.length -1].xs += xs
// } else {
boxes.push({xs: xs, task: null, hour: line[i]})
// }
} else {
const task = sortedTasks[line[i].split('-')[1]]
const taskIsThere = boxes.find((taskFind)=>{
......@@ -114,7 +120,9 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
if (taskIsThere) {
taskIsThere.xs +=xs
} else {
boxes.push({xs: xs, task: sortedTasks[line[i].split('-')[1]]})
boxes.push({
xs: xs,
task: sortedTasks[line[i].split('-')[1]]})
}
}
}
......@@ -129,17 +137,20 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
xs={10.8}
align='center'
>
{hoursInDay.map((hour, i)=>{
return (
<CalendarStandartCell
key={i}
item xs={xs}
createTaskInCellHandler={createTaskInCellHandler}
hours={hour}
dayNumber={day.dayNumber}
currentTask={currentTask}
handleOpen={handleOpen}
modal={modal}
linesInDay={linesInDay}
key={i}
item xs={xs}
createTaskInCellHandler={createTaskInCellHandler}
hours={hour}
dragTaskHandler={dragTaskHandler}
dayNumber={day.dayNumber}
currentTask={currentTask}
handleOpen={handleOpen}
modal={modal}
>
</CalendarStandartCell>
)
......@@ -148,30 +159,62 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
{linesInDay?.map((line, i)=>{
const boxes = getBoxesInLine(line)
return(
<Grid key={i} container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginTop: i === 0 ? '-35px' : 0, marginBottom: '5px'}}>
{boxes.map((box, i)=>{
<Grid key={i} container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginBottom: '5px'}}>
{boxes.map((box, index)=>{
if (box.task) {
return (<Grid
key={box.task.id}
item xs={box.xs}
sx={{height: '35px', marginBottom: '5px', display: 'flex', justifyContent: 'flex-start'}}
>
key={i}
item xs={box.xs}
sx={{height: '35px', marginBottom: '5px'}}
>
<CalendarTask
dragTaskHandler={dragTaskHandler}
setCurrentLine={()=>{setCurrentLine(day.dayNumber)}}
currentTask={currentTask}
currentLine={currentLine}
hour={parseInt(hours[index])}
line={day.dayNumber}
task={box.task}
setCurrentTask={setCurrentTask}
handleOpen={handleOpen}
/>
</Grid>)
} else {
return (<Grid key={i} item xs={box.xs} sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)'}}>
</Grid>)
return (<EmptyBox
key={i}
modal={modal}
dayNumber={day.dayNumber}
hourNumber={box.hour}
handleOpen={handleOpen}
dragTaskHandler={dragTaskHandler}
createTaskInCellHandler={createTaskInCellHandler}
xs={box.xs}
>
</EmptyBox>)
}
})}
</Grid>)
})}
<Grid container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginBottom: '5px'}}>
{hoursInDay.map((hour, i)=>{
const hourNumber = parseInt(hour)
return(<EmptyBox
key={i}
modal={modal}
dayNumber={day.dayNumber}
hourNumber={hourNumber}
handleOpen={handleOpen}
dragTaskHandler={dragTaskHandler}
createTaskInCellHandler={createTaskInCellHandler}
xs={xs}
>
</EmptyBox>)
})}
</Grid>
</Grid>
</>
};
export default memo(CalendarRowDay);
\ No newline at end of file
export default memo(CalendarRowDay);
import { Grid} from "@mui/material";
import React, { memo, useEffect, useState} from "react";
import DefaultTask from "../../DefaultTask/DefaultTask";
const EmptyBox = ({hourNumber, handleOpen, dayNumber, xs, dragTaskHandler, modal, createTaskInCellHandler}) => {
const [isThisCell, setIsThisCell] = useState(false)
useEffect(()=>{
if(!modal) {
setIsThisCell(false);
}
}, [modal])
const onClickHandler = (e, dayNumber, hour) => {
createTaskInCellHandler(dayNumber, hour);
setIsThisCell(true);
handleOpen(e)
}
const dragOverHandler = (e) => {
e.preventDefault();
}
const dropHandler = (e) => {
e.stopPropagation()
e.preventDefault();
dragTaskHandler(dayNumber, hourNumber)
}
return(<Grid
onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}}
onClick={(e)=>{onClickHandler(e, dayNumber, hourNumber)}}
item xs={xs} sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)'}}>
{isThisCell ?
<DefaultTask/> : null}
</Grid>)
};
export default memo(EmptyBox, (prevProps, nextProps)=>{
if(!prevProps.modal) return false
if(nextProps.modal) return true
});
\ No newline at end of file
import { Grid} from "@mui/material";
import { memo, useEffect, useState } from "react";
const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, divRef }) => {
const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, divRef, dragTaskHandler, linesInDay}) => {
const [isThisCell, setIsThisCell] = useState(false)
useEffect(()=>{
if(!modal) {
......@@ -9,11 +9,23 @@ const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCell
}
}, [modal])
const dragOverHandler = (e) => {
e.preventDefault();
}
const dropHandler = (e) => {
e.stopPropagation()
e.preventDefault();
dragTaskHandler(dayNumber, parseInt(hours.split(':')[0]))
}
return <>
<Grid
item xs={xs}
sx={{position: 'relative', height: '35px'}}
sx={{position: 'relative', height: linesInDay?.length ? `${35*linesInDay}px` : '35px'}}
onClick={(e)=>{createTaskInCellHandler(dayNumber, hours); setIsThisCell(true); handleOpen(e)}}
onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}}
>
{children}
{isThisCell ?
......
......@@ -2,8 +2,19 @@ import { Grid} from "@mui/material";
import React, { memo, useEffect, useRef} from "react";
const CalendarTask = ({setCurrentTask, handleOpen, task}) => {
const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, currentLine, currentTask, dragTaskHandler, hour}) => {
const [color, setColor] = useState('')
useEffect(() => {
if (task.priority) {
if (task.priority === 'A') setColor('rgb(32, 138, 250)')
if (task.priority === 'B') setColor('lightgreen')
if (task.priority === 'C') setColor('yellow')
} else {
setColor('rgb(171, 157, 157);')
}
}, [task])
const ref = useRef(null);
const refLeft = useRef(null);
const refRight = useRef(null);
......@@ -72,21 +83,33 @@ const CalendarTask = ({setCurrentTask, handleOpen, task}) => {
handleOpen(e)
}
return (<>
<Grid ref={ref}
sx={{flexBasis: '100%', position: 'relative', height: '30px', backgroundColor: 'lightgreen', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', borderRadius: '10px', margin: '5px 10px', display: 'flex', alignItems: 'center', paddingLeft: '5px', zIndex: '5', justifyContent: 'space-between'}}
onClick={(e)=>{onClickTaskHandler(e, task)}}
>
<div ref={refLeft} style={{cursor: 'col-resize',height: '100%', left: '0',top: '0', width: '5px', backgroundColor: 'grey'}}></div>
<span>
{task.title}
</span>
<div ref={refRight}
style={{cursor: 'col-resize',height: '100%', right: '0',top: '0', width: '5px', backgroundColor: 'grey'}}></div>
</Grid>
const dragLeaveHandler = (e) => {
e.target.style.boxShadow = 'none'
}
const dragStartHandler = (e, line, task) => {
setCurrentLine()
setCurrentTask(task);
}
const dragEndHandler = (e) => {
e.target.style.boxShadow = 'none'
}
return (<>
<Grid
draggable={true}
ref={ref}
onDragLeave={(e)=>{dragLeaveHandler(e)}}
onDragStart={(e)=>{dragStartHandler(e, line, task)}}
onDragEnd={(e)=>{dragEndHandler(e)}}
sx={{ position: 'relative', height: '30px', backgroundColor: color, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', borderRadius: '10px', margin: '5px 10px', display: 'flex', justifyContent: 'flex-start', alignItems: 'center', paddingLeft: '5px', zIndex: '5'}}
onClick={(e)=>{onClickTaskHandler(e, task)}}
>
<div ref={refLeft} style={{cursor: 'col-resize',height: '100%', left: '0',top: '0', width: '5px', backgroundColor: 'grey'}}></div>
<span>
{task.title}
</span>
<div ref={refRight} style={{cursor: 'col-resize',height: '100%', right: '0',top: '0', width: '5px', backgroundColor: 'grey'}}></div>
</Grid>
</>)
};
......
import { Box } from "@mui/material";
import { memo } from "react";
const DefaultTask = ({}) => {
return (<>
<Box
sx={{ position: 'relative', height: '30px', backgroundColor: 'lightgreen', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', borderRadius: '10px', margin: '5px 10px', display: 'flex', justifyContent: 'flex-start', alignItems: 'center', paddingLeft: '5px', zIndex: '5'}}
>
<span>
Задача
</span>
</Box>
</>)
};
export default memo(DefaultTask);
\ No newline at end of file
import { FormControlLabel, Switch} from "@mui/material";
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import CalendarRow from "./CalendarRow/CalendarRow";
import CalendarSmallCell from "./CalendarSmallCell/CalendarSmallCell";
import CalendarStandartCell from "./CalendarStandartCell.js/CalendarStandartCell";
......@@ -7,33 +7,39 @@ import ModalTask from "../UI/ModalTask/ModalTask";
import MonthCalendarModalContent from "../MonthCalendarModalContent/MonthCalendarModalContent";
import CalendarRowDay from "./CalendarRowDay/CalendarRowDay";
function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, currentTask, setCurrentTask, hourFormat, setHourFormat, onChangeCurrentTaskHandler, sendNewTaskHandler, deleteTaskHandler, cellSizes, hoursInDay, daysInMonth}) {
function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, currentTask, setCurrentTask, hourFormat, setHourFormat, onChangeCurrentTaskHandler, sendNewTaskHandler, deleteTaskHandler, cellSizes, hoursInDay, daysInMonth, dragTaskHandler}) {
const [currentLine, setCurrentLine] = useState('')
const [modal, setModal] = useState({open:false, y: 0, x: 0,});
const handleOpen = (e) => {
setModal( {
open: true,
yPage: e.pageY,
xPage: e.pageX,
yPage: e.clientY,
xPage: e.clientX,
yDivClick: e.nativeEvent.offsetY,
xDivClick: e.nativeEvent.offsetX,
yDiv: e.target.offsetHeight,
xDiv: e.target.offsetWidth,
})
};
const handleClose = () => {
setModal({...modal, open: false})
setCurrentTask({})
};
const divRef = useRef(null)
const [divHeight, setDivHeight] = useState('')
useEffect(() => {
if (divRef) {
setDivHeight(()=>{
return divRef.current?.offsetHeight
})
}
}, [divRef.current?.offsetHeight, hourFormat, month, tasks]);
}
}, [divRef.current?.offsetHeight, hourFormat, month, tasks]);
return (
<div ref={divRef} style={{marginBottom: '30px'}}>
......@@ -54,30 +60,34 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
)
})}
</CalendarRow>
{daysInMonth?.map((day, i)=>{
return (
<CalendarRow
key={i}
>
<CalendarSmallCell xs={cellSizes.smallCell}>{day.dayNumber}</CalendarSmallCell>
<CalendarSmallCell xs={cellSizes.smallCell}>{day.dayOfWeek}</CalendarSmallCell>
<CalendarRowDay
xs={cellSizes.dayCell}
createTaskInCellHandler={createTaskInCellHandler}
hoursInDay={hoursInDay}
currentTask={currentTask}
handleOpen={handleOpen}
modal={modal.open}
setCurrentTask={setCurrentTask}
year={year}
month={month}
tasks={tasks}
day={day}
hourFormat={hourFormat}
/>
</CalendarRow>
)
})}
{daysInMonth?.map((day, i)=>{
return (
<CalendarRow
key={i}
>
<CalendarSmallCell xs={cellSizes.smallCell}>{day.dayNumber}</CalendarSmallCell>
<CalendarSmallCell xs={cellSizes.smallCell}>{day.dayOfWeek}</CalendarSmallCell>
<CalendarRowDay
dragTaskHandler={dragTaskHandler}
xs={cellSizes.dayCell}
createTaskInCellHandler={createTaskInCellHandler}
hoursInDay={hoursInDay}
currentTask={currentTask}
handleOpen={handleOpen}
currentLine={currentLine}
setCurrentLine={setCurrentLine}
modal={modal.open}
setCurrentTask={setCurrentTask}
year={year}
month={month}
tasks={tasks}
day={day}
hourFormat={hourFormat}
>
</CalendarRowDay>
</CalendarRow>
)
})}
<ModalTask
modal={modal}
handleClose={()=>{handleClose()}}
......
......@@ -8,6 +8,18 @@ import MultipleSelect from '../../components/UI/MultipleSelect/MultipleSelect';
export default function MyTaskToolBar(props) {
let projectsFilter =
<></>
if (props.projects) {
projectsFilter=
<MultipleSelect
projects={props.projects}
onClose={props.onClose}
projectName={props.projectName}
setProjectName={props.setProjectName}
/>
}
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
......@@ -15,13 +27,8 @@ export default function MyTaskToolBar(props) {
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Мои задачи
</Typography>
<MultipleSelect
projects={props.projects}
onClose={props.onClose}
projectName={props.projectName}
setProjectName={props.setProjectName}
/>
<Button color="inherit" onClick={props.onClick} >Добавить задачу</Button>
{projectsFilter}
<Button color="inherit" onClick={()=>(props.onClick)} >Добавить задачу</Button>
</Toolbar>
</AppBar>
</Box>
......
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
let inputChildren = null
if (select) {
inputChildren = options.map(option => {
inputChildren = options?.map(option => {
return <MenuItem key={option._id} value={option._id}>
{option.name}
</MenuItem>
......
......@@ -46,9 +46,11 @@ export default function MultipleSelect(props) {
return (
<div>
<FormControl sx={{ m: 1, width: 250,borderColor:'white' }}>
<InputLabel id="demo-multiple-name-label" sx={{color:'white' }}>Project</InputLabel>
<InputLabel placeholder='Choose Project' label='I am a really really long green TextField label' id="demo-multiple-name-label" sx={{color:'white', padding:'1' }}>Project</InputLabel>
<Select
labelId="demo-multiple-name-label"
label='Choose Project'
name='Choose Project'
id="demo-multiple-name"
multiple
value={props.projectName}
......
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;
......@@ -101,8 +101,14 @@ function MonthCalendar() {
const iso = dateLocal.toISOString();
return iso;
}
const createTaskInCellHandler = (dayNumber, dayHour) => {
const hour = parseInt(dayHour.split(':')[0])
let hour
if (!isNaN(dayHour)) {
hour = dayHour
} else {
hour = parseInt(dayHour.split(':')[0])
}
let hourDue
if (hourFormat) {
hourDue = hour + 0
......@@ -118,11 +124,26 @@ function MonthCalendar() {
}
setCurrentTask((newTask))
}
const dragTaskHandler = async (dayNumber, hour) => {
const timeEnd = currentTask.dateTimeDue.split('T')[1]
const timeEndHour = parseInt(timeEnd.split(':')[0])
const timeStart = currentTask.dateTimeStart.split('T')[1]
const timeStartHour = parseInt(timeStart.split(':')[0])
const hourDiff = timeEndHour - timeStartHour
const due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour + hourDiff, 59))
const start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour, 0))
await dispatch(editTask({
...currentTask,
dateTimeStart: start,
dateTimeDue: due
}))
setCurrentTask({})
}
const sendNewTaskHandler = async () => {
if (currentTask.id) {
delete currentTask.infoForCell
console.log(currentTask)
setCurrentTask(() => {
return{
...currentTask,
......@@ -171,6 +192,7 @@ function MonthCalendar() {
cellSizes={cellSizes}
hoursInDay={hoursInDay}
daysInMonth={daysInMonth}
dragTaskHandler={dragTaskHandler}
/>
</>
......
......@@ -58,18 +58,12 @@ function stableSort(array, comparator) {
export default function EnhancedTable() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchAllTasks());
}, [dispatch]);
const tasks = useSelector((state) => state.tasks.tasks);
const calendarTasks = useSelector((state) => state.tasks.tasks);
const tasks = calendarTasks
console.log(tasks)
const [recievedTasks, setRecievedTasks] = useState([]);
const projects=[
"project1",
"project2",
"project3"
]
const [projects,setProjects]=useState(['1','2'])
const [order, setOrder] = React.useState("asc");
const [orderBy, setOrderBy] = React.useState("id");
......@@ -91,21 +85,6 @@ console.log(tasks)
setPage(0);
};
useEffect(() => {
if (tasks && tasks?.length > 0) {
let currentTasks = [];
currentTasks = tasks?.map((task) => {
return {
...task,
isEditMode: false,
readOnly: true,
authorDisplayName: task.author.displayName,
};
});
setRecievedTasks(currentTasks);
}
}, [tasks]);
const onChange = (e, task) => {
const value = e.target.value;
const name = e.target.name;
......@@ -220,33 +199,49 @@ console.log(tasks)
// ++++++++фильтрация по проектам+++++++++++++++
const [projectName, setProjectName] = React.useState([]);
const [filter, setFilter] = React.useState(false);
const [filterProjectTumbler, setFilterProjectTumbler] = React.useState(false);
const onClose=(projectName)=>{
console.log(projectName)
setFilter(true)
console.log("projectName", projectName, 'projects',projects)
let tasksFilteredByProject = tasks
if (projectName.length>0) {
tasksFilteredByProject = tasks.filter(task=>projectName.includes(task.project?.title))
}
setRecievedTasks(tasksFilteredByProject)
setFilterProjectTumbler(true)
}
useEffect(() => {
const filterProjectsNamesFromTasks = ()=>{
if (tasks && tasks?.length > 0) {
let currentTasks = [];
let i
for (i=0; i<projectName?.lenght; i++)
{currentTasks = tasks?.map((task) => {
if (task.project=projectName)
return {
...task,
};
})};
setRecievedTasks(currentTasks);
const rawProjects= tasks.map(task=>task.project)
let rawSetProjectNames = []
for (let project of rawProjects){
if (project===null){
} else{
rawSetProjectNames.push(project.title)
}
}
let uniqueTitlesProjects = [...new Set(rawSetProjectNames)];
setProjects(uniqueTitlesProjects)
console.log('filterProjectsNamesFromTasks uniqueTitlesProjects',uniqueTitlesProjects)
}
}, [filter==true]);
}
useEffect(() => {
dispatch(fetchAllTasks());
filterProjectsNamesFromTasks()
if (tasks && tasks?.length > 0) {
setRecievedTasks(tasks);
}
}, [tasks===undefined]);
// ++++++++фильтрация по проектам+++++++++++++++
if (
tasks &&
tasks?.length > 0 &&
recievedTasks &&
recievedTasks?.length > 0
recievedTasks?.length >= 0
) {
return (
<Box sx={{ width: "fullwidth" }}>
......@@ -267,7 +262,7 @@ console.log(tasks)
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
rowCount={tasks.length}
// rowCount={tasks.length}
/>
<TableBody>
{/* <TableRow sx={{height:'1px',margin:0,padding:0}}>
......@@ -419,6 +414,7 @@ console.log(tasks)
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
......
......@@ -119,5 +119,5 @@ EnhancedTableHead.propTypes = {
onRequestSort: PropTypes.func.isRequired,
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
orderBy: PropTypes.string.isRequired,
rowCount: PropTypes.number.isRequired,
// rowCount: PropTypes.number.isRequired,
};
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';
import usersReducer from './store/reducers/usersReducer';
import tasksReducer from './store/reducers/tasksReducer';
import axios from './axiosPlanner';
import projectsReducer from './store/reducers/projectsReducer';
const localStorageMiddleware = ({getState}) => (next) => (action) => {
const result = next(action);
......@@ -25,7 +26,8 @@ const loadFromLocalStorage = () => {
const store = configureStore({
reducer: {
users: usersReducer,
tasks: tasksReducer
tasks: tasksReducer,
projects: projectsReducer
},
preloadedState: loadFromLocalStorage(),
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";
......@@ -8,6 +8,10 @@ export const ADD_NEW_TASK_REQUEST = "ADD_NEW_TASK_REQUEST";
export const ADD_NEW_TASK_SUCCESS = "ADD_NEW_TASK_SUCCESS";
export const ADD_NEW_TASK_FAILURE = "ADD_NEW_TASK_FAILURE";
export const FETCH_TASKS_BY_PROJECT_REQUEST = "FETCH_TASKS_BY_PROJECT_REQUEST";
export const FETCH_TASKS_BY_PROJECT_SUCCESS = "FETCH_TASKS_BY_PROJECT_SUCCESS";
export const FETCH_TASKS_BY_PROJECT_FAILURE = "FETCH_TASKS_BY_PROJECT_FAILURE";
export const EDIT_TASK_REQUEST = "EDIT_TASK_REQUEST";
export const EDIT_TASK_SUCCESS = "EDIT_TASK_SUCCESS";
export const EDIT_TASK_FAILURE = "EDIT_TASK_FAILURE";
......
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
......@@ -12,6 +12,9 @@ import {
FETCH_CALENDAR_TASKS_REQUEST,
FETCH_CALENDAR_TASKS_SUCCESS,
FETCH_ALL_TASKS_SUCCESS,
FETCH_TASKS_BY_PROJECT_SUCCESS,
FETCH_TASKS_BY_PROJECT_FAILURE,
FETCH_TASKS_BY_PROJECT_REQUEST
} from "../actionTypes/tasksTypes";
import axios from '../../axiosPlanner'
......@@ -95,11 +98,8 @@ const editTaskFailure = (error) => {
export const editTask = (task) => {
return async (dispatch, getState) => {
dispatch(editTaskRequest());
// const token = getState().users?.user?.token;
try {
console.log('task' , task)
const r=await axios.put("/tasks/", task);
console.log(r)
await axios.put("/tasks", task);
dispatch(editTaskSuccess())
dispatch(fetchAllTasks())
dispatch(fetchCalendarTasks())
......@@ -134,4 +134,29 @@ export const deleteTask = (taskId) => {
dispatch(deleteTaskFailure(error.response.data));
}
}
}
const fetchTasksByProjectRequest = () => {
return {type: FETCH_TASKS_BY_PROJECT_REQUEST}
};
const fetchTasksByProjectSuccess = () => {
return {type: FETCH_TASKS_BY_PROJECT_SUCCESS}
};
const fetchTasksByProjectFailure = (error) => {
return {type: FETCH_TASKS_BY_PROJECT_FAILURE, error}
};
export const fetchTasksByProject = (projects) => {
return async (dispatch) => {
dispatch(fetchTasksByProjectRequest());
try {
const response =await axios.post("/tasks/project", projects);
dispatch(fetchTasksByProjectSuccess(response.data.tasks))
} catch (error) {
dispatch(fetchTasksByProjectFailure(error.response.data));
}
}
}
\ No newline at end of file
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";
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
......@@ -13,6 +13,9 @@ import {
DELETE_TASK_FAILURE,
FETCH_ALL_TASKS_SUCCESS,
EDIT_CALENDAR_TASK,
FETCH_TASKS_BY_PROJECT_REQUEST,
FETCH_TASKS_BY_PROJECT_SUCCESS,
FETCH_TASKS_BY_PROJECT_FAILURE
} from "../actionTypes/tasksTypes";
const initialState = {
......@@ -66,6 +69,12 @@ const tasksReduсer = (state = initialState, action) => {
return {...state, loading: true};
case ADD_NEW_TASK_FAILURE:
return {...state, loading: false, error: action.error};
case FETCH_TASKS_BY_PROJECT_SUCCESS:
return {...state, loading: false, tasks: action.tasks};
case FETCH_TASKS_BY_PROJECT_REQUEST:
return {...state, loading: true};
case FETCH_TASKS_BY_PROJECT_FAILURE:
return {...state, loading: false, error: action.error};
case EDIT_TASK_SUCCESS:
return {...state, loading: false};
case EDIT_TASK_REQUEST:
......
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 = {
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