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

удалила сортировку из колонки Дата и время выполнения, скорректировала работу сортировок, откорректировала ширину колонок
parent 2a234024
......@@ -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) */
export const authAuthorOrExecutorOfTask = async(req: Request,res: Response, next:NextFunction):Promise<void | express.Response<Response>>=>{
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'})
req.body={...req.body,executorStatus:false}
......@@ -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?.author?.token === token ) {
req.body={...req.body,authorStatus:true}
} else if(task?.executor?.token === token) {
if(task?.executor?.token === token) {
req.body={...req.body,executorStatus:true}
} else {
}
if (task?.author?.token === token ) {
req.body={...req.body,authorStatus:true}
}
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 */
export const taskFinderById = async (taskId:string):Promise<null | Task>=>{
const task = await dataSource
......
......@@ -27,7 +27,7 @@ import { User } from './User';
@Column({ name: 'token', type: 'varchar',length:100, unique: true, nullable:true })
token!: string;
@OneToOne(()=>User,{nullable: false, eager:true})
@OneToOne(()=>User,{nullable: false, eager:true, onDelete:"CASCADE"})
@JoinColumn()
user!:User;
......
......@@ -5,7 +5,7 @@ import { User } from '../models/User';
import { Member } from '../models/Member';
import { In } from 'typeorm';
import { DateTimeTask } from '../models/DateTimeTask';
import { auth, authAuthorOrExecutorOfTask } from '../helpers';
import { auth, authAuthorOrExecutorOfDateTimeTask, authAuthorOrExecutorOfTask } from '../helpers';
const router:Router = express.Router();
const dataSource = myDataSource;
......@@ -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 */
router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: Response):Promise<Response>=>{
const {dateTimeTaskId, taskId, dateTimeStart, dateTimeDue, description, title, priority} = req.body
router.put("/change-copy/:dateTimeTaskId", authAuthorOrExecutorOfTask, async(req:Request, res: Response):Promise<Response>=>{
const {dateTimeTaskId} = req.params
const {executorStatus, taskId, dateTimeStart, dateTimeDue, description, title, priority} = req.body
const dateTimeTask = await dataSource
.createQueryBuilder()
.select('dateTimeTask')
......@@ -55,7 +56,7 @@ router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: R
await dateTimeTask.save()
const task = await taskFinderById(taskId)
if (!task) return res.status(404).send({Message:'task not found'})
task.title = title;
task.title = title;
task.description = description;
task.priority = priority;
await task.save()
......@@ -63,15 +64,18 @@ router.put("/change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: R
})
/**delete copyTask by dateTimeTaskId */
router.delete('/:id', authAuthorOrExecutorOfTask, async(req:Request, res:Response):Promise<Response>=>{
const {id} = req.params
router.delete('/:dateTimeTaskId',authAuthorOrExecutorOfDateTimeTask, async(req:Request, res:Response):Promise<Response|void>=>{
const {executorStatus} = req.body
if(executorStatus){
const {dateTimeTaskId} = req.params
await myDataSource
.createQueryBuilder()
.delete()
.from(DateTimeTask)
.where("id = :id", { id })
.where("id = :dateTimeTaskId", { dateTimeTaskId })
.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
router.post('/add-user/', async (req: Request, res: Response):Promise<Response>=>{
const {userId, projectId, roleProject} = req.body;
console.log("req body" + req.body)
const newMember:Member = new Member();
try{
newMember.user= userId;
......@@ -128,7 +129,7 @@ router.post('/add-user/', async (req: Request, res: Response):Promise<Response>=
newMember.roleProject=roleProject
await newMember.save()
return res.send({newMember})
} catch(e){
} catch(e){
return res.send({message:"add user to project failed" })
}
......
......@@ -157,8 +157,9 @@ router.delete('/:taskId',async (req: Request, res: Response):Promise<Response>=>
/**change of task by task id */
router.put('/',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;
router.put('/:taskId',authAuthorOrExecutorOfTask,async(req:Request, res:Response)=> {
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)
if (!task) return res.status(404).send({Message:'task not found'})
let dateTimeTask = null;
......
......@@ -100,6 +100,7 @@ router.delete('/sessions', async(req: Request, res: Response):Promise<void | obj
if(!user) return res.send({successMsg});
user.token = nanoid();
await user.save();
return res.send(successMsg)
})
......
......@@ -2,7 +2,7 @@ import {Routes, Route, Outlet, Navigate, BrowserRouter} from "react-router-dom";
import {Container} from "@mui/material";
import {useSelector} from "react-redux";
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 Register from './containers/Register/Register';
import MonthCalendar from './containers/MonthCalendar/MonthCalendar';
......@@ -10,6 +10,7 @@ import ForgottenPassword from "./containers/ForgottenPassword/ForgottenPassword"
import Projects from "./containers/Projects/Projects";
import FullProject from "./containers/FullProject/FullProject";
import NewProject from "./containers/NewProject/NewProject";
import WeekCalendar from "./containers/WeekCalendar/WeekCalendar";
const ProtectedRoute = ({isAllowed, roles, redirectUrl, children}) => {
const user = useSelector(state => state.users?.user);
......@@ -56,7 +57,7 @@ const App = () => {
isAllowed={user}
redirectUrl={"/sign-in"}
>
<h1>week page</h1>
<WeekCalendar/>
</ProtectedRoute>
}/>
......@@ -65,7 +66,7 @@ const App = () => {
isAllowed={user}
redirectUrl={"/sign-in"}
>
<h1>week page</h1>
<WeekCalendar/>
</ProtectedRoute>
}/>
......@@ -74,7 +75,7 @@ const App = () => {
isAllowed={user}
redirectUrl={"/sign-in"}
>
<MonthCalendar>month page</MonthCalendar>
<MonthCalendar></MonthCalendar>
</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 { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {NavLink, useNavigate} from "react-router-dom";
import { logoutUser } from "../../../store/actions/usersActions";
import {Button} from "@mui/material";
import {NavLink} from "react-router-dom";
import { superuserMenuButtons } from "../../../constants";
import ProfileBlock from "../ProfileBlock/ProfileBlock";
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 <>
<Button
component={NavLink}
to="/projects"
color="inherit"
size="large"
>
Проекты
</Button>
<Button
component={NavLink}
to="/week"
color="inherit"
size="large"
>
Неделя
</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>
{superuserMenuButtons.map((button, i)=>{
return(
<Button
key={i}
component={NavLink}
to={button.path}
color="inherit"
size="large"
>
{button.text}
</Button>
)
})}
<ProfileBlock/>
</>
};
......
import {Button} from "@mui/material";
import {NavLink} from "react-router-dom";
import { anonymoysMenuButtons } from "../../../constants";
const AnonymousMenu = () => {
return <>
<Button
component={NavLink}
color="inherit"
to="/sign-in"
>
Вход
</Button>
{anonymoysMenuButtons.map((button, i)=>{
return(
<Button
key={i}
component={NavLink}
to={button.path}
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 { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {NavLink, useNavigate} from "react-router-dom";
import { logoutUser } from "../../../store/actions/usersActions";
import {Button} from "@mui/material";
import {NavLink} from "react-router-dom";
import { workerMenuButtons } from "../../../constants";
import ProfileBlock from "../ProfileBlock/ProfileBlock";
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 <>
<Button
component={NavLink}
to="/projects"
color="inherit"
size="large"
>
Проекты
</Button>
<Button
component={NavLink}
to="/week"
color="inherit"
size="large"
>
Неделя
</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>
{workerMenuButtons.map((button, i)=>{
return(
<Button
key={i}
component={NavLink}
to={button.path}
color="inherit"
size="large"
>
{button.text}
</Button>
)
})}
<ProfileBlock/>
</>
};
......
import { Grid } from "@mui/material";
const CalendarRow = ({children}) => {
const CalendarRow = ({children, week}) => {
return <>
<Grid
container
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}
</Grid>
......
......@@ -34,14 +34,15 @@ const EmptyBox = ({hourNumber, handleOpen, dayNumber, xs, dragTaskHandler, modal
onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}}
onClick={(e)=>{onClickHandler(e, dayNumber, hourNumber)}}
className='test_empty_box'
item xs={xs} sx={{
height: '35px',
height: '40px',
backgroundColor: 'rgb(0,0,0,0)',
zIndex: '6',
cursor: copyTask ? 'pointer' : 'default'
}}>
{isThisCell ?
<DefaultTask/> : null}
<DefaultTask/> : ' '}
</Grid>)
};
......
import { Grid } from "@mui/material";
import { memo } from "react";
const CalendarSmallCell = ({children, xs}) => {
const CalendarSmallCell = ({children, xs, week}) => {
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}
</Grid>
</>
......
......@@ -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 cellClass = {
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',
borderBottom: week ?'1px solid black' : null,
}
useEffect(()=>{
if(!modal) {
setIsThisCell(false);
......@@ -27,7 +30,7 @@ const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCell
dragTaskHandler(dayNumber, parseInt(hours.split(':')[0]))
}
const onClickHandler = (e) => {
if (!linesInDay?.length) {
if (linesInDay?.length) {
createTaskInCellHandler(dayNumber, hours);
setIsThisCell(true);
handleOpen(e)
......
......@@ -3,7 +3,7 @@ import { useState } from "react";
import CalendarRow from "./CalendarRow/CalendarRow";
import CalendarSmallCell from "./CalendarSmallCell/CalendarSmallCell";
import CalendarStandartCell from "./CalendarStandartCell.js/CalendarStandartCell";
import ModalTask from "../UI/ModalTask/ModalTask";
import ModalTask from "../../UI/ModalTask/ModalTask";
import MonthCalendarModalContent from "../MonthCalendarModalContent/MonthCalendarModalContent";
import CalendarRowDay from "./CalendarRowDay/CalendarRowDay";
......
import { Box, Typography } from '@mui/material';
import { memo } from 'react';
import MonthDecrementButton from './MonthDecrementButton/MonthDecrementButton';
import MonthIncrementButton from './MonthIncrementButton/MonthIncrementButton';
import ArrowDecrementButton from '../../../UI/ArrowDecrementButton/ArrowDecrementButton';
import ArrowIncrementButton from '../../../UI/ArrowIncrementButton/ArrowIncrementButton';
function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMonth}) {
return (
......@@ -14,9 +14,10 @@ function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMo
alignItems: 'center',
gap: '10px'
}}
id='test_month_info'
>
<MonthDecrementButton
decrementMonth={decrementMonth}
<ArrowDecrementButton
onClick={decrementMonth}
/>
<Box sx={{ flexBasis: '150px' }}>
<Typography
......@@ -30,8 +31,8 @@ function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMo
</Typography>
<Typography align='center'>{year}</Typography>
</Box>
<MonthIncrementButton
incrementMonth={incrementMonth}
<ArrowIncrementButton
onClick={incrementMonth}
/>
</Box>
</> );
......
import { AppBar, Toolbar} from '@mui/material';
import { Box } from '@mui/system';
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 types = [{value: 'Месяц', text: 'Месяц'}, {value: 'Неделя', text: 'Неделя'}]
......
import { Button, TextField } from "@mui/material";
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'}]
......@@ -56,7 +56,7 @@ function MonthCalendarModalContent({title, onChangeCurrentTaskHandler, descripti
/>
</div>
<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>
</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}) => {
return (
<div >
<Button onClick={handleOpen} >Add project</Button>
<Button onClick={handleOpen} >Добавить проект</Button>
<Modal
open={open}
onClose={handleClose}
......@@ -56,7 +56,7 @@ const ProjectForm = ({onSubmit}) => {
<Box sx={style}>
<form >
<Grid container direction="column" spacing={2}>
<Typography variant="h4">New project</Typography>
<Typography variant="h4">Новый проект</Typography>
<FormElement
onChange={inputChangeHandler}
name={"title"}
......@@ -70,7 +70,7 @@ const ProjectForm = ({onSubmit}) => {
variant="contained"
>
Create
Создать
</Button>
</Grid>
</Grid>
......
......@@ -7,12 +7,12 @@ import { useDispatch, useSelector } from "react-redux";
const ProjectItem = ({ title, tasks, id }) => {
const user = useSelector(state => state.users.user);
const dispatch = useDispatch();
console.log(tasks)
console.log(user)
return <>
<Grid item xs={12} sm={12} md={6} lg={4}>
<Grid item xs={12} sm={12} md={6} lg={4}>
<Card>
<CardContent>
<CardContent >
<strong>
<br></br>
Название проекта: {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 ArrowForwardIcon from "@mui/icons-material/ArrowForward";
import { useDispatch, useSelector } from "react-redux";
import DeleteIcon from "@mui/icons-material/Delete";
const ProjectMembersItem = ({ displayName, id }) => {
const ProjectMembersItem = ({ displayName, roleProject, id, roleProjectOfAuthor }) => {
console.log(displayName)
const dispatch = useDispatch();
const user = useSelector(state => state.users)
console.log(user)
const { projects, project } = useSelector(state => state.projects);
return <>
<Grid item xs={12} sm={12} md={6} lg={4}>
<Card>
<CardContent>
<strong>
<br></br>
{displayName}
</strong>
<strong>
<br></br>
роль: {project?.project?.members[0]?.roleProject}
</strong>
<strong>
<br></br>
<button>delete</button>
</strong>
</CardContent>
</Card>
</Grid>
return <>
<Grid item xs={12} sm={12} md={6} lg={4}>
<Card>
<CardContent>
<strong>
<br></br>
{displayName}
</strong>
<strong>
<br></br>
роль: {roleProject}
</strong>
<strong>
{roleProjectOfAuthor === "admin" ?
(<strong>
<Tooltip title="Удалить">
<IconButton
onClick={(id) => {
// deleteHandle(task.id);
}}
>
<DeleteIcon style={{ marginTop: "-5px" }} />
</IconButton>
</Tooltip>
</strong>) : null}
</strong>
</CardContent>
</Card>
</Grid>
</>
};
......
import {Grid} from "@mui/material";
import ProjectMembersItem from "../ProjectMembersItem/ProjectMembersItem";
const ProjectMembersList = ({users}) => {
console.log(users)
const ProjectMembersList = ({members, roleProjectOfAuthor}) => {
console.log("members ", members)
return (
<Grid item container direction="row" spacing={1}>
{users?.map(user => {
<Grid item container direction="column" spacing={1}>
{members?.map(member => {
return <ProjectMembersItem
displayName={user.displayName}
id={user.id}
key={user.id}
displayName={member?.user?.displayName}
roleProject={member?.roleProject}
id={member.id}
key={member.id}
roleProjectOfAuthor={roleProjectOfAuthor}
/>
})}
</Grid>
......
......@@ -2,8 +2,9 @@ import {Grid} from "@mui/material";
import ProjectItem from "../ProjectItem/ProjectItem";
const ProjectsList = ({projects}) => {
console.log(projects)
return (
<Grid item container direction="row" spacing={1}>
<Grid item container direction="column" spacing={1}>
{projects?.map(project => {
return <ProjectItem
tasks={project.tasks}
......
......@@ -13,15 +13,15 @@ const arrowClass = {
}
function MonthDecrementButton({decrementMonth}) {
function ArrowDecrementButton({onClick}) {
return (
<>
<ArrowBackIcon
sx={arrowClass}
onClick={decrementMonth}
onClick={onClick}
/>
</> );
}
export default memo(MonthDecrementButton);
\ No newline at end of file
export default memo(ArrowDecrementButton);
\ No newline at end of file
......@@ -13,15 +13,15 @@ const arrowClass = {
}
function MonthIncrementButton({incrementMonth}) {
function ArrowIncrementButton({onClick}) {
return (
<>
<ArrowForwardIcon
sx={arrowClass}
onClick={incrementMonth}
onClick={onClick}
/>
</> );
}
export default memo(MonthIncrementButton);
\ No newline at end of file
export default memo(ArrowIncrementButton);
\ No newline at end of file
import { Grid, TextField, MenuItem } from "@mui/material";
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
if (select) {
......@@ -19,6 +19,7 @@ const FormElement = ({ name, label, state, error, onChange, select, options, typ
type={type}
variant="outlined"
value={state?.[name]}
users={users}
onChange={onChange}
error={!!error}
helperText={error}
......
......@@ -56,6 +56,7 @@ export default function ModalTask({modal, handleClose, children}) {
return (
<>
<Modal
id='test_modal_task'
open={modal.open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
......
......@@ -25,6 +25,7 @@ const UserForm = ({ state, onChange, onSubmit, getFieldError, buttonText, resetP
sx={{ mt: "15px" }}
type="submit"
fullWidth
id='test_login'
variant="contained"
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 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";
import ProjectTasksBody from "../../components/ProjectTasks/ProjectTasksBody";
import { fetchUsers } from "../../store/actions/usersActions";
import ProjectMembersList from "../../components/ProjectMembersList/ProjectMembersList";
import NewMember from "../NewMember/NewMember";
const FullProject = () => {
const { projects, project } = useSelector(state => state.projects);
const users = useSelector(state => state.users.users);
const dispatch = useDispatch();
const params = useParams()
const tasks = project.tasks;
......@@ -22,13 +23,13 @@ const FullProject = () => {
useEffect(() => {
dispatch(fetchUsers())
}, [dispatch]);
console.log(users)
useEffect(() => {
dispatch(fetchProject(params.id))
}, [params.id, dispatch]);
console.log(project);
console.log("project ", project);
return <>
<Grid item xs={12} sm={12} md={6} lg={4}>
......@@ -53,9 +54,13 @@ const FullProject = () => {
</strong>
<strong>
<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>
<br></br>
......
import { useEffect, useCallback, useState, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import MonthCalendarBody from '../../components/MonthCalendarBody/MonthCalendarBody';
import MonthCalendarHeader from '../../components/MonthCalendarHeader/MonthCalendarHeader';
import MonthCalendarBody from '../../components/MonthCalendar/MonthCalendarBody/MonthCalendarBody';
import MonthCalendarHeader from '../../components/MonthCalendar/MonthCalendarHeader/MonthCalendarHeader';
import { dateToISOLikeButLocal, getCurrentMonthString, getDaysInMonth } from '../../helpers/CalendarHelpers';
import { addCalendarTask, addCopyCalendarTask, deleteCalendarTask, editCalendarTask, fetchCalendarTasks} from '../../store/actions/tasksActions';
......@@ -47,8 +47,8 @@ function MonthCalendar() {
}, [dateNow])
const currentMonthString = useMemo(() => {
return getCurrentMonthString(dateNow)
}, [dateNow])
return getCurrentMonthString(dateNow.month)
}, [dateNow.month])
const onChangeWorkerHandler = useCallback((event) => {
setWorker(event.target.value);
......@@ -158,18 +158,17 @@ function MonthCalendar() {
...currentTask,
dateTimeStart: start,
dateTimeDue: due,
dateTimeTaskId: currentTask.id,
taskId: currentTask.mainTaskId
}
delete newTask.infoForCell
delete newTask.id
await dispatch(editCalendarTask(newTask))
await dispatch(editCalendarTask(newTask, currentTask.id))
} else {
const newTask = {
...currentTask,
dateTimeStart: start,
dateTimeDue: due,
executor: user,
dateTimeDeadLine: due,
}
delete newTask.infoForCell
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 = () => {
const onSubmit = async (projectData) => {
await dispatch(createProject(projectData, navigate));
console.log(projectData)
};
useEffect(()=> {
......
......@@ -12,6 +12,7 @@ const Projects = () => {
const dispatch = useDispatch();
const { projects, loading } = useSelector(state => state.projects.projects);
const {users} = useSelector(state => state.users);
const members = useSelector(state => state.projects.projects)
console.log(projects)
console.log(users)
......@@ -32,7 +33,7 @@ const Projects = () => {
>
<Grid item>
<Typography variant="h4">
Projects
Проекты
</Typography>
</Grid>
<HasAccess roles={["superuser", "admin", "user"]} >
......@@ -42,7 +43,7 @@ const Projects = () => {
</HasAccess>
</Grid>
<Loader loading={loading} />
<ProjectsList projects={projects} />
<ProjectsList projects={projects} members={members} />
</Grid>
</>) :
<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) => {
if (dateNow.month <= 11 && dateNow.month >= 0) {
const newDaysInMonth = []
......@@ -13,9 +15,9 @@ export const getDaysInMonth = (dateNow) => {
}
}
export const getCurrentMonthString = (dateNow) => {
if (dateNow.month <= 11 && dateNow.month >= 0) {
return ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь", "Декабрь"][dateNow.month];
export const getCurrentMonthString = (month) => {
if (month <= 11 && month >= 0) {
return ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь", "Декабрь"][month];
} else {
return null
}
......@@ -31,4 +33,36 @@ export const dateToISOLikeButLocal = (date) => {
} else {
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_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";
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";
export const DELETE_TASK_SUCCESS = "DELETE_TASK_SUCCESS";
export const DELETE_TASK_FAILURE = "DELETE_TASK_FAILURE";
export const EDIT_CALENDAR_TASK = "EDIT_CALENDAR_TASK";
\ No newline at end of file
export const EDIT_CALENDAR_TASK = "EDIT_CALENDAR_TASK";
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 { 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";
const fetchProjectsRequest = () => {
......@@ -8,17 +8,21 @@ const fetchProjectsRequest = () => {
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 fetchProjectSuccess = (project) => {
return {type: FETCH_PROJECT_SUCCESS, project};
};
const createProjectSuccess = () => {
return {type: CREATE_PROJECT_SUCCESS};
};
const createMemberSuccess = (member) => {
return {type: CREATE_MEMBER_SUCCESS, member};
};
export const fetchProjects = () => {
return async dispatch => {
dispatch(fetchProjectsRequest());
......@@ -37,6 +41,7 @@ export const fetchProject = (id) => {
try {
const response = await axios.get("/projects/" + id);
dispatch(fetchProjectSuccess(response.data));
console.log("fetch project "+response.data)
} catch (e) {
dispatch(fetchProjectsError(e));
}
......@@ -56,4 +61,21 @@ export const createProject = (projectData, navigate) => {
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 {
FETCH_ALL_TASKS_SUCCESS,
FETCH_TASKS_BY_PROJECT_SUCCESS,
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";
import axios from '../../axiosPlanner'
......@@ -74,7 +77,7 @@ export const addCalendarTask = (task) => {
return async (dispatch) => {
dispatch(addTaskRequest());
try {
const response = await axios.post("/tasks", task);
await axios.post("/tasks", task);
dispatch(addTaskSuccess())
dispatch(fetchCalendarTasks())
} catch (error) {
......@@ -87,7 +90,7 @@ export const addCopyCalendarTask = (task) => {
return async (dispatch) => {
dispatch(addTaskRequest());
try {
const response = await axios.post("/copy-tasks/make-copy", task);
await axios.post("/copy-tasks/make-copy", task);
dispatch(addTaskSuccess())
dispatch(fetchCalendarTasks())
} catch (error) {
......@@ -134,11 +137,11 @@ export const editTask = (task) => {
}
}
export const editCalendarTask = (task) => {
export const editCalendarTask = (task, taskId) => {
return async (dispatch) => {
dispatch(editTaskRequest());
try {
await axios.put("/copy-tasks/change-copy", task);
await axios.put(`/copy-tasks/change-copy/${taskId}`, task);
dispatch(editTaskSuccess())
dispatch(fetchCalendarTasks())
} catch (error) {
......@@ -209,4 +212,31 @@ export const fetchTasksByProject = (projects) => {
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) => {
}
export const logoutUser = (navigate) => {
return async (dispatch, getState) => {
return async (dispatch) => {
try {
await axios.delete("/users/sessions", {
headers: {
'Authorization': getState().users.user?.token
}
});
await axios.delete("/users/sessions");
dispatch(logoutUserSuccess());
navigate("/");
dispatch(showNotification("Вы успешно вышли"));
......
......@@ -12,10 +12,12 @@ import {
DELETE_TASK_REQUEST,
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
FETCH_TASKS_BY_PROJECT_FAILURE,
DELETE_DATETIMETASK_FAILURE,
DELETE_DATETIMETASK_SUCCESS,
DELETE_DATETIMETASK_REQUEST
} from "../actionTypes/tasksTypes";
const initialState = {
......@@ -102,6 +104,12 @@ const tasksReduсer = (state = initialState, action) => {
return {...state, loading: true};
case DELETE_TASK_FAILURE:
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:
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