удалила сортировку из колонки Дата и время выполнения, скорректировала работу…

удалила сортировку из колонки Дата и время выполнения, скорректировала работу сортировок, откорректировала ширину колонок
parent 2a234024
...@@ -25,7 +25,14 @@ export const auth = async(req: Request,res: Response, next:NextFunction):Promise ...@@ -25,7 +25,14 @@ export const auth = async(req: Request,res: Response, next:NextFunction):Promise
/**Check if user with the given token is executor or author of task with the given Id(taskId) */ /**Check if user with the given token is executor or author of task with the given Id(taskId) */
export const authAuthorOrExecutorOfTask = async(req: Request,res: Response, next:NextFunction):Promise<void | express.Response<Response>>=>{ export const authAuthorOrExecutorOfTask = async(req: Request,res: Response, next:NextFunction):Promise<void | express.Response<Response>>=>{
const token = req.get('Authorization'); const token = req.get('Authorization');
const {taskId} = req.body let taskId = null
taskId = req.body.taskId
if (req.body?.taskId) {
taskId = req.body.taskId
} else if (req.params?.taskId){
taskId = req.params.taskId
} else return res.send({vessage:"there are no taskId found"})
if(!token) return res.status(401).send({Message:'token not exists'}) if(!token) return res.status(401).send({Message:'token not exists'})
req.body={...req.body,executorStatus:false} req.body={...req.body,executorStatus:false}
...@@ -53,15 +60,51 @@ export const authAuthorOrExecutorOfTask = async(req: Request,res: Response, next ...@@ -53,15 +60,51 @@ export const authAuthorOrExecutorOfTask = async(req: Request,res: Response, next
}, },
]}) ]})
if (!task) return res.status(404).send({message:'task with possible user involved is not found'}) if (!task) return res.status(404).send({message:'task with possible user involved is not found'})
if (task?.author?.token === token ) { if(task?.executor?.token === token) {
req.body={...req.body,authorStatus:true}
} else if(task?.executor?.token === token) {
req.body={...req.body,executorStatus:true} req.body={...req.body,executorStatus:true}
} else { }
if (task?.author?.token === token ) {
req.body={...req.body,authorStatus:true}
} }
next() next()
}; };
/**Check if user with the given token is executor or author of task with the given dateTimeTaskId */
export const authAuthorOrExecutorOfDateTimeTask = async(req: Request,res: Response, next:NextFunction):Promise<void | express.Response<Response>>=>{
const token = req.get('Authorization');
let dateTimeTaskId = null
req.body={...req.body,executorStatus:false}
req.body={...req.body,authorStatus:false}
dateTimeTaskId = req.body.dateTimeTaskId
if (req.body?.dateTimeTaskId) {
dateTimeTaskId = req.body.dateTimeTaskId
} else if (req.params?.dateTimeTaskId){
dateTimeTaskId = req.params.dateTimeTaskId
} else return res.send({vessage:"there are no dateTimeTaskId found"})
const task = await dataSource
.createQueryBuilder()
.select(["task"])
.from(Task,"task")
.leftJoinAndSelect("task.executor","user")
.leftJoinAndSelect("task.dateTimeTasks","dateTimeTask")
.leftJoinAndSelect("task.author","users")
.where("dateTimeTask.id = :dateTimeTaskId", {dateTimeTaskId})
.getOne()
if (!task) return res.status(404).send({message:'task with possible user involved is not found'})
if(task?.executor?.token === token) {
req.body={...req.body,executorStatus:true}
}
if (task?.author?.token === token ) {
req.body={...req.body,authorStatus:true}
}
next()
}
/**task finder by id, return one task */ /**task finder by id, return one task */
export const taskFinderById = async (taskId:string):Promise<null | Task>=>{ export const taskFinderById = async (taskId:string):Promise<null | Task>=>{
const task = await dataSource const task = await dataSource
......
...@@ -27,7 +27,7 @@ import { User } from './User'; ...@@ -27,7 +27,7 @@ import { User } from './User';
@Column({ name: 'token', type: 'varchar',length:100, unique: true, nullable:true }) @Column({ name: 'token', type: 'varchar',length:100, unique: true, nullable:true })
token!: string; token!: string;
@OneToOne(()=>User,{nullable: false, eager:true}) @OneToOne(()=>User,{nullable: false, eager:true, onDelete:"CASCADE"})
@JoinColumn() @JoinColumn()
user!:User; user!:User;
......
...@@ -5,7 +5,7 @@ import { User } from '../models/User'; ...@@ -5,7 +5,7 @@ import { User } from '../models/User';
import { Member } from '../models/Member'; import { Member } from '../models/Member';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { DateTimeTask } from '../models/DateTimeTask'; import { DateTimeTask } from '../models/DateTimeTask';
import { auth, authAuthorOrExecutorOfTask } from '../helpers'; import { auth, authAuthorOrExecutorOfDateTimeTask, authAuthorOrExecutorOfTask } from '../helpers';
const router:Router = express.Router(); const router:Router = express.Router();
const dataSource = myDataSource; const dataSource = myDataSource;
...@@ -41,8 +41,9 @@ router.post("/make-copy", async(req:Request, res:Response):Promise<Response>=>{ ...@@ -41,8 +41,9 @@ router.post("/make-copy", async(req:Request, res:Response):Promise<Response>=>{
} ) } )
/** change date time of copy of task in calendar view */ /** change date time of copy of task in calendar view */
router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: Response):Promise<Response>=>{ router.put("/change-copy/:dateTimeTaskId", authAuthorOrExecutorOfTask, async(req:Request, res: Response):Promise<Response>=>{
const {dateTimeTaskId, taskId, dateTimeStart, dateTimeDue, description, title, priority} = req.body const {dateTimeTaskId} = req.params
const {executorStatus, taskId, dateTimeStart, dateTimeDue, description, title, priority} = req.body
const dateTimeTask = await dataSource const dateTimeTask = await dataSource
.createQueryBuilder() .createQueryBuilder()
.select('dateTimeTask') .select('dateTimeTask')
...@@ -55,7 +56,7 @@ router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: R ...@@ -55,7 +56,7 @@ router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: R
await dateTimeTask.save() await dateTimeTask.save()
const task = await taskFinderById(taskId) const task = await taskFinderById(taskId)
if (!task) return res.status(404).send({Message:'task not found'}) if (!task) return res.status(404).send({Message:'task not found'})
task.title = title; task.title = title;
task.description = description; task.description = description;
task.priority = priority; task.priority = priority;
await task.save() await task.save()
...@@ -63,15 +64,18 @@ router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: R ...@@ -63,15 +64,18 @@ router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: R
}) })
/**delete copyTask by dateTimeTaskId */ /**delete copyTask by dateTimeTaskId */
router.delete('/:id', authAuthorOrExecutorOfTask, async(req:Request, res:Response):Promise<Response>=>{ router.delete('/:dateTimeTaskId',authAuthorOrExecutorOfDateTimeTask, async(req:Request, res:Response):Promise<Response|void>=>{
const {id} = req.params const {executorStatus} = req.body
if(executorStatus){
const {dateTimeTaskId} = req.params
await myDataSource await myDataSource
.createQueryBuilder() .createQueryBuilder()
.delete() .delete()
.from(DateTimeTask) .from(DateTimeTask)
.where("id = :id", { id }) .where("id = :dateTimeTaskId", { dateTimeTaskId })
.execute() .execute()
return res.send({message:"delete succesfully"}) return res.send({message:"copyTask delete succesfully"})
}
} }
) )
......
...@@ -121,6 +121,7 @@ router.get('/user/:userId', async (req : Request, res : Response): Promise<Respo ...@@ -121,6 +121,7 @@ router.get('/user/:userId', async (req : Request, res : Response): Promise<Respo
router.post('/add-user/', async (req: Request, res: Response):Promise<Response>=>{ router.post('/add-user/', async (req: Request, res: Response):Promise<Response>=>{
const {userId, projectId, roleProject} = req.body; const {userId, projectId, roleProject} = req.body;
console.log("req body" + req.body)
const newMember:Member = new Member(); const newMember:Member = new Member();
try{ try{
newMember.user= userId; newMember.user= userId;
...@@ -128,7 +129,7 @@ router.post('/add-user/', async (req: Request, res: Response):Promise<Response>= ...@@ -128,7 +129,7 @@ router.post('/add-user/', async (req: Request, res: Response):Promise<Response>=
newMember.roleProject=roleProject newMember.roleProject=roleProject
await newMember.save() await newMember.save()
return res.send({newMember}) return res.send({newMember})
} catch(e){ } catch(e){
return res.send({message:"add user to project failed" }) return res.send({message:"add user to project failed" })
} }
......
...@@ -157,8 +157,9 @@ router.delete('/:taskId',async (req: Request, res: Response):Promise<Response>=> ...@@ -157,8 +157,9 @@ router.delete('/:taskId',async (req: Request, res: Response):Promise<Response>=>
/**change of task by task id */ /**change of task by task id */
router.put('/',authAuthorOrExecutorOfTask,async(req:Request, res:Response)=> { router.put('/:taskId',authAuthorOrExecutorOfTask,async(req:Request, res:Response)=> {
const {authorStatus,executorStatus,taskId,title,description,note, archive,project,dateTimeTaskId,dateTimeStart,dateTimeDue,executor,accomplish,dateTimeDeadLine, dateTimeFactDeadLine,priority} = req.body; const {taskId} = req.params
const {authorStatus,executorStatus,title,description,note, archive,project,dateTimeTaskId,dateTimeStart,dateTimeDue,executor,accomplish,dateTimeDeadLine, dateTimeFactDeadLine,priority} = req.body;
const task = await taskFinderById(taskId) const task = await taskFinderById(taskId)
if (!task) return res.status(404).send({Message:'task not found'}) if (!task) return res.status(404).send({Message:'task not found'})
let dateTimeTask = null; let dateTimeTask = null;
......
...@@ -100,6 +100,7 @@ router.delete('/sessions', async(req: Request, res: Response):Promise<void | obj ...@@ -100,6 +100,7 @@ router.delete('/sessions', async(req: Request, res: Response):Promise<void | obj
if(!user) return res.send({successMsg}); if(!user) return res.send({successMsg});
user.token = nanoid(); user.token = nanoid();
await user.save(); await user.save();
return res.send(successMsg)
}) })
......
...@@ -2,7 +2,7 @@ import {Routes, Route, Outlet, Navigate, BrowserRouter} from "react-router-dom"; ...@@ -2,7 +2,7 @@ import {Routes, Route, Outlet, Navigate, BrowserRouter} from "react-router-dom";
import {Container} from "@mui/material"; import {Container} from "@mui/material";
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
import AppToolbar from "./components/UI/AppToolBar/AppToolBar"; import AppToolbar from "./components/UI/AppToolBar/AppToolBar";
import MyTasks from './containers/MyTasks/MyTasks'; import MyTasks from './containers/MyTasks/MyTasks1';
import Login from './containers/Login/Login'; 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';
...@@ -10,6 +10,7 @@ import ForgottenPassword from "./containers/ForgottenPassword/ForgottenPassword" ...@@ -10,6 +10,7 @@ import ForgottenPassword from "./containers/ForgottenPassword/ForgottenPassword"
import Projects from "./containers/Projects/Projects"; import Projects from "./containers/Projects/Projects";
import FullProject from "./containers/FullProject/FullProject"; import FullProject from "./containers/FullProject/FullProject";
import NewProject from "./containers/NewProject/NewProject"; import NewProject from "./containers/NewProject/NewProject";
import WeekCalendar from "./containers/WeekCalendar/WeekCalendar";
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);
...@@ -56,7 +57,7 @@ const App = () => { ...@@ -56,7 +57,7 @@ const App = () => {
isAllowed={user} isAllowed={user}
redirectUrl={"/sign-in"} redirectUrl={"/sign-in"}
> >
<h1>week page</h1> <WeekCalendar/>
</ProtectedRoute> </ProtectedRoute>
}/> }/>
...@@ -65,7 +66,7 @@ const App = () => { ...@@ -65,7 +66,7 @@ const App = () => {
isAllowed={user} isAllowed={user}
redirectUrl={"/sign-in"} redirectUrl={"/sign-in"}
> >
<h1>week page</h1> <WeekCalendar/>
</ProtectedRoute> </ProtectedRoute>
}/> }/>
...@@ -74,7 +75,7 @@ const App = () => { ...@@ -74,7 +75,7 @@ const App = () => {
isAllowed={user} isAllowed={user}
redirectUrl={"/sign-in"} redirectUrl={"/sign-in"}
> >
<MonthCalendar>month page</MonthCalendar> <MonthCalendar></MonthCalendar>
</ProtectedRoute> </ProtectedRoute>
}/> }/>
......
import { Box, Button, Grid, Modal } from "@mui/material";
import { useState } from "react";
import { useSelector } from "react-redux";
import FormElement from "../UI/Form/FormElement/FormElement";
import { Typography } from "@mui/material";
import TextField from '@mui/material/TextField';
import Stack from '@mui/material/Stack';
import Autocomplete from '@mui/material/Autocomplete';
import { useParams } from "react-router-dom";
import PersonAddIcon from '@mui/icons-material/PersonAdd';
const style = {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 400,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
p: 4,
};
const MemberForm = ({ onSubmit }) => {
const users = useSelector(state => state.users.users)
const [role, setRole] = useState([{ role: "user" }, { role: "admin" }, { role: "watcher" }])
const params = useParams()
console.log(users)
const [open, setOpen] = useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const [state, setState] = useState({
displayName: "",
roleProject: "",
userId: "",
projectId: ""
});
const submitFormHandler = (e) => {
e.preventDefault();
let idOfUser = users?.map((user) => (user.displayName === state.displayName) ? user.id : null)
let idOfUser1 = users?.map((user) => {
console.log(user)
return null
})
console.log(idOfUser1)
console.log(idOfUser)
console.log("state of submit " + state);
let members = {roleProject: state.roleProject, projectId: params.id, userId: idOfUser }
console.log(members);
onSubmit(members);
};
// const onChange = (e) => {
// const value = e.target.value;
// const name = e.target.name;
// const newState= { ...state, [name]: value };
// console.log("newState " + newState)
// console.log("e.target " + e.target)
// console.log("e " + e)
// setState(newState);
// };
const memberChangeHandler = (e, value) => {
setState(() => { return { ...state, member: value, userId: state.userId} });
console.log("memberChangeHandler" + value)
}
const roleChangeHandler = (e, value) => {
setState(() => { return { ...state, roleProject: value } });
console.log(value)
}
return (
<div >
<PersonAddIcon onClick={handleOpen} style={{marginLeft: "30px", marginTop: "-3px"}} >Добавить участника</PersonAddIcon>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<form onSubmit={submitFormHandler}>
<Grid container direction="column" spacing={2}>
<Typography variant="h5" style={{margin: "5px", textAlign: "center"}} >Новый участник</Typography>
<Autocomplete
id="free-solo-demo"
freeSolo
options={users?.map((user) => user.displayName)}
onChange={memberChangeHandler}
name={"userId"}
value={state.userId}
renderInput={(params) => <TextField
style={{margin: "5px"}}
label={"Участник"}
state={state}
{...params} />}
/>
<Autocomplete
id="free-solo-demo"
freeSolo
options={role?.map((role) => role.role)}
value={state.roleProject}
onChange={roleChangeHandler}
renderInput={(params) => <TextField
style={{margin: "5px"}}
name={"roleProject"}
label={"Роль в проекте"}
state={state}
{...params} />}
/>
<Grid item>
<Button
type="submit"
color="primary"
variant="contained"
>
Create
</Button>
</Grid>
</Grid>
</form>
</Box>
</Modal>
</div>
);
};
export default MemberForm;
\ No newline at end of file
import {Button, Menu, MenuItem} from "@mui/material"; import {Button} from "@mui/material";
import { useState } from "react"; import {NavLink} from "react-router-dom";
import { useDispatch, useSelector } from "react-redux"; import { superuserMenuButtons } from "../../../constants";
import {NavLink, useNavigate} from "react-router-dom"; import ProfileBlock from "../ProfileBlock/ProfileBlock";
import { logoutUser } from "../../../store/actions/usersActions";
const AdminMenu = () => { const AdminMenu = () => {
const dispatch = useDispatch();
const navigate = useNavigate()
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const user = useSelector(state => state.users.user)
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const logout = () => {
dispatch(logoutUser(navigate));
handleClose()
}
return <> return <>
<Button {superuserMenuButtons.map((button, i)=>{
component={NavLink} return(
to="/projects" <Button
color="inherit" key={i}
size="large" component={NavLink}
> to={button.path}
Проекты color="inherit"
</Button> size="large"
<Button >
component={NavLink} {button.text}
to="/week" </Button>
color="inherit" )
size="large" })}
> <ProfileBlock/>
Неделя
</Button>
<Button
component={NavLink}
to="/month"
color="inherit"
size="large"
>
Месяц
</Button>
<Button
component={NavLink}
to="/my-tasks"
color="inherit"
size="large"
>
Мои задачи
</Button>
<Button
component={NavLink}
to="/workers-tasks"
color="inherit"
size="large"
>
Задачи сотрудников
</Button>
<Button
component={NavLink}
to="/sign-up"
color="inherit"
size="large"
>
Создать сотрудника
</Button>
<Button
color="inherit"
onClick={handleClick}
>
Hello, {user?.displayName}
</Button>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem component={NavLink} to="/profile/test" color="inherit" onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={logout}>Logout</MenuItem>
</Menu>
</> </>
}; };
......
import {Button} from "@mui/material"; import {Button} from "@mui/material";
import {NavLink} from "react-router-dom"; import {NavLink} from "react-router-dom";
import { anonymoysMenuButtons } from "../../../constants";
const AnonymousMenu = () => { const AnonymousMenu = () => {
return <> return <>
<Button {anonymoysMenuButtons.map((button, i)=>{
component={NavLink} return(
color="inherit" <Button
to="/sign-in" key={i}
> component={NavLink}
Вход to={button.path}
</Button> color="inherit"
size="large"
>
{button.text}
</Button>
)
})}
</> </>
}; };
......
import {Button, Menu, MenuItem} from "@mui/material";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {NavLink, useNavigate} from "react-router-dom";
import { logoutUser } from "../../../store/actions/usersActions";
const ProfileBlock = () => {
const dispatch = useDispatch();
const navigate = useNavigate()
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const user = useSelector(state => state.users.user)
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const logout = () => {
dispatch(logoutUser(navigate));
handleClose()
}
return <>
<Button
color="inherit"
onClick={handleClick}
id='test_greetings'
>
Hello, {user?.displayName}
</Button>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem component={NavLink} to="/profile/test" color="inherit" onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={()=>{logout()}}>Logout</MenuItem>
</Menu>
</>
};
export default ProfileBlock;
\ No newline at end of file
import {Button, Menu, MenuItem} from "@mui/material"; import {Button} from "@mui/material";
import { useState } from "react"; import {NavLink} from "react-router-dom";
import { useDispatch, useSelector } from "react-redux"; import { workerMenuButtons } from "../../../constants";
import {NavLink, useNavigate} from "react-router-dom"; import ProfileBlock from "../ProfileBlock/ProfileBlock";
import { logoutUser } from "../../../store/actions/usersActions";
const WorkerMenu = () => { const WorkerMenu = () => {
const dispatch = useDispatch();
const navigate = useNavigate()
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const user = useSelector(state => state.users.user)
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const logout = () => {
dispatch(logoutUser(navigate));
handleClose()
}
return <> return <>
<Button {workerMenuButtons.map((button, i)=>{
component={NavLink} return(
to="/projects" <Button
color="inherit" key={i}
size="large" component={NavLink}
> to={button.path}
Проекты color="inherit"
</Button> size="large"
<Button >
component={NavLink} {button.text}
to="/week" </Button>
color="inherit" )
size="large" })}
>
Неделя <ProfileBlock/>
</Button>
<Button
component={NavLink}
to="/month"
color="inherit"
size="large"
>
Месяц
</Button>
<Button
component={NavLink}
to="/my-tasks"
color="inherit"
size="large"
>
Мои задачи
</Button>
<Button
color="inherit"
onClick={handleClick}
>
Hello, {user?.displayName}
</Button>
<Menu
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
<MenuItem component={NavLink} to="/profile/test" color="inherit" onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={logout}>Logout</MenuItem>
</Menu>
</> </>
}; };
......
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
const CalendarRow = ({children}) => { const CalendarRow = ({children, week}) => {
return <> return <>
<Grid <Grid
container container
align='center' align='center'
sx={{borderBottom: '1px solid black', borderRight: '1px solid black', borderLeft: '1px solid black'}} sx={{borderBottom: week ? null : '1px solid black', borderRight: '1px solid black', borderLeft: '1px solid black'}}
> >
{children} {children}
</Grid> </Grid>
......
...@@ -34,14 +34,15 @@ const EmptyBox = ({hourNumber, handleOpen, dayNumber, xs, dragTaskHandler, modal ...@@ -34,14 +34,15 @@ const EmptyBox = ({hourNumber, handleOpen, dayNumber, xs, dragTaskHandler, modal
onDragOver={(e)=>{dragOverHandler(e)}} onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}} onDrop={(e)=>{dropHandler(e)}}
onClick={(e)=>{onClickHandler(e, dayNumber, hourNumber)}} onClick={(e)=>{onClickHandler(e, dayNumber, hourNumber)}}
className='test_empty_box'
item xs={xs} sx={{ item xs={xs} sx={{
height: '35px', height: '40px',
backgroundColor: 'rgb(0,0,0,0)', backgroundColor: 'rgb(0,0,0,0)',
zIndex: '6', zIndex: '6',
cursor: copyTask ? 'pointer' : 'default' cursor: copyTask ? 'pointer' : 'default'
}}> }}>
{isThisCell ? {isThisCell ?
<DefaultTask/> : null} <DefaultTask/> : ' '}
</Grid>) </Grid>)
}; };
......
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { memo } from "react"; import { memo } from "react";
const CalendarSmallCell = ({children, xs}) => { const CalendarSmallCell = ({children, xs, week}) => {
return <> return <>
<Grid align='center' item xs={xs} sx={{borderRight: '1px solid black'}}> <Grid align='center' item xs={xs} sx={{borderRight: week ? null : '1px solid black', height: week ? '40px' : null, borderBottom: week ? '1px solid black' : null,}}>
{children} {children}
</Grid> </Grid>
</> </>
......
...@@ -4,13 +4,16 @@ import DefaultTask from "../DefaultTask/DefaultTask"; ...@@ -4,13 +4,16 @@ import DefaultTask from "../DefaultTask/DefaultTask";
const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, dragTaskHandler, linesInDay}) => { const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, dragTaskHandler, linesInDay, week}) => {
const [isThisCell, setIsThisCell] = useState(false) const [isThisCell, setIsThisCell] = useState(false)
const cellClass = { const cellClass = {
position: 'relative', position: 'relative',
height: linesInDay?.length ? `${40*linesInDay.length+35}px` : `${35}px`, height: linesInDay?.length ? `${40*linesInDay.length+35}px` : `${40}px`,
borderRight: '1px solid black', borderRight: '1px solid black',
borderBottom: week ?'1px solid black' : null,
} }
useEffect(()=>{ useEffect(()=>{
if(!modal) { if(!modal) {
setIsThisCell(false); setIsThisCell(false);
...@@ -27,7 +30,7 @@ const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCell ...@@ -27,7 +30,7 @@ const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCell
dragTaskHandler(dayNumber, parseInt(hours.split(':')[0])) dragTaskHandler(dayNumber, parseInt(hours.split(':')[0]))
} }
const onClickHandler = (e) => { const onClickHandler = (e) => {
if (!linesInDay?.length) { if (linesInDay?.length) {
createTaskInCellHandler(dayNumber, hours); createTaskInCellHandler(dayNumber, hours);
setIsThisCell(true); setIsThisCell(true);
handleOpen(e) handleOpen(e)
......
...@@ -3,7 +3,7 @@ import { useState } from "react"; ...@@ -3,7 +3,7 @@ import { useState } from "react";
import CalendarRow from "./CalendarRow/CalendarRow"; import CalendarRow from "./CalendarRow/CalendarRow";
import CalendarSmallCell from "./CalendarSmallCell/CalendarSmallCell"; import CalendarSmallCell from "./CalendarSmallCell/CalendarSmallCell";
import CalendarStandartCell from "./CalendarStandartCell.js/CalendarStandartCell"; import CalendarStandartCell from "./CalendarStandartCell.js/CalendarStandartCell";
import ModalTask from "../UI/ModalTask/ModalTask"; import ModalTask from "../../UI/ModalTask/ModalTask";
import MonthCalendarModalContent from "../MonthCalendarModalContent/MonthCalendarModalContent"; import MonthCalendarModalContent from "../MonthCalendarModalContent/MonthCalendarModalContent";
import CalendarRowDay from "./CalendarRowDay/CalendarRowDay"; import CalendarRowDay from "./CalendarRowDay/CalendarRowDay";
......
import { Box, Typography } from '@mui/material'; import { Box, Typography } from '@mui/material';
import { memo } from 'react'; import { memo } from 'react';
import MonthDecrementButton from './MonthDecrementButton/MonthDecrementButton'; import ArrowDecrementButton from '../../../UI/ArrowDecrementButton/ArrowDecrementButton';
import MonthIncrementButton from './MonthIncrementButton/MonthIncrementButton'; import ArrowIncrementButton from '../../../UI/ArrowIncrementButton/ArrowIncrementButton';
function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMonth}) { function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMonth}) {
return ( return (
...@@ -14,9 +14,10 @@ function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMo ...@@ -14,9 +14,10 @@ function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMo
alignItems: 'center', alignItems: 'center',
gap: '10px' gap: '10px'
}} }}
id='test_month_info'
> >
<MonthDecrementButton <ArrowDecrementButton
decrementMonth={decrementMonth} onClick={decrementMonth}
/> />
<Box sx={{ flexBasis: '150px' }}> <Box sx={{ flexBasis: '150px' }}>
<Typography <Typography
...@@ -30,8 +31,8 @@ function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMo ...@@ -30,8 +31,8 @@ function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMo
</Typography> </Typography>
<Typography align='center'>{year}</Typography> <Typography align='center'>{year}</Typography>
</Box> </Box>
<MonthIncrementButton <ArrowIncrementButton
incrementMonth={incrementMonth} onClick={incrementMonth}
/> />
</Box> </Box>
</> ); </> );
......
import { AppBar, Toolbar} from '@mui/material'; import { AppBar, Toolbar} from '@mui/material';
import { Box } from '@mui/system'; import { Box } from '@mui/system';
import MonthAndYearInfo from './MonthAndYearInfo/MonthAndYearInfo'; import MonthAndYearInfo from './MonthAndYearInfo/MonthAndYearInfo';
import СustomSelect from '../UI/СustomSelect/СustomSelect' import СustomSelect from '../../UI/СustomSelect/СustomSelect'
const workers = [{value: '', text: '--выберите сотрудника--'}, {value: 'Василий', text: 'Василий'}, {value: 'Никита', text: 'Никита'}] const workers = [{value: '', text: '--выберите сотрудника--'}, {value: 'Василий', text: 'Василий'}, {value: 'Никита', text: 'Никита'}]
const types = [{value: 'Месяц', text: 'Месяц'}, {value: 'Неделя', text: 'Неделя'}] const types = [{value: 'Месяц', text: 'Месяц'}, {value: 'Неделя', text: 'Неделя'}]
......
import { Button, TextField } from "@mui/material"; import { Button, TextField } from "@mui/material";
import { memo } from "react"; import { memo } from "react";
import CustomSelect from '../UI/СustomSelect/СustomSelect' import CustomSelect from '../../UI/СustomSelect/СustomSelect'
const priorities = [{value: null, text: '--Приоритет--'}, {value: 'A', text: 'A'}, {value: 'B', text: 'B'}, {value: 'C', text: 'C'}] const priorities = [{value: null, text: '--Приоритет--'}, {value: 'A', text: 'A'}, {value: 'B', text: 'B'}, {value: 'C', text: 'C'}]
...@@ -56,7 +56,7 @@ function MonthCalendarModalContent({title, onChangeCurrentTaskHandler, descripti ...@@ -56,7 +56,7 @@ function MonthCalendarModalContent({title, onChangeCurrentTaskHandler, descripti
/> />
</div> </div>
<div style={{display: 'flex', gap: '20px', margin: '10px 0'}}> <div style={{display: 'flex', gap: '20px', margin: '10px 0'}}>
<Button onClick={sendNewTaskHandler}>Сохранить</Button> <Button id='test_button_save_task'onClick={sendNewTaskHandler}>Сохранить</Button>
<Button onClick={deleteTaskHandler}>Удалить</Button> <Button onClick={deleteTaskHandler}>Удалить</Button>
</div> </div>
</>); </>);
......
import * as React from "react";
import TableCell from "@mui/material/TableCell";
import Input from "@mui/material/Input";
import moment from "moment";
const CustomTableCell = ({
task,
name,
value,
value2,
onChange,
onModalOpen,
placeholder,
user,
width,
}) => {
const styles = { width: width, padding: "1px 16px 1px 1px" };
const inputStyles = placeholder
? { width: "100%", padding: "1px" }
: { padding: "1px" };
const divStyle = {
display: "flex",
justifyContent: "space-between",
flexDirection: "column",
fontSize: "12px",
};
const duration = moment.duration(
moment(task?.dateTimeTasks[0]?.dateTimeDue).diff(
moment(task?.dateTimeTasks[0]?.dateTimeStart)
)
);
const hours = Math.round(duration.asHours());
if (task) {
return (
<>
<TableCell
onClick={(e) => (onModalOpen ? onModalOpen(e, task) : null)}
align="left"
style={styles}
>
{(task.isEditMode &&
onChange &&
name !== "author" &&
task.author?.id === user?.id) ||
placeholder ? (
<Input
placeholder={placeholder}
value={value}
name={name}
onChange={(e) => onChange(e, task)}
style={inputStyles}
/>
) : name !== "dateTimeStart" ? (
<span>{value}</span>
) : (
<div style={divStyle}>
<span>{value}</span>
<span>{value2}</span>
<span>часы:{hours}</span>
</div>
)}
</TableCell>
</>
);
}
};
export default CustomTableCell;
import * as React from "react";
import {
Table,
TableBody,
TableCell,
TableContainer,
TableRow,
TableHead,
IconButton,
Tooltip,
Typography,
Divider,
} from "@mui/material";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AddBox } from "@mui/icons-material";
import moment from "moment";
import CustomTableCell from "./CustomTableCell";
import MaterialUIPickers from "./DateTimePicker/DateTimePicker";
import BasicSelect from "../UI/Select/Select";
import { addTask } from "../../store/actions/tasksActions";
import TaskModal from "./TaskModal/TaskModal";
export default function NewTaskForm({ projects, setAddTaskForm }) {
const dispatch = useDispatch();
const user = useSelector((state) => state.users.user);
const currentDateTime = new Date();
const dateTime = moment(currentDateTime).utc().format();
const [task, setTask] = useState({
id: 0,
title: "",
description: "",
createdAt: dateTime,
dateTimeStart: null,
dateTimeDeadLine: null,
dateTimeTasks: [],
dateTimeDue: null,
project: projects[0],
accomplish: "opened",
priority: "B",
author: { id: user.id },
authorDisplayName: user.displayName,
executors: [],
isEditMode: true,
});
const [modal, setModal] = useState(false);
const onModalOpen = (event, task) => {
event.stopPropagation();
setModal(true);
};
const handleClose = () => {
setModal(false);
};
const onChange = (e, task) => {
const value = e.target.value;
const name = e.target.name;
const newTask = { ...task, [name]: value };
setTask(newTask);
};
const onProjectChange = (e, task) => {
const value = e.target.value;
const project = projects.find((e) => e.id === value);
const newTask = { ...task };
newTask.project = project;
setTask(newTask);
};
const onDateChange = (id, value, property) => {
const newTask = {
...task,
[property]: moment.parseZone(value, "DD/MM/YYYY", true).format(),
};
setTask(newTask);
};
const handleAddTask = () => {
dispatch(addTask(task));
setAddTaskForm();
};
return (
<>
<Divider>
<Typography variant="overline">Добавить новую задачу</Typography>
</Divider>
<TableContainer
style={{
backgroundColor: "#E8E8E8",
marginBottom: "2em",
}}
>
<Table sx={{ minWidth: 600 }} aria-labelledby="tableTitle">
<TableHead>
<TableRow>
{header.map((headCell) => (
<TableCell
key={headCell.id}
align="left"
padding={headCell.disablePadding ? "none" : "normal"}
style={{ paddingLeft: "0" }}
>
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
<TableRow hover key={task.id} style={{ paddingLeft: "40px" }}>
<TableCell style={{ width: "40px", padding: "0" }}></TableCell>
<TableCell style={{ width: "8%", paddingLeft: "0" }}>
<BasicSelect
items={[
{ value: "A", title: "A" },
{ value: "B", title: "B" },
{ value: "C", title: "C" },
]}
task={task}
value={task.priority}
onChange={onChange}
name="priority"
/>
</TableCell>
<CustomTableCell
{...{
task,
name: "createdAt",
value: moment(task.createdAt).format("DD-MM-YYYY hh:mm A"),
width: "15%",
}}
/>
<CustomTableCell
{...{
task,
name: "title",
value: task.title,
onModalOpen,
user: user,
placeholder: "Кликните для ввода информации по задаче",
width: "auto",
}}
/>
<TableCell style={{ width: "15%", paddingLeft: "0" }}>
<BasicSelect
items={projects.map((e) => ({
value: e?.id,
title: e?.title,
}))}
task={task}
onChange={onProjectChange}
name="project"
value={task.project?.id}
/>
</TableCell>
{/* <TableCell>
<MaterialUIPickers
task={task}
name="dateTimeStart"
onChange={onDateChange}
/>
</TableCell> */}
<TableCell style={{ width: "20%", paddingLeft: "0" }}>
<MaterialUIPickers
task={task}
name="dateTimeDeadLine"
onChange={onDateChange}
/>
</TableCell>
<TableCell style={{ width: "5%" }}>
<Tooltip title="Добавить">
<IconButton size="large" onClick={handleAddTask}>
<AddBox fontSize="large" />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
</TableBody>
</Table>
<TaskModal
task={task}
open={modal}
handleClose={handleClose}
onChange={onChange}
user={user}
/>
</TableContainer>
<Divider />
</>
);
}
export const header = [
{
id: "id",
numeric: true,
disablePadding: true,
label: "",
},
{
id: "priority",
numeric: false,
disablePadding: true,
label: "Приоритет",
},
{
id: "createdAt",
numeric: true,
disablePadding: false,
label: "Дата создания",
},
{
id: "title",
numeric: false,
disablePadding: false,
label: "Заголовок",
},
{
id: "projectName",
numeric: true,
disablePadding: false,
label: "Проект",
},
{
id: "dateTimeDue",
numeric: true,
disablePadding: false,
label: "Дедлайн",
},
{
id: "add",
numeric: false,
disablePadding: false,
label: "",
},
];
...@@ -45,7 +45,7 @@ const ProjectForm = ({onSubmit}) => { ...@@ -45,7 +45,7 @@ const ProjectForm = ({onSubmit}) => {
return ( return (
<div > <div >
<Button onClick={handleOpen} >Add project</Button> <Button onClick={handleOpen} >Добавить проект</Button>
<Modal <Modal
open={open} open={open}
onClose={handleClose} onClose={handleClose}
...@@ -56,7 +56,7 @@ const ProjectForm = ({onSubmit}) => { ...@@ -56,7 +56,7 @@ const ProjectForm = ({onSubmit}) => {
<Box sx={style}> <Box sx={style}>
<form > <form >
<Grid container direction="column" spacing={2}> <Grid container direction="column" spacing={2}>
<Typography variant="h4">New project</Typography> <Typography variant="h4">Новый проект</Typography>
<FormElement <FormElement
onChange={inputChangeHandler} onChange={inputChangeHandler}
name={"title"} name={"title"}
...@@ -70,7 +70,7 @@ const ProjectForm = ({onSubmit}) => { ...@@ -70,7 +70,7 @@ const ProjectForm = ({onSubmit}) => {
variant="contained" variant="contained"
> >
Create Создать
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>
......
...@@ -7,12 +7,12 @@ import { useDispatch, useSelector } from "react-redux"; ...@@ -7,12 +7,12 @@ import { useDispatch, useSelector } from "react-redux";
const ProjectItem = ({ title, tasks, id }) => { const ProjectItem = ({ title, tasks, id }) => {
const user = useSelector(state => state.users.user); const user = useSelector(state => state.users.user);
const dispatch = useDispatch(); const dispatch = useDispatch();
console.log(tasks) console.log(user)
return <> return <>
<Grid item xs={12} sm={12} md={6} lg={4}> <Grid item xs={12} sm={12} md={6} lg={4}>
<Card> <Card>
<CardContent> <CardContent >
<strong> <strong>
<br></br> <br></br>
Название проекта: {title} Название проекта: {title}
......
import { Card, CardActions, CardContent, Grid, IconButton } from "@mui/material"; import { Card, CardActions, CardContent, Grid, IconButton, Tooltip } from "@mui/material";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import DeleteIcon from "@mui/icons-material/Delete";
const ProjectMembersItem = ({ displayName, roleProject, id, roleProjectOfAuthor }) => {
const ProjectMembersItem = ({ displayName, id }) => { console.log(displayName)
const dispatch = useDispatch(); const dispatch = useDispatch();
const user = useSelector(state => state.users)
console.log(user)
const { projects, project } = useSelector(state => state.projects); const { projects, project } = useSelector(state => state.projects);
return <> return <>
<Grid item xs={12} sm={12} md={6} lg={4}> <Grid item xs={12} sm={12} md={6} lg={4}>
<Card> <Card>
<CardContent> <CardContent>
<strong> <strong>
<br></br> <br></br>
{displayName} {displayName}
</strong> </strong>
<strong> <strong>
<br></br> <br></br>
роль: {project?.project?.members[0]?.roleProject} роль: {roleProject}
</strong> </strong>
<strong> <strong>
<br></br>
<button>delete</button> {roleProjectOfAuthor === "admin" ?
</strong> (<strong>
</CardContent> <Tooltip title="Удалить">
</Card> <IconButton
</Grid> onClick={(id) => {
// deleteHandle(task.id);
}}
>
<DeleteIcon style={{ marginTop: "-5px" }} />
</IconButton>
</Tooltip>
</strong>) : null}
</strong>
</CardContent>
</Card>
</Grid>
</> </>
}; };
......
import {Grid} from "@mui/material"; import {Grid} from "@mui/material";
import ProjectMembersItem from "../ProjectMembersItem/ProjectMembersItem"; import ProjectMembersItem from "../ProjectMembersItem/ProjectMembersItem";
const ProjectMembersList = ({users}) => { const ProjectMembersList = ({members, roleProjectOfAuthor}) => {
console.log(users) console.log("members ", members)
return ( return (
<Grid item container direction="row" spacing={1}> <Grid item container direction="column" spacing={1}>
{users?.map(user => { {members?.map(member => {
return <ProjectMembersItem return <ProjectMembersItem
displayName={user.displayName} displayName={member?.user?.displayName}
id={user.id} roleProject={member?.roleProject}
key={user.id} id={member.id}
key={member.id}
roleProjectOfAuthor={roleProjectOfAuthor}
/> />
})} })}
</Grid> </Grid>
......
...@@ -2,8 +2,9 @@ import {Grid} from "@mui/material"; ...@@ -2,8 +2,9 @@ import {Grid} from "@mui/material";
import ProjectItem from "../ProjectItem/ProjectItem"; import ProjectItem from "../ProjectItem/ProjectItem";
const ProjectsList = ({projects}) => { const ProjectsList = ({projects}) => {
console.log(projects)
return ( return (
<Grid item container direction="row" spacing={1}> <Grid item container direction="column" spacing={1}>
{projects?.map(project => { {projects?.map(project => {
return <ProjectItem return <ProjectItem
tasks={project.tasks} tasks={project.tasks}
......
...@@ -13,15 +13,15 @@ const arrowClass = { ...@@ -13,15 +13,15 @@ const arrowClass = {
} }
function MonthDecrementButton({decrementMonth}) { function ArrowDecrementButton({onClick}) {
return ( return (
<> <>
<ArrowBackIcon <ArrowBackIcon
sx={arrowClass} sx={arrowClass}
onClick={decrementMonth} onClick={onClick}
/> />
</> ); </> );
} }
export default memo(MonthDecrementButton); export default memo(ArrowDecrementButton);
\ No newline at end of file \ No newline at end of file
...@@ -13,15 +13,15 @@ const arrowClass = { ...@@ -13,15 +13,15 @@ const arrowClass = {
} }
function MonthIncrementButton({incrementMonth}) { function ArrowIncrementButton({onClick}) {
return ( return (
<> <>
<ArrowForwardIcon <ArrowForwardIcon
sx={arrowClass} sx={arrowClass}
onClick={incrementMonth} onClick={onClick}
/> />
</> ); </> );
} }
export default memo(MonthIncrementButton); export default memo(ArrowIncrementButton);
\ No newline at end of file \ No newline at end of file
import { Grid, TextField, MenuItem } from "@mui/material"; import { Grid, TextField, MenuItem } from "@mui/material";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
const FormElement = ({ name, label, state, error, onChange, select, options, type = "'text" }) => { const FormElement = ({ name, label, state, error, onChange, select, options, type = "'text", users}) => {
let inputChildren = null let inputChildren = null
if (select) { if (select) {
...@@ -19,6 +19,7 @@ const FormElement = ({ name, label, state, error, onChange, select, options, typ ...@@ -19,6 +19,7 @@ const FormElement = ({ name, label, state, error, onChange, select, options, typ
type={type} type={type}
variant="outlined" variant="outlined"
value={state?.[name]} value={state?.[name]}
users={users}
onChange={onChange} onChange={onChange}
error={!!error} error={!!error}
helperText={error} helperText={error}
......
...@@ -56,6 +56,7 @@ export default function ModalTask({modal, handleClose, children}) { ...@@ -56,6 +56,7 @@ export default function ModalTask({modal, handleClose, children}) {
return ( return (
<> <>
<Modal <Modal
id='test_modal_task'
open={modal.open} open={modal.open}
onClose={handleClose} onClose={handleClose}
aria-labelledby="modal-modal-title" aria-labelledby="modal-modal-title"
......
...@@ -25,6 +25,7 @@ const UserForm = ({ state, onChange, onSubmit, getFieldError, buttonText, resetP ...@@ -25,6 +25,7 @@ const UserForm = ({ state, onChange, onSubmit, getFieldError, buttonText, resetP
sx={{ mt: "15px" }} sx={{ mt: "15px" }}
type="submit" type="submit"
fullWidth fullWidth
id='test_login'
variant="contained" variant="contained"
color="primary" color="primary"
> >
......
export const getCurrentWeekDayString = (dayNumber) => {
if (dayNumber <= 6 && dayNumber >= 0) {
return ["ПН","ВТ","СР","ЧТ","ПТ","СБ","ВС"][dayNumber];
} else {
return null
}
}
\ No newline at end of file
import { FormControlLabel, Grid, Switch } from "@mui/material";
import { Box } from "@mui/system";
import CalendarRow from "../../MonthCalendar/MonthCalendarBody/CalendarRow/CalendarRow";
import CalendarSmallCell from "../../MonthCalendar/MonthCalendarBody/CalendarSmallCell/CalendarSmallCell";
import CalendarStandartCell from "../../MonthCalendar/MonthCalendarBody/CalendarStandartCell.js/CalendarStandartCell";
import { getCurrentWeekDayString } from "./Helpers";
function WeekCalendarBody({week, hoursInDay, hourFormat, setHourFormat}) {
return (
<>
<Box style={{marginBottom: '30px'}}>
<Box style={{position: 'sticky', top: '0px', zIndex: '10', backgroundColor: 'lightgrey'}}>
<CalendarRow
>
<CalendarSmallCell xs={1}>
<FormControlLabel
control={<Switch color="primary" checked={hourFormat} onChange={()=>{setHourFormat(()=>!hourFormat)}}/>}
label="1 час"
labelPlacement="end"
/>
</CalendarSmallCell>
{week?.map((weekDay, i)=>{
return (
<CalendarStandartCell key={i} xs={11/week.length}>
<span style={{display: 'block', fontWeight: "600"}}>{weekDay}</span>
<span style={{marginBottom: '10px'}}>{getCurrentWeekDayString(i)}</span>
</CalendarStandartCell>
)
})}
</CalendarRow>
</Box>
<Grid container>
<CalendarRow week={true}>
<Grid item xs={0.995} flexDirection='column'>
{hoursInDay?.map((hour, i)=>{
return (
<CalendarSmallCell key={i} week={true}>
{hour}
</CalendarSmallCell>)
})}
</Grid>
<Grid item xs={11.005}>
<CalendarRow week={true}>
{week?.map((weekDay, i)=>{
return (
<Grid item key={i} xs={12/week.length}>
{hoursInDay?.map((hour, i)=>{
return (
<CalendarStandartCell key={i} week={true}>
</CalendarStandartCell>)
})}
</Grid>)
})}
</CalendarRow>
</Grid>
</CalendarRow>
</Grid>
</Box>
</>
);
}
export default WeekCalendarBody;
\ No newline at end of file
import { AppBar, Toolbar} from '@mui/material';
import { Box } from '@mui/system';
import WeekCalendarHeaderInfo from './WeekCalendarHeaderInfo/WeekCalendarHeaderInfo';
function WeekCalendarHeader({decrementWeek, incrementWeek, weekInfo}) {
return (
<>
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<WeekCalendarHeaderInfo
decrementWeek={decrementWeek}
incrementWeek={incrementWeek}
weekInfo={weekInfo}
/>
</Toolbar>
</AppBar>
</Box>
</>
);
}
export default WeekCalendarHeader;
\ No newline at end of file
import ArrowDecrementButton from '../../../UI/ArrowDecrementButton/ArrowDecrementButton';
import ArrowIncrementButton from '../../../UI/ArrowIncrementButton/ArrowIncrementButton';
import { Box } from '@mui/system';
import {Typography} from '@mui/material';
function WeekCalendarHeaderInfo({decrementWeek, incrementWeek, weekInfo}) {
return (
<>
<Box sx={{width: '40%', marginBottom: '15px'}}>
<h2>Цель недели: Наладить режим сна</h2>
<Box sx={{display: 'flex', alignItems: 'center'}}>
<ArrowDecrementButton
onClick={()=>{decrementWeek()}}
/>
<Typography
variant="h6"
sx={{
flexBasis: '250px',
display: 'flex',
justifyContent: 'center',
}}
>
{weekInfo}
</Typography>
<ArrowIncrementButton
onClick={()=>{incrementWeek()}}
/>
</Box>
</Box>
</>
);
}
export default WeekCalendarHeaderInfo;
\ No newline at end of file
export const apiUrl = "http://localhost:8000"; export const apiUrl = "http://localhost:8000";
export const uploadsUrl = `${apiUrl}/uploads`; export const uploadsUrl = `${apiUrl}/uploads`;
export const workerMenuButtons = [
{text: 'Проекты', path: '/projects'},
{text: 'Неделя', path: '/week'},
{text: 'Месяц', path: '/month'},
{text: 'Мои задачи', path: '/my-tasks'}
]
export const superuserMenuButtons = [
{text: 'Проекты', path: '/projects'},
{text: 'Неделя', path: '/week'},
{text: 'Месяц', path: '/month'},
{text: 'Мои задачи', path: '/my-tasks'},
{text: 'Задачи Сотрудников', path: '/workers-tasks'},
{text: 'Создать Сотрудника', path: '/sign-up'}
]
export const anonymoysMenuButtons = [
{text: 'Вход', path: '/sign-in'},
]
\ No newline at end of file
...@@ -6,13 +6,14 @@ import { fetchProject } from "../../store/actions/projectsActions"; ...@@ -6,13 +6,14 @@ import { fetchProject } from "../../store/actions/projectsActions";
import ProjectTasksBody from "../../components/ProjectTasks/ProjectTasksBody"; import ProjectTasksBody from "../../components/ProjectTasks/ProjectTasksBody";
import { fetchUsers } from "../../store/actions/usersActions"; import { fetchUsers } from "../../store/actions/usersActions";
import ProjectMembersList from "../../components/ProjectMembersList/ProjectMembersList"; import ProjectMembersList from "../../components/ProjectMembersList/ProjectMembersList";
import NewMember from "../NewMember/NewMember";
const FullProject = () => { const FullProject = () => {
const { projects, project } = useSelector(state => state.projects); const { projects, project } = useSelector(state => state.projects);
const users = useSelector(state => state.users.users); const users = useSelector(state => state.users.users);
const dispatch = useDispatch(); const dispatch = useDispatch();
const params = useParams() const params = useParams()
const tasks = project.tasks; const tasks = project.tasks;
...@@ -22,13 +23,13 @@ const FullProject = () => { ...@@ -22,13 +23,13 @@ const FullProject = () => {
useEffect(() => { useEffect(() => {
dispatch(fetchUsers()) dispatch(fetchUsers())
}, [dispatch]); }, [dispatch]);
console.log(users) console.log(users)
useEffect(() => { useEffect(() => {
dispatch(fetchProject(params.id)) dispatch(fetchProject(params.id))
}, [params.id, dispatch]); }, [params.id, dispatch]);
console.log(project); console.log("project ", project);
return <> return <>
<Grid item xs={12} sm={12} md={6} lg={4}> <Grid item xs={12} sm={12} md={6} lg={4}>
...@@ -53,9 +54,13 @@ const FullProject = () => { ...@@ -53,9 +54,13 @@ const FullProject = () => {
</strong> </strong>
<strong> <strong>
<br></br> <br></br>
<div style={{display: 'flex', direction: 'column'}}>
Участники проекта: Участники проекта:
<NewMember members={project?.project?.members} />
</div>
<ProjectMembersList users={users} project={project}/> <ProjectMembersList users={users} project={project} members={project?.project?.members} roleProjectOfAuthor={project?.project?.members[0]?.roleProject} />
</strong> </strong>
<strong> <strong>
<br></br> <br></br>
......
import { useEffect, useCallback, useState, useMemo } from 'react'; import { useEffect, useCallback, useState, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import MonthCalendarBody from '../../components/MonthCalendarBody/MonthCalendarBody'; import MonthCalendarBody from '../../components/MonthCalendar/MonthCalendarBody/MonthCalendarBody';
import MonthCalendarHeader from '../../components/MonthCalendarHeader/MonthCalendarHeader'; import MonthCalendarHeader from '../../components/MonthCalendar/MonthCalendarHeader/MonthCalendarHeader';
import { dateToISOLikeButLocal, getCurrentMonthString, getDaysInMonth } from '../../helpers/CalendarHelpers'; import { dateToISOLikeButLocal, getCurrentMonthString, getDaysInMonth } from '../../helpers/CalendarHelpers';
import { addCalendarTask, addCopyCalendarTask, deleteCalendarTask, editCalendarTask, fetchCalendarTasks} from '../../store/actions/tasksActions'; import { addCalendarTask, addCopyCalendarTask, deleteCalendarTask, editCalendarTask, fetchCalendarTasks} from '../../store/actions/tasksActions';
...@@ -47,8 +47,8 @@ function MonthCalendar() { ...@@ -47,8 +47,8 @@ function MonthCalendar() {
}, [dateNow]) }, [dateNow])
const currentMonthString = useMemo(() => { const currentMonthString = useMemo(() => {
return getCurrentMonthString(dateNow) return getCurrentMonthString(dateNow.month)
}, [dateNow]) }, [dateNow.month])
const onChangeWorkerHandler = useCallback((event) => { const onChangeWorkerHandler = useCallback((event) => {
setWorker(event.target.value); setWorker(event.target.value);
...@@ -158,18 +158,17 @@ function MonthCalendar() { ...@@ -158,18 +158,17 @@ function MonthCalendar() {
...currentTask, ...currentTask,
dateTimeStart: start, dateTimeStart: start,
dateTimeDue: due, dateTimeDue: due,
dateTimeTaskId: currentTask.id,
taskId: currentTask.mainTaskId taskId: currentTask.mainTaskId
} }
delete newTask.infoForCell delete newTask.infoForCell
delete newTask.id await dispatch(editCalendarTask(newTask, currentTask.id))
await dispatch(editCalendarTask(newTask))
} else { } else {
const newTask = { const newTask = {
...currentTask, ...currentTask,
dateTimeStart: start, dateTimeStart: start,
dateTimeDue: due, dateTimeDue: due,
executor: user, executor: user,
dateTimeDeadLine: due,
} }
delete newTask.infoForCell delete newTask.infoForCell
delete newTask.id delete newTask.id
......
This diff is collapsed.
import * as React from "react";
import PropTypes from "prop-types";
import Box from "@mui/material/Box";
import TableCell from "@mui/material/TableCell";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import TableSortLabel from "@mui/material/TableSortLabel";
import { visuallyHidden } from "@mui/utils";
const headCells = [
{
id: "id",
numeric: true,
disablePadding: true,
label: "",
canSort: true,
},
{
id: "priority",
numeric: false,
disablePadding: true,
label: "Приоритет",
canSort: true,
},
{
id: "createdAt",
numeric: true,
disablePadding: false,
label: "Дата создания",
canSort: true,
},
{
id: "title",
numeric: true,
disablePadding: false,
label: "Заголовок",
canSort: true,
},
{
id: "projectName",
numeric: true,
disablePadding: false,
label: "Проект",
canSort: true,
},
{
id: "authorDisplayName",
numeric: true,
disablePadding: false,
label: "Автор",
canSort: true,
},
{
id: "dateTimeStart",
numeric: true,
disablePadding: false,
label: "Дата и время выполнения",
canSort: false,
},
{
id: "dateTimeDeadLine",
numeric: true,
disablePadding: false,
label: "Дедлайн",
canSort: true,
},
{
id: "accomplish",
numeric: true,
disablePadding: false,
label: "Статус",
canSort: true,
},
{
id: "change",
numeric: false,
disablePadding: false,
label: "",
canSort: false,
},
{
id: "delete",
numeric: false,
disablePadding: false,
label: "",
canSort: false,
},
];
export default function EnhancedTableHead({
order,
orderBy,
rowCount,
onRequestSort,
}) {
return (
<TableHead>
<TableRow>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={"left"}
padding={headCell.disablePadding ? "none" : "normal"}
sortDirection={
headCell.canSort && orderBy === headCell.id ? order : false
}
style={{ paddingLeft: "0" }}
>
{headCell.canSort ? (
<TableSortLabel
active={orderBy === headCell.id}
direction={orderBy === headCell.id ? order : "asc"}
onClick={() => onRequestSort(headCell.id)}
>
{headCell.label}
{orderBy === headCell.id ? (
<Box component="span" sx={visuallyHidden}>
{order === "desc"
? "sorted descending"
: "sorted ascending"}
</Box>
) : null}
</TableSortLabel>
) : (
<span>{headCell.label}</span>
)}
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
onRequestSort: PropTypes.func.isRequired,
order: PropTypes.oneOf(["asc", "desc"]).isRequired,
orderBy: PropTypes.string.isRequired,
rowCount: PropTypes.number.isRequired,
};
import {useNavigate} from "react-router-dom";
import {useDispatch, useSelector} from "react-redux";
import { useEffect } from "react";
import ProjectForm from "../../components/ProjectForm/ProjectForm";
import { createMember, createProject, fetchMembers, fetchProjects } from "../../store/actions/projectsActions";
import MemberForm from "../../components/MemberForm/MemberForm";
const NewMember = ({members}) => {
const dispatch = useDispatch();
const projects = useSelector(state => state.projects.projects);
const navigate = useNavigate();
const onSubmit = async (memberData) => {
await dispatch(createMember(memberData, navigate));
// navigate("/projects/" + memberData.id)
console.log("memberData ", memberData)
};
// useEffect(()=> {
// dispatch(fetchMembers());
// }, [dispatch])
return (
<>
<MemberForm members={members} onSubmit={onSubmit} />
</>
);
};
export default NewMember;
\ No newline at end of file
...@@ -13,7 +13,6 @@ const NewProject = () => { ...@@ -13,7 +13,6 @@ const NewProject = () => {
const onSubmit = async (projectData) => { const onSubmit = async (projectData) => {
await dispatch(createProject(projectData, navigate)); await dispatch(createProject(projectData, navigate));
console.log(projectData) console.log(projectData)
}; };
useEffect(()=> { useEffect(()=> {
......
...@@ -12,6 +12,7 @@ const Projects = () => { ...@@ -12,6 +12,7 @@ const Projects = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { projects, loading } = useSelector(state => state.projects.projects); const { projects, loading } = useSelector(state => state.projects.projects);
const {users} = useSelector(state => state.users); const {users} = useSelector(state => state.users);
const members = useSelector(state => state.projects.projects)
console.log(projects) console.log(projects)
console.log(users) console.log(users)
...@@ -32,7 +33,7 @@ const Projects = () => { ...@@ -32,7 +33,7 @@ const Projects = () => {
> >
<Grid item> <Grid item>
<Typography variant="h4"> <Typography variant="h4">
Projects Проекты
</Typography> </Typography>
</Grid> </Grid>
<HasAccess roles={["superuser", "admin", "user"]} > <HasAccess roles={["superuser", "admin", "user"]} >
...@@ -42,7 +43,7 @@ const Projects = () => { ...@@ -42,7 +43,7 @@ const Projects = () => {
</HasAccess> </HasAccess>
</Grid> </Grid>
<Loader loading={loading} /> <Loader loading={loading} />
<ProjectsList projects={projects} /> <ProjectsList projects={projects} members={members} />
</Grid> </Grid>
</>) : </>) :
<h1>Созданных проектов нет</h1> <h1>Созданных проектов нет</h1>
......
import moment from 'moment';
import { useEffect, useState, useMemo, useCallback } from 'react';
import WeekCalendarBody from '../../components/WeekCalendar/WeekCalendarBody/WeekCalendarBody';
import WeekCalendarHeader from '../../components/WeekCalendar/WeekCalendarHeader/WeekCalendarHeader'
import { getWeekInfoString, getWeekFromCurrentDate } from '../../helpers/CalendarHelpers';
function WeekCalendar() {
const [date, setDate] = useState({year: '', month: '', currentDay: ''})
const [hourFormat, setHourFormat] = useState(false);
useEffect(()=>{
const year = new Date().getFullYear()
const month = new Date().getMonth()
const currentDay = moment().date()
setDate({year: year, month: month, currentDay: currentDay})
}, [])
const hoursInDay = useMemo(()=>{
let arr
if (hourFormat) {
arr = ['8:00', '9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00','21:00','22:00']
} else {
arr = ['8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00']
}
return arr
}, [hourFormat])
const week = useMemo(()=>{
return getWeekFromCurrentDate(date.year, date.month, date.currentDay)
}, [date])
const incrementWeek = useCallback(() => {
setDate((prevState)=>{
const newDate = new Date(prevState.year, prevState.month, prevState.currentDay + 7)
return {year: newDate.getFullYear(), month: newDate.getMonth(), currentDay: moment(newDate).date()}
})
}, [])
const decrementWeek = useCallback(() => {
setDate((prevState)=>{
const newDate = new Date(prevState.year, prevState.month, prevState.currentDay - 7)
return {year: newDate.getFullYear(), month: newDate.getMonth(), currentDay: moment(newDate).date()}
})
}, [])
const weekInfo = useMemo(()=>{
return getWeekInfoString(week, date)
}, [date, week])
return (
<>
<WeekCalendarHeader
incrementWeek={incrementWeek}
decrementWeek={decrementWeek}
weekInfo={weekInfo}
/>
<WeekCalendarBody
week={week}
hourFormat={hourFormat}
setHourFormat={setHourFormat}
hoursInDay={hoursInDay}
/>
</>
);
}
export default WeekCalendar;
\ No newline at end of file
import moment from "moment"
export const getDaysInMonth = (dateNow) => { export const getDaysInMonth = (dateNow) => {
if (dateNow.month <= 11 && dateNow.month >= 0) { if (dateNow.month <= 11 && dateNow.month >= 0) {
const newDaysInMonth = [] const newDaysInMonth = []
...@@ -13,9 +15,9 @@ export const getDaysInMonth = (dateNow) => { ...@@ -13,9 +15,9 @@ export const getDaysInMonth = (dateNow) => {
} }
} }
export const getCurrentMonthString = (dateNow) => { export const getCurrentMonthString = (month) => {
if (dateNow.month <= 11 && dateNow.month >= 0) { if (month <= 11 && month >= 0) {
return ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь", "Декабрь"][dateNow.month]; return ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь", "Декабрь"][month];
} else { } else {
return null return null
} }
...@@ -31,4 +33,36 @@ export const dateToISOLikeButLocal = (date) => { ...@@ -31,4 +33,36 @@ export const dateToISOLikeButLocal = (date) => {
} else { } else {
return null return null
} }
}
export const getWeekFromCurrentDate = (year, month, curDay) => {
const week = [0, 0, 0, 0, 0, 0, 0]
const currentDayWeek = moment(new Date(year, month, curDay)).isoWeekday()
for (let i = 1; i <= 7; i++) {
if (currentDayWeek > i ) {
const day = moment(new Date(year, month, curDay - i)).date()
const dayWeek = moment(new Date(year, month, curDay - i)).isoWeekday()
week[dayWeek - 1] = day
} else if (currentDayWeek === i) {
const day = moment(new Date(year, month, curDay)).date()
const dayWeek = moment(new Date(year, month, curDay)).isoWeekday()
week[dayWeek - 1] = day
} else {
const day = moment(new Date(year, month, curDay + i - moment(new Date(year, month, curDay)).isoWeekday())).date()
const dayWeek = i - moment(new Date(year, month, curDay)).isoWeekday() + moment(new Date(year, month, curDay)).isoWeekday()
week[dayWeek - 1] = day
}
}
return week
}
export const getWeekInfoString = (week, date) => {
if (week[0] > week[6]) {
if (date.month === 11) {
return getCurrentMonthString(date.month) + ' - ' + getCurrentMonthString(0) + ' ' + date.year
}
return getCurrentMonthString(date.month) + ' - ' + getCurrentMonthString(date.month + 1) + ' ' + date.year
} else {
return getCurrentMonthString(date.month) + ' ' + date.year
}
} }
\ No newline at end of file
export const FETCH_PROJECTS_REQUEST = "FETCH_PROJECTS_REQUEST"; export const FETCH_PROJECTS_REQUEST = "FETCH_PROJECTS_REQUEST";
export const FETCH_PROJECTS_SUCCESS = "FETCH_PROJECTS_SUCCESS"; export const FETCH_PROJECTS_SUCCESS = "FETCH_PROJECTS_SUCCESS";
export const FETCH_PROJECTS_ERROR = "FETCH_PROJECTS_ERROR"; export const FETCH_PROJECTS_ERROR = "FETCH_PROJECTS_ERROR";
export const FETCH_PROJECT_SUCCESS = "FETCH_PROJECT_SUCCESS"; export const FETCH_PROJECT_SUCCESS = "FETCH_PROJECT_SUCCESS";
export const CREATE_PROJECT_SUCCESS = "CREATE_PROJECT_SUCCESS"; export const CREATE_PROJECT_SUCCESS = "CREATE_PROJECT_SUCCESS";
export const CREATE_MEMBER_SUCCESS = "CREATE_MEMBER_SUCCESS";
\ No newline at end of file
...@@ -20,4 +20,8 @@ export const DELETE_TASK_REQUEST = "DELETE_TASK_REQUEST"; ...@@ -20,4 +20,8 @@ export const DELETE_TASK_REQUEST = "DELETE_TASK_REQUEST";
export const DELETE_TASK_SUCCESS = "DELETE_TASK_SUCCESS"; export const DELETE_TASK_SUCCESS = "DELETE_TASK_SUCCESS";
export const DELETE_TASK_FAILURE = "DELETE_TASK_FAILURE"; export const DELETE_TASK_FAILURE = "DELETE_TASK_FAILURE";
export const EDIT_CALENDAR_TASK = "EDIT_CALENDAR_TASK"; export const EDIT_CALENDAR_TASK = "EDIT_CALENDAR_TASK";
\ No newline at end of file
export const DELETE_DATETIMETASK_REQUEST = "DELETE_TASK_REQUEST";
export const DELETE_DATETIMETASK_SUCCESS = "DELETE_TASK_SUCCESS";
export const DELETE_DATETIMETASK_FAILURE = "DELETE_TASK_FAILURE";
\ No newline at end of file
import axios from "../../axiosPlanner"; import axios from "../../axiosPlanner";
import { CREATE_PROJECT_SUCCESS, FETCH_PROJECTS_ERROR, FETCH_PROJECTS_REQUEST, FETCH_PROJECTS_SUCCESS, FETCH_PROJECT_SUCCESS } from "../actionTypes/projectsActionTypes"; import { CREATE_MEMBER_SUCCESS, CREATE_PROJECT_SUCCESS, FETCH_MEMBERS_ERROR, FETCH_MEMBERS_REQUEST, FETCH_MEMBERS_SUCCESS, FETCH_PROJECTS_ERROR, FETCH_PROJECTS_REQUEST, FETCH_PROJECTS_SUCCESS, FETCH_PROJECT_SUCCESS } from "../actionTypes/projectsActionTypes";
import { showNotification } from "./commonActions"; import { showNotification } from "./commonActions";
const fetchProjectsRequest = () => { const fetchProjectsRequest = () => {
...@@ -8,17 +8,21 @@ const fetchProjectsRequest = () => { ...@@ -8,17 +8,21 @@ const fetchProjectsRequest = () => {
const fetchProjectsSuccess = (projects) => { const fetchProjectsSuccess = (projects) => {
return {type: FETCH_PROJECTS_SUCCESS, projects}; return {type: FETCH_PROJECTS_SUCCESS, projects};
}; };
const fetchProjectSuccess = (project) => {
return {type: FETCH_PROJECT_SUCCESS, project};
};
const fetchProjectsError = (error) => { const fetchProjectsError = (error) => {
return {type: FETCH_PROJECTS_ERROR, error}; return {type: FETCH_PROJECTS_ERROR, error};
} }
const fetchProjectSuccess = (project) => {
return {type: FETCH_PROJECT_SUCCESS, project};
};
const createProjectSuccess = () => { const createProjectSuccess = () => {
return {type: CREATE_PROJECT_SUCCESS}; return {type: CREATE_PROJECT_SUCCESS};
}; };
const createMemberSuccess = (member) => {
return {type: CREATE_MEMBER_SUCCESS, member};
};
export const fetchProjects = () => { export const fetchProjects = () => {
return async dispatch => { return async dispatch => {
dispatch(fetchProjectsRequest()); dispatch(fetchProjectsRequest());
...@@ -37,6 +41,7 @@ export const fetchProject = (id) => { ...@@ -37,6 +41,7 @@ export const fetchProject = (id) => {
try { try {
const response = await axios.get("/projects/" + id); const response = await axios.get("/projects/" + id);
dispatch(fetchProjectSuccess(response.data)); dispatch(fetchProjectSuccess(response.data));
console.log("fetch project "+response.data)
} catch (e) { } catch (e) {
dispatch(fetchProjectsError(e)); dispatch(fetchProjectsError(e));
} }
...@@ -56,4 +61,21 @@ export const createProject = (projectData, navigate) => { ...@@ -56,4 +61,21 @@ export const createProject = (projectData, navigate) => {
dispatch(showNotification("Не удалось создать проект", "error")) dispatch(showNotification("Не удалось создать проект", "error"))
} }
}; };
} }
\ No newline at end of file
export const createMember = (memberData, navigate) => {
return async (dispatch) => {
try {
console.log(memberData)
const response = await axios.post("/projects/add-user", memberData);
dispatch(createMemberSuccess());
console.log(memberData)
navigate("/projects/")
dispatch(showNotification("Участник успешно добавлен"))
} catch (e) {
console.log(e);
dispatch(showNotification("Не удалось добавить участника", "error"))
}
};
}
...@@ -14,7 +14,10 @@ import { ...@@ -14,7 +14,10 @@ import {
FETCH_ALL_TASKS_SUCCESS, FETCH_ALL_TASKS_SUCCESS,
FETCH_TASKS_BY_PROJECT_SUCCESS, FETCH_TASKS_BY_PROJECT_SUCCESS,
FETCH_TASKS_BY_PROJECT_FAILURE, FETCH_TASKS_BY_PROJECT_FAILURE,
FETCH_TASKS_BY_PROJECT_REQUEST FETCH_TASKS_BY_PROJECT_REQUEST,
DELETE_DATETIMETASK_FAILURE,
DELETE_DATETIMETASK_SUCCESS,
DELETE_DATETIMETASK_REQUEST,
} from "../actionTypes/tasksTypes"; } from "../actionTypes/tasksTypes";
import axios from '../../axiosPlanner' import axios from '../../axiosPlanner'
...@@ -74,7 +77,7 @@ export const addCalendarTask = (task) => { ...@@ -74,7 +77,7 @@ export const addCalendarTask = (task) => {
return async (dispatch) => { return async (dispatch) => {
dispatch(addTaskRequest()); dispatch(addTaskRequest());
try { try {
const response = await axios.post("/tasks", task); await axios.post("/tasks", task);
dispatch(addTaskSuccess()) dispatch(addTaskSuccess())
dispatch(fetchCalendarTasks()) dispatch(fetchCalendarTasks())
} catch (error) { } catch (error) {
...@@ -87,7 +90,7 @@ export const addCopyCalendarTask = (task) => { ...@@ -87,7 +90,7 @@ export const addCopyCalendarTask = (task) => {
return async (dispatch) => { return async (dispatch) => {
dispatch(addTaskRequest()); dispatch(addTaskRequest());
try { try {
const response = await axios.post("/copy-tasks/make-copy", task); await axios.post("/copy-tasks/make-copy", task);
dispatch(addTaskSuccess()) dispatch(addTaskSuccess())
dispatch(fetchCalendarTasks()) dispatch(fetchCalendarTasks())
} catch (error) { } catch (error) {
...@@ -134,11 +137,11 @@ export const editTask = (task) => { ...@@ -134,11 +137,11 @@ export const editTask = (task) => {
} }
} }
export const editCalendarTask = (task) => { export const editCalendarTask = (task, taskId) => {
return async (dispatch) => { return async (dispatch) => {
dispatch(editTaskRequest()); dispatch(editTaskRequest());
try { try {
await axios.put("/copy-tasks/change-copy", task); await axios.put(`/copy-tasks/change-copy/${taskId}`, task);
dispatch(editTaskSuccess()) dispatch(editTaskSuccess())
dispatch(fetchCalendarTasks()) dispatch(fetchCalendarTasks())
} catch (error) { } catch (error) {
...@@ -209,4 +212,31 @@ export const fetchTasksByProject = (projects) => { ...@@ -209,4 +212,31 @@ export const fetchTasksByProject = (projects) => {
dispatch(fetchTasksByProjectFailure(error.response.data)); dispatch(fetchTasksByProjectFailure(error.response.data));
} }
} }
} }
\ No newline at end of file
const deleteDateTimeTaskRequest = () => {
return {type: DELETE_DATETIMETASK_REQUEST}
};
const deleteDateTimeTaskSuccess = () => {
return {type: DELETE_DATETIMETASK_SUCCESS}
};
const deleteDateTimeTaskFailure = (error) => {
return {type: DELETE_DATETIMETASK_FAILURE, error}
};
export const deleteDateTimeTask = (dateTimeTaskId) => {
return async (dispatch) => {
dispatch(deleteDateTimeTaskRequest());
try {
await axios.delete(`/copy-tasks/${dateTimeTaskId}`);
dispatch(deleteDateTimeTaskSuccess())
dispatch(fetchAllTasks())
} catch (error) {
dispatch(deleteDateTimeTaskFailure(error.response.data));
}
}
}
...@@ -85,13 +85,9 @@ export const forgottenPassword = (userData, navigate) => { ...@@ -85,13 +85,9 @@ export const forgottenPassword = (userData, navigate) => {
} }
export const logoutUser = (navigate) => { export const logoutUser = (navigate) => {
return async (dispatch, getState) => { return async (dispatch) => {
try { try {
await axios.delete("/users/sessions", { await axios.delete("/users/sessions");
headers: {
'Authorization': getState().users.user?.token
}
});
dispatch(logoutUserSuccess()); dispatch(logoutUserSuccess());
navigate("/"); navigate("/");
dispatch(showNotification("Вы успешно вышли")); dispatch(showNotification("Вы успешно вышли"));
......
...@@ -12,10 +12,12 @@ import { ...@@ -12,10 +12,12 @@ import {
DELETE_TASK_REQUEST, DELETE_TASK_REQUEST,
DELETE_TASK_FAILURE, DELETE_TASK_FAILURE,
FETCH_ALL_TASKS_SUCCESS, FETCH_ALL_TASKS_SUCCESS,
EDIT_CALENDAR_TASK,
FETCH_TASKS_BY_PROJECT_REQUEST, FETCH_TASKS_BY_PROJECT_REQUEST,
FETCH_TASKS_BY_PROJECT_SUCCESS, FETCH_TASKS_BY_PROJECT_SUCCESS,
FETCH_TASKS_BY_PROJECT_FAILURE FETCH_TASKS_BY_PROJECT_FAILURE,
DELETE_DATETIMETASK_FAILURE,
DELETE_DATETIMETASK_SUCCESS,
DELETE_DATETIMETASK_REQUEST
} from "../actionTypes/tasksTypes"; } from "../actionTypes/tasksTypes";
const initialState = { const initialState = {
...@@ -102,6 +104,12 @@ const tasksReduсer = (state = initialState, action) => { ...@@ -102,6 +104,12 @@ const tasksReduсer = (state = initialState, action) => {
return {...state, loading: true}; return {...state, loading: true};
case DELETE_TASK_FAILURE: case DELETE_TASK_FAILURE:
return {...state, loading: false, error: action.error}; return {...state, loading: false, error: action.error};
case DELETE_DATETIMETASK_SUCCESS:
return {...state, loading: false};
case DELETE_DATETIMETASK_REQUEST:
return {...state, loading: true};
case DELETE_DATETIMETASK_FAILURE:
return {...state, loading: false, error: action.error};
default: default:
return state; return state;
} }
......
node_modules
.idea
output
exports.config = {
output: './output',
helpers: {
Puppeteer: {
url: 'http://localhost:3000',
show: true,
windowSize: '1200x900'
}
},
include: {
I: './steps_file.js'
},
mocha: {},
bootstrap: null,
timeout: null,
teardown: null,
hooks: [],
gherkin: {
features: './features/*.feature',
steps: [
'./step_definitions/steps.js',
'./step_definitions/create_task_steps.js']
},
plugins: {
screenshotOnFail: {
enabled: true
},
tryTo: {
enabled: true
},
retryFailedStep: {
enabled: false
},
retryTo: {
enabled: true
},
eachElement: {
enabled: true
},
pauseOnFail: {}
},
stepTimeout: 0,
stepTimeoutOverride: [{
pattern: 'wait.*',
timeout: 0
},
{
pattern: 'amOnPage',
timeout: 0
}
],
tests: './step_definitions/*_steps.js',
name: 'planner-tests',
translation: 'ru-RU'
}
\ No newline at end of file
#language: ru
Функция: Создание задачи
Сценарий: Успешное создание задачи
Допустим я нахожусь на странице "/"
То я нажимаю на кнопку '#test_month_header'
И я вижу элемент "#test_month_info"
Если я нажимаю на элемент ".test_empty_box"
И я вижу элемент "#test_modal_task"
Если я ввожу в поле "title" текст "test tasklasdlasdl"
И я нажимаю на кнопку '#test_button_save_task'
Тогда я вижу элемент с текстом "Задачаtest tasklasdlasdl"
\ No newline at end of file
{
"compilerOptions": {
"allowJs": true
}
}
\ No newline at end of file
This diff is collapsed.
{
"name": "planner-tests",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "npx codeceptjs run --steps"
},
"author": "",
"license": "ISC",
"dependencies": {
"codeceptjs": "^3.3.7",
"puppeteer": "^19.3.0"
}
}
const { I } = inject();
Given("я нахожусь на странице {string}", (page) => {
I.login('a@a.a0', '123')
I.amOnPage(page)
});
Then('я нажимаю на кнопку {string}', (buttonId) => {
I.click(buttonId)
});
When('я вижу элемент {string}', (infoString) => {
I.waitForElement(infoString, 5)
});
When('я нажимаю на элемент {string}', (className) => {
I.click(className)
});
When('я ввожу в поле {string} текст {string}', (field, title) => {
I.fillField(field, title)
});
When('я нажимаю на кнопку {string}', (buttonId) => {
I.click(buttonId)
I.wait(2)
});
Then('я вижу элемент с текстом {string}', (text) => {
I.see(text)
I.wait(2)
});
/// <reference types='codeceptjs' />
type steps_file = typeof import('./steps_file.js');
declare namespace CodeceptJS {
interface SupportObject { I: I, current: any, Я: Я }
interface Methods extends Puppeteer {}
interface I extends ReturnType<steps_file> {}
interface Я extends WithTranslation<Methods> {}
namespace Translation {
interface Actions {
"say": "сообщаю",
"waitForElement": "ожидаю_элемент",
"waitForVisible": "ожидаю_увидеть",
"waitForText": "ожидаю_текст",
"amOnPage": "на_странице",
"click": "кликаю",
"doubleClick": "дважды_кликаю",
"see": "вижу",
"dontSee": "не_вижу",
"selectOption": "выбираю_опцию",
"fillField": "заполняю_поле",
"pressKey": "нажимаю_кнопку",
"triggerMouseEvent": "триггерное_событие_мыши",
"attachFile": "загружаю_файл",
"seeInField": "вижу_в_поле",
"dontSeeInField": "не_вижу_в_поле",
"appendField": "дописываю_в_поле",
"checkOption": "выбираю_опцию",
"seeCheckboxIsChecked": "вижу_галочку",
"dontSeeCheckboxIsChecked": "не_вижу_галочку",
"grabTextFrom": "беру_текст_из",
"grabValueFrom": "беру_значение_из",
"grabAttributeFrom": "беру_атрибут_из",
"seeInTitle": "вижу_в_заголовке",
"dontSeeInTitle": "не_вижу_в_заголовке",
"grabTitle": "беру_заголовок",
"seeElement": "вижу_элемент",
"dontSeeElement": "не_вижу_элемент",
"seeInSource": "вижу_в_коде",
"dontSeeInSource": "не_вижу_в_коде",
"executeScript": "выполняю_скрипт",
"executeAsyncScript": "выполняю_скрипт_асинхронно",
"seeInCurrentUrl": "вижу_в_адресе",
"dontSeeInCurrentUrl": "не_вижу_в_адресе",
"seeCurrentUrlEquals": "вижу_адрес_равен",
"dontSeeCurrentUrlEquals": "не_вижу_адрес",
"saveScreenshot": "делаю_скриншот",
"setCookie": "устанавливаю_куки",
"clearCookie": "очищаю_куки",
"seeCookie": "вижу_в_куки",
"dontSeeCookie": "не_вижу_в_куки",
"grabCookie": "беру_куки",
"resizeWindow": "растягиваю_окно",
"wait": "жду"
}
}
}
declare const Цель: typeof Feature;
declare const Сценарий: typeof Scenario;
declare const Начало: typeof Before;
declare const Конец: typeof After;
declare const Перед_всем: typeof BeforeSuite;
declare const После_всего: typeof AfterSuite;
\ No newline at end of file
// in this file you can append custom step methods to 'I' object
module.exports = function() {
return actor({
login: function(email, password) {
this.amOnPage('/sign-in')
this.fillField('email', email)
this.fillField('password', password)
this.click('#test_login')
this.waitForElement('#test_greetings', 5)
}
// Define custom steps here, use 'this' to access default methods of I.
// It is recommended to place a general 'login' function here.
});
}
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