Commit e76b3f24 authored by Нелли Ибрагимова's avatar Нелли Ибрагимова

Merge branch 'development' of…

Merge branch 'development' of ssh://git.attractor-school.com:30022/apollo64/crm-team-one into task-64-reconstruction-of-table-My-task
parents 1307ccd2 461c5497
......@@ -3,6 +3,7 @@ import {User} from './models/User';
import {Task} from './models/Task';
import { Project } from "./models/Project";
import { Member } from "./models/Member";
import { DateTimeTask } from "./models/DateTimeTask";
export const myDataSource = new DataSource({
type: "postgres",
......@@ -11,7 +12,7 @@ export const myDataSource = new DataSource({
username: "pluser",
password: "pluser",
database: "planner",
entities: [User,Task,Project,Member],
entities: [User,Task,Project,Member,DateTimeTask],
logging: true,
synchronize: true, // in build switch to false
migrationsRun: false
......
......@@ -2,13 +2,47 @@ import { myDataSource } from "./app-data-source";
import { User, UserRole } from "./models/User";
import { faker } from '@faker-js/faker';
import { Task } from "./models/Task";
import { priorityType, Task, taskFinishType } from "./models/Task";
import { Project } from "./models/Project";
import { Member, MemberRole } from "./models/Member";
import { DateTimeTask } from "./models/DateTimeTask";
function randomIntFromInterval(min:number, max:number) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
let countUsers = 0
let countMembers =0
let countProjects = 0
let countRolesProject=0
const cycleThroughObject=(countKey:number,objectObserve:any):MemberRole=>{
let arrayOfKeys = Object.keys(objectObserve)
let keyOfObject = arrayOfKeys[countKey]
let valueOfKey = objectObserve[keyOfObject]
countKey++
if(countKey===arrayOfKeys.length-1){
countKey=0
}
return valueOfKey
}
const cycleArrayOfMembers=(countIndex:number, members:Member[]):Member=>{
let member = members[countIndex]
countIndex++
if (countIndex ===members.length-1){
countIndex=0
}
return member
}
const cycleArrayOfProjects=(countIndex:number, projects:Project[]):Project=>{
let project = projects[countIndex]
countIndex++
if (countIndex ===projects.length-1){
countIndex=0
}
return project
}
const loadFixtures = async () => {
myDataSource
......@@ -18,25 +52,27 @@ const loadFixtures = async () => {
await repositoryMember.delete({})
const repositoryTask = myDataSource.getRepository(Task);
await repositoryTask.delete({});
const repositoryDateTimeTask = myDataSource.getRepository(DateTimeTask);
await repositoryDateTimeTask.delete({})
const repositoryProject = myDataSource.getRepository(Project);
await repositoryProject.delete({})
const repositoryUser = myDataSource.getRepository(User);
await repositoryUser.delete({});
console.log('========================== ' + '\n' + 'Data Source has been cleared!' +'\n' + '==========================')
const userRoles = [{role: UserRole.DIRECTOR}, {role: UserRole.SUPERUSER}, {role: UserRole.USER}, {role: UserRole.USER}];
const userRoles = [{role: UserRole.SUPERUSER}, {role: UserRole.USER}, {role: UserRole.USER},{role: UserRole.USER},];
const users = []
for (let i = 0; i < 4; i++) {
for (let i = 0; i < 3; i++) {
const name = faker.name.firstName()
const surname = faker.name.lastName()
const displayName = name + ' ' + surname[0] + '.'
const user = new User()
user.name = name;
user.surname = surname;
user.password = '12345qwert';
user.password = '123';
user.displayName= displayName;
user.phone = faker.phone.number('+77#########')
user.email = faker.internet.email();
user.email = 'a@a.a'+i;
user.role = userRoles[i].role;
user.generateToken()
await user.save();
......@@ -44,21 +80,29 @@ const loadFixtures = async () => {
}
const tasks:Task[] = []
type taskFinishType = "opened" | "done" |"failed";
type priorityType = "A" | "B" |"C";
const priorities:priorityType[] = ["A", "B" , "C"]
const accomplish:taskFinishType[] = ["opened", "done" , "failed"]
for (let i = 0; i < 20; i++) {
let dateOfMonth = randomIntFromInterval(20, 30)
let deadLineDateOfMonth = randomIntFromInterval(1, 15)
let startDateTime = new Date(2022, 11, dateOfMonth, randomIntFromInterval(10, 15), 0, 0);
let dueDateTime = new Date(2022, 11, dateOfMonth, randomIntFromInterval(16, 20), 0, 0);
let deadLine = new Date(2022, 12, deadLineDateOfMonth, 0, 0, 0);
const newDateTimeTask= new DateTimeTask()
newDateTimeTask.dateTimeStart=startDateTime;
newDateTimeTask.dateTimeDue=dueDateTime;
await newDateTimeTask.save()
if (i <= 15) {
const newTask = new Task();
newTask.title = `Buy ${faker.commerce.productName()}`;
newTask.description = faker.random.words(4);
newTask.executor = faker.helpers.arrayElement(users);
newTask.dateTimeDue = faker.date.soon(randomIntFromInterval(1, 15));
newTask.dateTimeStart = faker.date.recent((randomIntFromInterval(0, 8)));
newTask.dateTimeDeadLine = faker.date.soon(randomIntFromInterval(1, 15));
newTask.dateTimeFactDeadLine = faker.date.soon(randomIntFromInterval(1, 15));
newTask.author = faker.helpers.arrayElement(users);
newTask.executor = faker.helpers.arrayElement(users);
newTask.dateTimeTasks=[newDateTimeTask]
newTask.dateTimeDeadLine =deadLine;
newTask.dateTimeFactDeadLine = deadLine;
newTask.accomplish = faker.helpers.arrayElement(accomplish);
newTask.priority = faker.helpers.arrayElement(priorities);
await newTask.save();
......@@ -68,8 +112,6 @@ const loadFixtures = async () => {
newTask.title = `Buy ${faker.commerce.productName()}`;
newTask.description = faker.random.words(4);
newTask.executor = faker.helpers.arrayElement(users);
// newTask.dateTimeDue = null;
// newTask.dateTimeStart = null;
newTask.author = faker.helpers.arrayElement(users);
newTask.accomplish = accomplish[0];
newTask.priority = faker.helpers.arrayElement(priorities);
......@@ -79,38 +121,33 @@ const loadFixtures = async () => {
}
const members:Member[]=[]
for (let i = 0; i < 5; i++) {
for (let i = 0; i <3; i++) {
const newMember = new Member();
newMember.user = faker.helpers.arrayElement(users);
newMember.roleProject=faker.helpers.objectValue(MemberRole);
newMember.roleProject=cycleThroughObject(countRolesProject, MemberRole);
await newMember.save();
members.push(newMember)
}
const projects:Project[] = []
for (let i = 0; i < 5; i++) {
for (let i = 0; i < 3; i++) {
const newProject = new Project();
newProject.title = `Project ${faker.random.words(1)}`;
newProject.color = faker.random.words(1);
newProject.members = faker.helpers.arrayElements(members,randomIntFromInterval(1, 3))
// newProject.admin = faker.helpers.arrayElement(users);
newProject.tasks= faker.helpers.arrayElements(tasks, randomIntFromInterval(1, 3));
// newProject.workers = faker.helpers.arrayElements(users, randomIntFromInterval(1, 3));
newProject.members = [cycleArrayOfMembers(countMembers,members), cycleArrayOfMembers(countMembers,members),cycleArrayOfMembers(countMembers,members)]
newProject.tasks= faker.helpers.arrayElements(tasks, randomIntFromInterval(2, 19));
await newProject.save();
projects.push(newProject)
}
console.log('========================== ' + '\n' + 'Fixtures done!' +'\n' + '==========================')
})
.catch((err) => {
console.error("Error during Data Source initialization:", err)
console.error("Error during Data Source initialization:", err)
})
};
......
import express, { NextFunction, Request, Response, Router } from "express";
import { myDataSource } from "./app-data-source";
import { Task } from "./models/Task";
import { User } from "./models/User";
const dataSource = myDataSource;
/** Check if user with given token exists , return user */
export const auth = async(req: Request,res: Response, next:NextFunction):Promise<void | express.Response<Response>>=>{
const token = req.get('Authorization');
if(!token) return res.status(401).send({Message:'token not exists'})
const user = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.token = :token", { token: token })
.getOne();
if (!user) return res.status(404).send({Message:'user not found'})
req.body={...req.body,user:user}
next()
};
/**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
if(!token) return res.status(401).send({Message:'token not exists'})
req.body={...req.body,executorStatus:false}
req.body={...req.body,authorStatus:false}
const executor = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.leftJoinAndSelect("user.tasks", "task")
.where("user.token = :token", { token: token })
.getOne();
console.log('executor', executor)
if (executor) {
req.body={...req.body,executorStatus:true}
}
const author = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.leftJoinAndSelect("user.createdTasks", "task")
.where("user.token = :token", { token: token })
.getOne();
console.log('author', author)
if (author) {
req.body={...req.body,authorStatus:true}
}
if(!author && !executor)return res.status(401).send({Message:'user is not authorized'})
next()
};
/**task finder by id, return one task */
export const taskFinderById = async (taskId:string):Promise<null | Task>=>{
const task = await dataSource
.getRepository(Task)
.findOne({
relations:{
executor:true,
author:true,
dateTimeTasks:true
},
where:{
id:taskId
}
})
return task
}
\ No newline at end of file
import {
Entity,
PrimaryGeneratedColumn,
CreateDateColumn,
BaseEntity,
ManyToOne,
} from 'typeorm';
import {Task} from './Task';
interface IDateTimeTask{
id: string;
createdAt: Date;
dateTimeStart: Date;
dateTimeDue: Date;
task: Task;
}
@Entity({name:"DateTimeTask"})
export class DateTimeTask extends BaseEntity implements IDateTimeTask{
@PrimaryGeneratedColumn("uuid")
id!: string;
@CreateDateColumn({ name: 'createdAt', type: Date, default: new Date() })
createdAt!: Date;
@CreateDateColumn({ name: 'dateTimeStart', type: Date, nullable:false })
dateTimeStart!: Date;
@CreateDateColumn({ name: 'dateTimeDue', type: Date, nullable:false })
dateTimeDue!: Date;
@ManyToOne(() => Task, (task: { dateTimeTasks: DateTimeTask[]; }) => task.dateTimeTasks,{cascade: true, onUpdate:'CASCADE',onDelete:'CASCADE',nullable:true})
task!: Task;
}
\ No newline at end of file
......@@ -5,9 +5,7 @@ import {
CreateDateColumn,
BaseEntity,
ManyToOne,
ManyToMany,
OneToMany,
JoinTable
} from 'typeorm';
import {User} from './User';
import {Project} from './Project';
......
......@@ -6,27 +6,30 @@ import {
BaseEntity,
ManyToOne,
OneToOne,
JoinTable
JoinTable,
OneToMany
} from 'typeorm';
import {User} from './User';
import {Project} from './Project';
import { DateTimeTask } from './DateTimeTask';
type taskFinishType = "opened" | "done" |"failed";
type priorityType = "A" | "B" |"C";
export type taskFinishType = "opened"| "progress" | "done" |"failed";
export type priorityType = "A" | "B" |"C";
interface ITask{
id: string;
title: string;
description: string;
note: string;
createdAt: Date;
dateTimeStart:Date| null;
dateTimeDue:Date| null;
dateTimeDeadLine: Date| null;
dateTimeFactDeadLine: Date| null;
accomplish: taskFinishType;
priority: priorityType | null;
archive:boolean,
author: User;
project:Project|null;
dateTimeTasks:DateTimeTask[]|null;
executor:User;
}
......@@ -38,16 +41,16 @@ import {
title!: string
@Column({ name: 'description', type: 'varchar', length:50,nullable: true })
description!: string
@Column({ name: 'note', type: 'varchar', length:100,nullable: true })
note!: string
@CreateDateColumn({ name: 'created_at', type: Date, default: new Date() })
createdAt!: Date;
@Column({ name: 'dateTimeStart', type: Date,nullable: true })
dateTimeStart!: Date ;
@Column({ name: 'dateTimeDue', type: Date,nullable: true })
dateTimeDue!: Date ;
@Column({ name: 'dateTimeDeadLine', type: Date,nullable: true })
dateTimeDeadLine!: Date;
@Column({ name: 'dateTimeFactDeadLine', type: Date,nullable: true })
dateTimeFactDeadLine!: Date;
@Column({ name: 'archive', type: 'varchar', length:50,nullable: false, default:false })
archive!: boolean
@Column({
type: "enum",
......@@ -76,4 +79,7 @@ import {
@ManyToOne(()=>Project,(project:{tasks: Task[]}) => project.tasks,{eager : true,nullable: true,onUpdate:'CASCADE'})
project!: Project;
@OneToMany(() => DateTimeTask, (dateTimeTask: { task: Task }) => dateTimeTask.task,{eager : true,nullable: true,onUpdate:'CASCADE'})
dateTimeTasks!: DateTimeTask[];
}
......@@ -35,9 +35,7 @@ interface IUser {
token: string;
createdAt: Date;
createdTasks:Task[];
// workerInProjects:Project[];
// adminInProjects:Project[];
membership: Member[];
members: Member[];
}
......@@ -88,7 +86,7 @@ export class User extends BaseEntity implements IUser {
@OneToMany(() => Member, (member: { user: User }) => member.user)
membership!: Member[];
members!: Member[];
// @ManyToMany(() => Project,(project: { user: User }) => project.user)
// @JoinTable()
......
import express,{Router, Request, Response,NextFunction } from 'express';
import {Task} from '../models/Task';
import {myDataSource} from '../app-data-source';
import { User } from '../models/User';
import { Member } from '../models/Member';
import { In } from 'typeorm';
import { DateTimeTask } from '../models/DateTimeTask';
import { auth, authAuthorOrExecutorOfTask } from '../helpers';
const router:Router = express.Router();
const dataSource = myDataSource;
/**task finder by id, return one task */
const taskFinderById = async (taskId:string):Promise<null | Task>=>{
const task = await dataSource
.getRepository(Task)
.findOne({
relations:{
executor:true,
author:true,
dateTimeTasks:true
},
where:{
id:taskId
}
})
return task
}
/** make copy of task in calendar view */
router.post("/make-copy",authAuthorOrExecutorOfTask, async(req:Request, res:Response):Promise<Response>=>{
const {executorStatus,taskId,start, due} = req.body
if (executorStatus){
const newDateTimeTask = new DateTimeTask();
newDateTimeTask.dateTimeStart = start
newDateTimeTask.dateTimeDue = due
newDateTimeTask.task = taskId
await newDateTimeTask.save()
const task = taskFinderById(taskId)
return res.send({task})
}
return res.send({message :"Something wrong in make-copy router"})
} )
/** change date time of copy of task in calendar view */
router.put("change-copy", authAuthorOrExecutorOfTask, async(req:Request, res: Response):Promise<Response>=>{
const {executorStatus,dateTimeTaskId,taskId, start, due} = req.body
if (executorStatus){
const dateTimeTask = await dataSource
.createQueryBuilder()
.select('dateTikeTask')
.from(DateTimeTask,'dateTimeTask')
.where("dateTimeTask.id = :dateTimeTaskId",{dateTimeTaskId})
.getOne()
if(!dateTimeTask) return res.send({message:"such dateTimeTask does not exists"})
dateTimeTask.dateTimeStart=start
dateTimeTask.dateTimeDue=due
await dateTimeTask.save()
const task = taskFinderById(taskId)
return res.send({task})
}
return res.send({message :"Something wrong in make-copy router"})
})
export default router;
......@@ -5,7 +5,7 @@ import { User } from '../models/User';
import { Member, MemberRole } from '../models/Member';
import { userInfo } from 'os';
import { Task } from '../models/Task';
import { getRepository } from 'typeorm';
import { auth } from '../helpers';
const router:Router = express.Router();
const dataSource = myDataSource;
......@@ -15,33 +15,42 @@ router.get('/',async (req:Request, res:Response): Promise<Response>=> {
const projects:Project[] = await dataSource.manager.find(Project)
return res.send({projects})
})
router.get('/my',async (req:Request, res:Response): Promise<Response>=> {
const token = req.get('Authorization');
const user = await dataSource
/**get projects were user is member, by user token */
router.get('/my',auth, async (req:Request, res:Response): Promise<Response>=> {
const user = req.body.user
const rawProjects = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.token = :token", { token: token })
.getOne();
if(!user) return res.status(404).send({Message:'user not found'})
const projects:Project[] = await dataSource.manager.find(Project)
.from(Project, "project")
.select(["project.id"])
.leftJoinAndSelect('project.members', 'member')
.loadRelationCountAndMap('project.tasks', 'project.tasks')
.leftJoinAndSelect('member.user', 'user' )
.where('member.userId = :userId',{userId:user.id})
.getMany()
const projectIds = []
if (rawProjects.length>0){
for(let project of rawProjects){
projectIds.push(project.id
)
}
}
const projects = await dataSource
.createQueryBuilder()
.from(Project, "project")
.select(["project"])
.leftJoinAndSelect('project.members', 'member')
.loadRelationCountAndMap('project.tasks', 'project.tasks')
.leftJoinAndSelect('member.user', 'user')
.where('project.id IN(:...projectIds)', {projectIds})
.getMany()
return res.send({projects})
})
router.post('/', async (req:Request, res:Response): Promise<Response> => {
/**create new project */
router.post('/',auth, async (req:Request, res:Response): Promise<Response> => {
if (!req.body) return res.status(400).send({Message:'problem in incoming req.body'})
const token = req.get('Authorization');
const {title,color}= req.body;
const user = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.token = :token", { token: token })
.getOne();
if(!user) return res.status(404).send({Message:'user not found'})
const {user, title,color}= req.body;
const member:Member = new Member;
member.user= user;
member.roleProject= MemberRole.ADMIN;
......@@ -53,7 +62,7 @@ router.post('/', async (req:Request, res:Response): Promise<Response> => {
await project.save()
return res.send({project})
})
/**get project with all FK & tasks with all FK, by project ID */
router.get("/:id",async (req:Request, res:Response): Promise<Response> => {
const project = await dataSource
.createQueryBuilder()
......@@ -81,6 +90,20 @@ router.get("/:id",async (req:Request, res:Response): Promise<Response> => {
return res.send({project, tasks})
})
/** Delete project by project ID*/
router.delete('/:projectId',async (req: Request, res: Response):Promise<Response>=>{
const projectId = req.params.projectId;
await myDataSource
.createQueryBuilder()
.delete()
.from(Project)
.where("id = :id", { id: projectId })
.execute()
return res.send({message: 'Project deleted successfully'})
})
/** Get projects were user is admin, by user ID*/
router.get('/user/:userId', async (req : Request, res : Response): Promise<Response>=>{
const userId:string = req.params.userId
......@@ -93,4 +116,57 @@ router.get('/user/:userId', async (req : Request, res : Response): Promise<Respo
return res.send({userProjects})
})
/** Add user to specific project */
router.post('/add-user/', async (req: Request, res: Response):Promise<Response>=>{
const {userId, projectId, roleProject} = req.body;
const newMember:Member = new Member();
try{
newMember.user= userId;
newMember.project=projectId
newMember.roleProject=roleProject
await newMember.save()
return res.send({newMember})
} catch(e){
return res.send({message:"add user to project failed" })
}
})
/** Remove user from specific project */
router.post('/remove-user', async (req: Request, res: Response):Promise<Response>=> {
const token = req.get('Authorization');
const {userId, projectId} = req.body;
const adminOfProject = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.leftJoinAndSelect("user.members","member")
.leftJoinAndSelect('member.project', 'project' )
.where("user.token = :token", { token })
.andWhere('project.id=:projectId',{projectId})
.andWhere('member.roleProject=:roleProject',{roleProject:'admin'})
.getOne()
if (!adminOfProject){
return res.send({message:'User is not authorized'})
}
try{
await dataSource
.createQueryBuilder()
.delete()
.from(Member)
.where("user = :userId", { userId })
.andWhere("project=:projectId",{projectId})
.execute()
return res.send({message:"User removed from project successfully" })
} catch(e){
return res.send({message:'Failed to remove user from project'})
}
})
export default router;
import express,{Router, Request, Response} from 'express';
import express,{Router, Request, Response } from 'express';
import {Task} from '../models/Task';
import {myDataSource} from '../app-data-source';
import { User } from '../models/User';
import { Member } from '../models/Member';
import { In } from 'typeorm';
import { DateTimeTask } from '../models/DateTimeTask';
import { auth, authAuthorOrExecutorOfTask, taskFinderById } from '../helpers';
const router:Router = express.Router();
const dataSource = myDataSource;
/**get all tasks */
router.get('/', async(req:Request, res:Response):Promise<Response> => {
const tasks = await dataSource
.getRepository(Task)
......@@ -17,29 +22,24 @@ router.get('/', async(req:Request, res:Response):Promise<Response> => {
return res.send({tasks})
})
router.post('/', async(req:Request, res:Response):Promise<Response>=>{
const token = req.get('Authorization');
/**create new task */
router.post('/', auth, async(req:Request, res:Response):Promise<Response>=>{
const {user,title,description,project,executor, dateTimeDeadLine,priority} = req.body;
const newTask = new Task();
const {title,description,project,dateTimeDue,dateTimeStart,accomplish,priority} = req.body;
const user = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.token = :token", { token: token })
.getOne()
if (!user) return res.status(404).send({Message:'user not found'})
newTask.title = title;
newTask.description = description;
newTask.project = project;
newTask.dateTimeDue = dateTimeDue;
newTask.dateTimeStart = dateTimeStart;
newTask.dateTimeDeadLine=dateTimeDeadLine;
newTask.author= user;
newTask.accomplish = accomplish;
newTask.executor= executor;
newTask.priority = priority;
await newTask.save();
return res.send({newTask})
await newTask.save();
return res.send({newTask});
})
/**check tasks of specific user by userID */
router.get('/user/:userId', async (req: Request, res: Response):Promise<Response>=>{
const userId = req.params.userId;
const tasks = await dataSource
......@@ -65,15 +65,10 @@ router.get('/user/:userId', async (req: Request, res: Response):Promise<Response
return res.send({tasks})
})
router.get('/my', async (req: Request, res: Response):Promise<Response>=>{
const token = req.get('Authorization');
const user = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.token = :token", { token: token })
.getOne()
if(!user) return res.status(404).send({Message:'user not found'})
/**check tasks of current user where he is author or executor, search by id*/
router.get('/my',auth, async (req: Request, res: Response):Promise<Response>=>{
const user = req.body.user
const tasks = await dataSource
.getRepository(Task)
.find({
......@@ -97,6 +92,54 @@ router.get('/my', async (req: Request, res: Response):Promise<Response>=>{
return res.send({tasks})
})
/**check tasks of projects in which current user is involved, search by token*/
router.get('/related', auth,async (req: Request, res: Response):Promise<Response>=>{
const user = req.body.user
const rawMembership = await dataSource
.createQueryBuilder()
.select("member")
.from(Member, "member")
.leftJoinAndSelect('member.user', 'user' )
.leftJoinAndSelect('member.project', 'project' )
.leftJoinAndSelect('project.tasks', 'task' )
.where("member.userId = :id", { id: user.id })
.getMany()
let projectIds:any[] = []
if (rawMembership.length>0){
for (let member of rawMembership){
const projectId = member?.project?.id
projectIds.push(projectId)
}
}
let tasks :Task[]= []
const projectsIdArray = [...new Set(projectIds)];
const searchByProjectsIdArray:object[] = []
for (let projectId of projectsIdArray){
searchByProjectsIdArray.push({id:projectId})
}
if (projectIds.length>0){
tasks = await dataSource
.getRepository(Task)
.find({
relations:{
author:true,
executor:true,
project:true
},
where:{
project:
{id:In(
projectsIdArray
)}
}
})
}
return res.send({tasks})
})
/**delete of task by task id */
router.delete('/:taskId',async (req: Request, res: Response):Promise<Response>=>{
const taskId = req.params.taskId;
await myDataSource
......@@ -109,49 +152,57 @@ router.delete('/:taskId',async (req: Request, res: Response):Promise<Response>=>
})
router.put('/',async(req:Request, res:Response)=> {
const token = req.get('Authorization');
const user = await dataSource
.createQueryBuilder()
.select("user")
.from(User, "user")
.where("user.token = :token", { token: token })
.getOne()
if (!user) return res.status(404).send({Message:'user not found'})
const {id,title,description,project,dateTimeDue,dateTimeStart,executors,accomplish,priority} = req.body;
const task = await dataSource
.createQueryBuilder()
.select("task")
.from(Task, "task")
.where("task.id = :id", { id })
.getOne()
/**change of task by task id */
router.put('/',authAuthorOrExecutorOfTask,async(req:Request, res:Response)=> {
const {authorStatus,executorStatus,taskId,title,description,note, archive,project,dateTimeTaskId,start,due,executor,accomplish,dateTimeDeadLine, dateTimeFactDeadLine,priority} = req.body;
const task = await taskFinderById(taskId)
if (!task) return res.status(404).send({Message:'task not found'})
task.title= title
task.description= description
task.project= project
task.dateTimeDue= dateTimeDue
task.dateTimeStart= dateTimeStart
task.author=user
// task.executors=executors
let dateTimeTask = null;
if (dateTimeTaskId) {
const dateTimeTaskData = await dataSource
.createQueryBuilder()
.select("dateTimeTask")
.from(DateTimeTask, "dateTimeTask")
.where("dateTimeTask,id=:dateTimeTaskId",{dateTimeTaskId})
.getOne()
if (!dateTimeTask) return res.status(404).send({Message:'dateTimeTask not found'})
dateTimeTask = dateTimeTaskData
}
if (authorStatus){
task.title = title
task.description= description
task.archive=archive
task.dateTimeDeadLine=dateTimeDeadLine;
task.project=project
task.executor=executor
task.priority= priority
}
if(executorStatus && dateTimeTask!==null){
dateTimeTask.dateTimeStart = start
dateTimeTask.dateTimeDue = due
await dateTimeTask.save()
task.note = note
task.dateTimeFactDeadLine= dateTimeFactDeadLine
}
task.accomplish= accomplish
task.priority= priority
await task.save()
res.send({message:'update task successfully'})
})
/** search all tasks on given projects: project Id array[] */
router.post('/project',async (req: Request, res: Response):Promise<Response>=>{
let projectArray :string[]= req.body;
console.log('projectArray ', projectArray)
if (projectArray.length===0) {
const rawTasks = await
dataSource
.getRepository(Task)
.find({
relations:{
// executors:true,
executor:true,
author:true,
project:true
}
......@@ -171,4 +222,6 @@ router.post('/project',async (req: Request, res: Response):Promise<Response>=>{
export default router;
......@@ -50,7 +50,7 @@ router.post('/', upload.single("avatar"), async (req : Request, res : Response):
email: user.email
})
return res.send({userToFront})
})
})
router.post('/sessions/', async (req : Request, res : Response):Promise<object> => {
......
......@@ -4,6 +4,7 @@ import users from './routers/users';
import tasks from './routers/tasks';
import projects from './routers/projects';
import {myDataSource} from './app-data-source';
import copyTasks from './routers/copyTasks';
myDataSource
......@@ -22,6 +23,7 @@ app.use(express.json());
const PORT = 8000;
app.use('/users',users)
app.use('/tasks',tasks)
app.use('/copy-tasks',copyTasks)
app.use('/projects',projects)
const run = async() => {
......
import { Grid } from "@mui/material";
import { memo, useEffect, useMemo, useState } from "react";
import { memo, useMemo} from "react";
import CalendarStandartCell from "../CalendarStandartCell.js/CalendarStandartCell";
import CalendarTask from "../CalendarTask/CalendarTask";
import EmptyBox from "./EmptyBox/EmptyBox";
import { getAvailableTasks, getBoxesInLine, getLinesInDay, getSortedTasks } from "./Helpers";
const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, handleOpen, modal, setCurrentTask, year, month, tasks, day, hourFormat, setCurrentLine, currentLine, dragTaskHandler, increaseTaskHandler, reduceTaskHandler, createCopyTask, setCopyTask, copyTask}) => {
const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, handleOpen, modal, setCurrentTask, year, month, tasks, day, hourFormat, setCurrentLine, currentLine, dragTaskHandler, createCopyTask, setCopyTask, copyTask}) => {
const hours = useMemo(()=>{
return hoursInDay.map((hour)=>parseInt(hour.split(':')[0]))},
[hoursInDay])
const availableTasks = useMemo(() => {
const tasksInDay = tasks.filter((task)=> {
if (year === task.infoForCell.startYear) {
if (month + 1 === task.infoForCell.startMonth) {
if (day.dayNumber === task.infoForCell.startDay) {
return task
} else {return false}
} else {return false}
} else {return false}
})
return tasksInDay
return getAvailableTasks(tasks, year, month, day.dayNumber)
}, [tasks, month, year, day.dayNumber])
const sortedTasks = useMemo(() => {
if (availableTasks.length) {
const newSortedArr = [...availableTasks].sort(function(a,b){
const durattionFirstDate = a.infoForCell.endHour - a.infoForCell.startHour
const durattionSecondDate = b.infoForCell.endHour - b.infoForCell.startHour
return durattionSecondDate - durattionFirstDate;
})
return newSortedArr
}
return getSortedTasks(availableTasks)
}, [availableTasks])
const linesInDay = useMemo(() => {
let hourDiff
let hourDiffEnd
const lines = []
if (hourFormat) {
hourDiff = 1
hourDiffEnd = 0
} else {
hourDiff = 2
hourDiffEnd = 1
}
if (availableTasks.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
for (let k = 0; k < sortedTasks.length; k++) {
let skipLine = false
for (let j = 0; j < lines.length; j++) {
const line = lines[j]
const task = sortedTasks[k]
if (skipLine) {
skipLine = false
break;
}
for (let i = 0; i < line.length; i++) {
const hour = hours[i]
let havePlace = true
if (((task.infoForCell.endHour <= hour || task.infoForCell.startHour <= hour) && (task.infoForCell.endHour > hour))
|| (!hourFormat && task.infoForCell.startHour >= hour && task.infoForCell.endHour < hour + hourDiff)
|| (!hourFormat && task.infoForCell.startHour === hour + hourDiffEnd && task.infoForCell.endHour > hour)
|| (task.infoForCell.endMinute <= 59 && task.infoForCell.endHour === hour)) {
if (!isNaN(line[i])) {
for (let a = 0; a < hours.length; a++) {
const hour = hours[a]
if ((task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour + hourDiffEnd) || (!hourFormat && task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour)) {
if (isNaN(line[a])) {
havePlace = false
break;
}
}
}
if (!havePlace) {
if (j + 1 === lines.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
}
havePlace = true
break;
}
line[i] += `-${k}`
if ((task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour + hourDiffEnd)
|| (!hourFormat && task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour)) {
skipLine = true
break;
}
} else {
if (j + 1 === lines.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
}
break;
}
}
}
}
}
}
return lines
}, [availableTasks.length, hourFormat, hours, hoursInDay, sortedTasks])
const getBoxesInLine = (line) => {
if (line) {
let xs = 12/hoursInDay.length
const boxes = []
for (let i = 0; i < line.length; i++) {
if (!isNaN(line[i])) {
// if (boxes[boxes.length -1]?.task === null) {
// boxes[boxes.length -1].xs += xs
// } else {
boxes.push({xs: xs, task: null, hour: line[i]})
// }
} else {
const task = sortedTasks[line[i].split('-')[1]]
const taskIsThere = boxes.find((taskFind)=>{
if (taskFind?.task?.id === task.id) return taskFind
})
if (taskIsThere) {
taskIsThere.xs +=xs
} else {
boxes.push({
xs: xs,
task: sortedTasks[line[i].split('-')[1]]})
}
}
}
return boxes
}
}
return getLinesInDay(availableTasks, sortedTasks, hoursInDay, hours, hourFormat)
}, [availableTasks, hourFormat, hours, hoursInDay, sortedTasks])
return <>
<Grid
......@@ -136,9 +29,28 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
item
xs={10.8}
align='center'
sx={{position: 'relative'}}
>
{hoursInDay.map((hour, i)=>{
return (
<CalendarStandartCell
linesInDay={linesInDay}
key={i}
item xs={xs}
createTaskInCellHandler={createTaskInCellHandler}
hours={hour}
dragTaskHandler={dragTaskHandler}
dayNumber={day.dayNumber}
currentTask={currentTask}
handleOpen={handleOpen}
modal={modal}
>
</CalendarStandartCell>
)
})}
<Grid sx={{position: 'absolute', top: '0'}} container item xs={12}>
{linesInDay?.map((line, i)=>{
const boxes = getBoxesInLine(line)
const boxes = getBoxesInLine(line, hoursInDay, sortedTasks)
return(
<Grid key={i} container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginBottom: '5px'}}>
{boxes.map((box, index)=>{
......@@ -156,11 +68,8 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
hour={parseInt(hours[index])}
line={day.dayNumber}
task={box.task}
hourFormat={hourFormat}
setCurrentTask={setCurrentTask}
handleOpen={handleOpen}
increaseTaskHandler={increaseTaskHandler}
reduceTaskHandler={reduceTaskHandler}
setCopyTask={setCopyTask}
/>
</Grid>)
......@@ -182,8 +91,9 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
})}
</Grid>)
})}
</Grid>
<Grid container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginBottom: '5px'}}>
<Grid container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginBottom: '5px', position: 'absolute', bottom: '0'}}>
{hoursInDay.map((hour, i)=>{
const hourNumber = parseInt(hour)
return(<EmptyBox
......@@ -205,5 +115,8 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
</>
};
export default memo(CalendarRowDay);
export default memo(CalendarRowDay, (prevProps, nextProps)=>{
if(!prevProps.modal) return false
if(nextProps.modal) return true
});
......@@ -34,7 +34,12 @@ const EmptyBox = ({hourNumber, handleOpen, dayNumber, xs, dragTaskHandler, modal
onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}}
onClick={(e)=>{onClickHandler(e, dayNumber, hourNumber)}}
item xs={xs} sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)'}}>
item xs={xs} sx={{
height: '35px',
backgroundColor: 'rgb(0,0,0,0)',
zIndex: '6',
cursor: copyTask ? 'pointer' : 'default'
}}>
{isThisCell ?
<DefaultTask/> : null}
</Grid>)
......
const taskIsAvailableInCell = (task, hour, hourDiffEnd, hourDiff, hourFormat) => {
if (((task.infoForCell.endHour <= hour || task.infoForCell.startHour <= hour) && (task.infoForCell.endHour > hour))
|| (!hourFormat && task.infoForCell.startHour >= hour && task.infoForCell.endHour < hour + hourDiff)
|| (!hourFormat && task.infoForCell.startHour === hour + hourDiffEnd && task.infoForCell.endHour > hour)
|| (task.infoForCell.endMinute <= 59 && task.infoForCell.endHour === hour)) {
return true
} else {
return false
}
}
const lastPlaceInLineForTask = (task, hour, hourDiffEnd, hourFormat) => {
if ((task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour + hourDiffEnd) || (!hourFormat && task.infoForCell.endMinute === 59 && task.infoForCell.endHour === hour)) {
return true
} else {
return false
}
}
export const getLinesInDay = (availableTasks, sortedTasks, hoursInDay, hours, hourFormat) => {
let hourDiff
let hourDiffEnd
const lines = []
if (hourFormat) {
hourDiff = 1
hourDiffEnd = 0
} else {
hourDiff = 2
hourDiffEnd = 1
}
if (availableTasks.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
for (let k = 0; k < sortedTasks.length; k++) {
let skipLine = false
for (let j = 0; j < lines.length; j++) {
const line = lines[j]
const task = sortedTasks[k]
if (skipLine) {
skipLine = false
break;
}
for (let i = 0; i < line.length; i++) {
const hour = hours[i]
let havePlace = true
if (taskIsAvailableInCell(task, hour, hourDiffEnd, hourDiff, hourFormat)) {
if (!isNaN(line[i])) {
for (let a = 0; a < hours.length; a++) {
const hour = hours[a]
if (lastPlaceInLineForTask(task, hour, hourDiffEnd, hourFormat)) {
if (isNaN(line[a])) {
havePlace = false
break;
}
}
}
if (!havePlace) {
if (j + 1 === lines.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
}
havePlace = true
break;
}
line[i] += `-${k}`
if (lastPlaceInLineForTask(task, hour, hourDiffEnd, hourFormat)) {
skipLine = true
break;
}
} else {
if (j + 1 === lines.length) {
lines.push(hoursInDay.map((hour)=>parseInt(hour.split(':')[0])))
}
break;
}
}
}
}
}
}
return lines
}
export const getSortedTasks = (availableTasks) => {
if (availableTasks.length) {
const newSortedArr = [...availableTasks].sort(function(a,b){
const durattionFirstDate = a.infoForCell.endHour - a.infoForCell.startHour
const durattionSecondDate = b.infoForCell.endHour - b.infoForCell.startHour
return durattionSecondDate - durattionFirstDate;
})
return newSortedArr
}
}
export const getAvailableTasks = (tasks, year, month, dayNumber) => {
const tasksInDay = tasks.filter((task)=> {
if (year === task.infoForCell.startYear) {
if (month + 1 === task.infoForCell.startMonth) {
if (dayNumber === task.infoForCell.startDay) {
return task
} else {return false}
} else {return false}
} else {return false}
})
return tasksInDay
}
export const getBoxesInLine = (line, hoursInDay, sortedTasks) => {
if (line) {
let xs = 12/hoursInDay.length
const boxes = []
for (let i = 0; i < line.length; i++) {
if (!isNaN(line[i])) {
// if (boxes[boxes.length -1]?.task === null) {
// boxes[boxes.length -1].xs += xs
// } else {
boxes.push({xs: xs, task: null, hour: line[i]})
// }
} else {
const task = sortedTasks[line[i].split('-')[1]]
const taskIsThere = boxes.find((taskFind)=>{
if (taskFind?.task?.id === task.id) return taskFind
return false
})
if (taskIsThere) {
taskIsThere.xs +=xs
} else {
boxes.push({
xs: xs,
task: sortedTasks[line[i].split('-')[1]]})
}
}
}
return boxes
}
}
\ No newline at end of file
......@@ -3,15 +3,14 @@ import { memo, useEffect, useState } from "react";
import DefaultTask from "../DefaultTask/DefaultTask";
const cellClass = {
transition: '0.3s',
position: 'relative',
height: '35px',
}
const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, divRef, dragTaskHandler, linesInDay}) => {
const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, dragTaskHandler, linesInDay}) => {
const [isThisCell, setIsThisCell] = useState(false)
const cellClass = {
position: 'relative',
height: linesInDay?.length ? `${40*linesInDay.length+35}px` : `${35+35}px`,
borderRight: '1px solid black',
}
useEffect(()=>{
if(!modal) {
setIsThisCell(false);
......@@ -27,21 +26,26 @@ const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCell
e.preventDefault();
dragTaskHandler(dayNumber, parseInt(hours.split(':')[0]))
}
const onClickHandler = (e) => {
if (!linesInDay?.length) {
createTaskInCellHandler(dayNumber, hours);
setIsThisCell(true);
handleOpen(e)
}
}
return <>
<Grid
item xs={xs}
sx={cellClass}
onClick={(e)=>{createTaskInCellHandler(dayNumber, hours); setIsThisCell(true); handleOpen(e)}}
onClick={(e)=>{onClickHandler(e)}}
onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}}
>
{children}
{isThisCell ?
<DefaultTask/> : null}
<div style={{position: 'absolute', height: children ? divRef : 0, width: '1px', backgroundColor: 'black', right: '0', top: '0', zIndex: '3'}}>
</div>
</Grid>
</>
};
......
.resizeable {
border: 2px solid #533535;
border-radius: 3px;
display: flex;
justify-content: center;
align-items: center;
min-width: 15px;
min-height: 15px;
}
.resizer {
position: absolute;
background: black;
}
.resizer-r {
cursor: col-resize;
height: 100%;
right: 0;
top: 0;
width: 5px;
}
.resizer-l {
cursor: col-resize;
height: 100%;
left: 0;
top: 0;
width: 5px;
}
\ No newline at end of file
import { Button, Grid} from "@mui/material";
import React, { memo, useEffect, useRef, useState} from "react";
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import { Grid} from "@mui/material";
import React, { memo, useEffect, useState} from "react";
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
const arrowClass = {
height: '20px',
cursor: 'pointer',
transition: '0.5s',
"&:hover": {
transition: '0.5s',
transform: 'scale(1.2)'
}
}
const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, increaseTaskHandler, reduceTaskHandler, hourFormat, setCopyTask}) => {
const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, setCopyTask}) => {
const [color, setColor] = useState('')
useEffect(() => {
......@@ -28,7 +17,15 @@ const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, i
const onClickTaskHandler = (e, task) => {
e.stopPropagation();
setCurrentTask(task);
setCurrentTask((prevState)=>{
return {
...task,
infoForCell: {
...task.infoForCell,
endHour: task.infoForCell.endHour + 1
}
}
});
handleOpen(e)
}
......@@ -49,45 +46,16 @@ const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, i
onDragLeave={(e)=>{dragLeaveHandler(e)}}
onDragStart={(e)=>{dragStartHandler(e, line, task)}}
onDragEnd={(e)=>{dragEndHandler(e)}}
sx={{ position: 'relative', height: '30px', backgroundColor: color, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', borderRadius: '10px', margin: '5px 10px', display: 'flex', justifyContent: 'flex-start', alignItems: 'center', paddingLeft: '5px', zIndex: '5', justifyContent: 'space-between'}}
sx={{ position: 'relative', height: '30px', backgroundColor: color, borderRadius: '10px', margin: '5px 10px', display: 'flex', alignItems: 'center', zIndex: '5', justifyContent: 'space-between', padding: '0 15px'}}
onClick={(e)=>{onClickTaskHandler(e, task)}}
>
{task.infoForCell.endHour === task.infoForCell.startHour && hourFormat ?
<>
<div tyle={{display: 'flex', alignItems: 'center'}}>
<div style={{display: 'flex', alignItems: 'center'}}>
<ArrowBackIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); increaseTaskHandler(line, task, true)}}/>
<ArrowForwardIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); reduceTaskHandler(line, task, true)}}/>
<ArrowBackIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); reduceTaskHandler(line, task, false)}}/>
<ArrowForwardIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); increaseTaskHandler(line, task, false)}}/>
</div>
</div>
<div>
<span>
<span style={{maxWidth: '60%', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>
{task.title}
</span>
</div>
</>
</span>
<ContentCopyIcon sx={{width: '20px', cursor: 'pointer'}} onClick={(e)=>{e.stopPropagation(); setCopyTask(task)}}>
</ContentCopyIcon>
:
<>
<div style={{display: 'flex', alignItems: 'center'}}>
<ArrowBackIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); increaseTaskHandler(line, task, true)}}/>
<ArrowForwardIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); reduceTaskHandler(line, task, true)}}/>
</div>
<span>
{task.title}
</span>
<div style={{display: 'flex', alignItems: 'center'}}>
<Button sx={{color:'black', fontWeight:'600'}} onClick={(e)=>{e.stopPropagation(); setCopyTask(task)}}>
Copy
</Button>
<ArrowBackIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); reduceTaskHandler(line, task, false)}}/>
<ArrowForwardIcon sx={arrowClass} onClick={(e)=>{e.stopPropagation(); increaseTaskHandler(line, task, false)}}/>
</div>
</>
}
</Grid>
</>)
};
......
import { Box } from "@mui/material";
import { memo } from "react";
const DefaultTaskStyles = {
position: 'relative',
height: '30px',
backgroundColor: 'lightgreen',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
borderRadius: '10px',
margin: '5px 10px',
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
paddingLeft: '5px',
zIndex: '5'
}
const DefaultTask = ({}) => {
return (<>
<Box
sx={{ position: 'relative', height: '30px', backgroundColor: 'lightgreen', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', borderRadius: '10px', margin: '5px 10px', display: 'flex', justifyContent: 'flex-start', alignItems: 'center', paddingLeft: '5px', zIndex: '5'}}
sx={DefaultTaskStyles}
>
<span>
Задача
......
import { FormControlLabel, Switch} from "@mui/material";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Box, FormControlLabel, Switch} from "@mui/material";
import { useState } from "react";
import CalendarRow from "./CalendarRow/CalendarRow";
import CalendarSmallCell from "./CalendarSmallCell/CalendarSmallCell";
import CalendarStandartCell from "./CalendarStandartCell.js/CalendarStandartCell";
......@@ -8,15 +8,15 @@ import MonthCalendarModalContent from "../MonthCalendarModalContent/MonthCalenda
import CalendarRowDay from "./CalendarRowDay/CalendarRowDay";
function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, currentTask, setCurrentTask, hourFormat, setHourFormat, onChangeCurrentTaskHandler, sendNewTaskHandler, deleteTaskHandler, cellSizes, hoursInDay, daysInMonth, dragTaskHandler, increaseTaskHandler, reduceTaskHandler, createCopyTask, setCopyTask, copyTask}) {
function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, currentTask, setCurrentTask, hourFormat, setHourFormat, onChangeCurrentTaskHandler, sendNewTaskHandler, deleteTaskHandler, cellSizes, hoursInDay, daysInMonth, dragTaskHandler, createCopyTask, setCopyTask, copyTask}) {
const [currentLine, setCurrentLine] = useState('')
const [modal, setModal] = useState({open:false, y: 0, x: 0,});
const handleOpen = (e) => {
setModal( {
open: true,
yPage: e.clientY,
xPage: e.clientX,
yClickСordinates: e.clientY,
xClickСordinates: e.clientX,
yDivClick: e.nativeEvent.offsetY,
xDivClick: e.nativeEvent.offsetX,
yDiv: e.target.offsetHeight,
......@@ -29,37 +29,27 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
setCurrentTask({})
};
const divRef = useRef(null)
const [divHeight, setDivHeight] = useState('')
useEffect(() => {
if (divRef) {
setDivHeight(()=>{
return divRef.current?.offsetHeight
})
}
}, [divRef.current?.offsetHeight, hourFormat, month, tasks]);
return (
<div ref={divRef} style={{marginBottom: '30px'}}>
<CalendarRow
>
<CalendarSmallCell xs={1.2}>
<FormControlLabel
control={<Switch color="primary" checked={hourFormat} onChange={()=>{setHourFormat(()=>!hourFormat)}}/>}
label="1 час"
labelPlacement="end"
/>
</CalendarSmallCell>
{hoursInDay?.map((hours, i)=>{
return (
<CalendarStandartCell key={i} xs={cellSizes.standarCell} divRef={divHeight}>
{hours}
</CalendarStandartCell>
)
})}
</CalendarRow>
<Box style={{marginBottom: '30px'}}>
<div style={{position: 'sticky', top: '0px', zIndex: '10', backgroundColor: 'lightgrey'}}>
<CalendarRow
>
<CalendarSmallCell xs={1.2}>
<FormControlLabel
control={<Switch color="primary" checked={hourFormat} onChange={()=>{setHourFormat(()=>!hourFormat)}}/>}
label="1 час"
labelPlacement="end"
/>
</CalendarSmallCell>
{hoursInDay?.map((hours, i)=>{
return (
<CalendarStandartCell key={i} xs={cellSizes.standarCell}>
{hours}
</CalendarStandartCell>
)
})}
</CalendarRow>
</div>
{daysInMonth?.map((day, i)=>{
return (
<CalendarRow
......@@ -83,8 +73,6 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
tasks={tasks}
day={day}
hourFormat={hourFormat}
increaseTaskHandler={increaseTaskHandler}
reduceTaskHandler={reduceTaskHandler}
createCopyTask={createCopyTask}
copyTask={copyTask}
setCopyTask={setCopyTask}
......@@ -101,12 +89,14 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
title={currentTask.title}
description={currentTask.description}
priority={currentTask.priority}
startHour={currentTask.infoForCell?.startHour}
endHour={currentTask.infoForCell?.endHour}
onChangeCurrentTaskHandler={(e)=>{ onChangeCurrentTaskHandler(e)}}
sendNewTaskHandler={()=>{sendNewTaskHandler(); handleClose()}}
deleteTaskHandler={()=>{deleteTaskHandler(currentTask.id); handleClose()}}
/>
</ModalTask>
</div>
</Box>
);
}
......
......@@ -3,7 +3,7 @@ import { Box, Typography } from '@mui/material';
import { memo } from 'react';
import MonthDecrementButton from './MonthDecrementButton/MonthDecrementButton';
import MonthIncrementButton from './MonthIncrementButton/MonthIncrementButton';
function MonthAndYearInfo({getCurrentMonthString, year, incrementMonth, decrementMonth}) {
function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMonth}) {
return (
<>
......@@ -26,7 +26,7 @@ function MonthAndYearInfo({getCurrentMonthString, year, incrementMonth, decremen
justifyContent: 'center',
}}
>
{getCurrentMonthString}
{currentMonthString}
</Typography>
<Typography align='center'>{year}</Typography>
</Box>
......
......@@ -3,10 +3,10 @@ import { Box } from '@mui/system';
import MonthAndYearInfo from './MonthAndYearInfo/MonthAndYearInfo';
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: 'Неделя'}]
function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementMonth, calendarType, onChangeWorkerHandler, onChangeCalendarTypeHandler, worker, year}) {
function MonthCalendarHeader({ currentMonthString, decrementMonth, incrementMonth, calendarType, onChangeWorkerHandler, onChangeCalendarTypeHandler, worker, year}) {
return (
<>
......@@ -15,7 +15,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM
<Toolbar>
<MonthAndYearInfo
getCurrentMonthString={getCurrentMonthString}
currentMonthString={currentMonthString}
decrementMonth={decrementMonth}
incrementMonth={incrementMonth}
year={year}
......@@ -27,7 +27,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM
id={'worker'}
items={workers}
/>
<div style={{marginLeft: '20px'}}>
<СustomSelect
value={calendarType}
onChange={(e)=>{onChangeCalendarTypeHandler(e)}}
......@@ -35,6 +35,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM
id={'calendar-type'}
items={types}
/>
</div>
</Toolbar>
</AppBar>
</Box>
......
import { Button, FormControl, InputLabel, MenuItem, Select, TextField } from "@mui/material";
import { Button, TextField } from "@mui/material";
import { memo } from "react";
import CustomSelect from '../UI/СustomSelect/СustomSelect'
function MonthCalendarModalContent({title, onChangeCurrentTaskHandler, description, priority, sendNewTaskHandler, deleteTaskHandler}) {
const priorities = [{value: null, text: '--Приоритет--'}, {value: 'A', text: 'A'}, {value: 'B', text: 'B'}, {value: 'C', text: 'C'}]
function MonthCalendarModalContent({title, onChangeCurrentTaskHandler, description, priority, sendNewTaskHandler, deleteTaskHandler, startHour, endHour}) {
return (<>
<TextField
id="task-description-title"
value={title}
label="Название"
variant="outlined"
sx={{marginBottom: '30px'}}
name='title'
onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
/>
<TextField
id="task-description"
multiline
rows={4}
value={description}
label="Описание"
variant="outlined"
sx={{marginBottom: '30px'}}
name='description'
onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
/>
<FormControl variant="outlined" sx={{width: '160px', marginBottom: '30px'}}>
<InputLabel id="priority-type-label">Приоритет</InputLabel>
<Select
labelId="priority-type-label"
id="priority-type"
label="Приоритет"
sx={{width: '160px'}}
value={priority}
name='priority'
onChange={onChangeCurrentTaskHandler}
>
<MenuItem value={null}>
<em>-- Выберите Приоритет --</em>
</MenuItem>
<MenuItem value={"A"}>A</MenuItem>
<MenuItem value={"B"}>B</MenuItem>
<MenuItem value={"C"}>C</MenuItem>
</Select>
</FormControl>
<Button sx={{marginRight: '40px'}} onClick={sendNewTaskHandler}>Сохранить</Button>
<Button onClick={deleteTaskHandler}>Удалить</Button>
<TextField
id="task-description-title"
value={title}
label="Название"
variant="outlined"
sx={{marginBottom: '30px'}}
name='title'
onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
/>
<TextField
id="task-description"
multiline
rows={4}
value={description}
label="Описание"
variant="outlined"
sx={{marginBottom: '30px'}}
name='description'
onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
/>
<CustomSelect
defaultValue={null}
value={priority}
name={'priority'}
variant={'outlined'}
onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
label={'Приоритет'}
id={'priority-type'}
items={priorities}
/>
<div style={{display: 'flex', gap: '20px', margin: '20px 0'}}>
<TextField
id="task-startHour"
value={startHour}
label="От"
variant="outlined"
name='startHour'
onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
/>
<TextField
id="task-endHour"
value={endHour}
label="До"
variant="outlined"
name='endHour'
onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
/>
</div>
<div style={{display: 'flex', gap: '20px', margin: '10px 0'}}>
<Button onClick={sendNewTaskHandler}>Сохранить</Button>
<Button onClick={deleteTaskHandler}>Удалить</Button>
</div>
</>);
}
export default MonthCalendarModalContent;
\ No newline at end of file
export default memo(MonthCalendarModalContent);
\ No newline at end of file
import Box from '@mui/material/Box';
import Modal from '@mui/material/Modal';
import { useEffect, useRef, useState } from 'react';
export default function ModalTask({modal, handleClose, children}) {
const [windowDimenion, detectHW] = useState({
winWidth: window.innerWidth,
winHeight: window.innerHeight,
})
const detectSize = () => {
detectHW({
winWidth: window.innerWidth,
winHeight: window.innerHeight,
})
}
useEffect(() => {
window.addEventListener('resize', detectSize)
return () => {
window.removeEventListener('resize', detectSize)
}
}, [windowDimenion])
const modalRef = useRef('')
const getYCordinatesToModal = () => {
if (windowDimenion.winHeight > modal.yClickСordinates + 450) {
return modal.yClickСordinates - modal.yDiv - modal.yDivClick
} else {
return modal.yClickСordinates - modal.yDiv - modal.yDivClick - ((modal.yClickСordinates + 450) - windowDimenion.winHeight) - 30
}
}
const getXCordinatesToModal = () => {
if (windowDimenion.winWidth > modal.xClickСordinates + 270 + modal.xDiv) {
return modal.xClickСordinates + modal.xDiv - modal.xDivClick + 10
} else {
return windowDimenion.winWidth - (windowDimenion.winWidth - modal.xClickСordinates) - modal.xDivClick - 270 - 80
}
}
const style = {
display: 'flex',
flexDirection: 'column',
position: 'absolute',
top: modal.yPage - modal.yDiv - modal.yDivClick,
left: modal.xPage + modal.xDiv - modal.xDivClick + 10,
width: 250,
height: 350,
top: getYCordinatesToModal(),
left: getXCordinatesToModal(),
width: 270,
height: 450,
bgcolor: 'background.paper',
border: '2px solid #000',
boxShadow: 24,
......@@ -24,7 +62,7 @@ export default function ModalTask({modal, handleClose, children}) {
aria-describedby="modal-modal-description"
BackdropProps={{ style: { backgroundColor: 'rgba(255,255,255, 0)' } }}
>
<Box sx={style}>
<Box sx={style} ref={modalRef}>
{children}
</Box>
</Modal>
......
......@@ -8,7 +8,7 @@ import Select from "@mui/material/Select";
export default function BasicSelect({value,label,name,onChange,task,items}) {
return (
<Box sx={{ minWidth: 60 }}>
<Box sx={{ minWidth: 60, m: 0}}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label"></InputLabel>
<Select
......
import { FormControl, InputLabel, MenuItem, Select} from '@mui/material';
import { memo } from 'react';
function СustomSelect({value, onChange, label, variant='standard', items, id}) {
function СustomSelect({value, onChange, label, variant='standard', items, id, defaultValue, name}) {
return (
<>
<FormControl variant={variant} sx={{ m: 1, minWidth: 120 }}>
<FormControl variant={variant} sx={{ m: 0, minWidth: 120 }}>
<InputLabel id={`${id}-select-label`}>{label}</InputLabel>
<Select
labelId={`${id}-select-label`}
......@@ -13,6 +13,8 @@ function СustomSelect({value, onChange, label, variant='standard', items, id})
value={value}
onChange={onChange}
label={label}
name={name}
defaultValue={defaultValue}
>
{items.map((item, i)=>{
return (
......
export const getDaysInMonth = (dateNow) => {
const newDaysInMonth = []
const daysInMonthNumber = new Date(dateNow.year, dateNow.month + 1, 0).getDate()
for (let i = 1; i <= daysInMonthNumber; i++) {
const dayOfWeekNumber = new Date(dateNow.year, dateNow.month, i).getDay()
const getDayOfWeekString = ["ВС","ПН","ВТ","СР","ЧТ","ПТ","СБ"][dayOfWeekNumber]
newDaysInMonth.push({dayNumber: i, dayOfWeek: getDayOfWeekString})
}
return newDaysInMonth
}
export const getCurrentMonthString = (dateNow) => {
return ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь", "Декабрь"][dateNow.month];
}
export const dateToISOLikeButLocal = (date) => {
const offsetMs = date.getTimezoneOffset() * 60 * 1000;
const msLocal = date.getTime() - offsetMs;
const dateLocal = new Date(msLocal);
const iso = dateLocal.toISOString();
return iso;
}
\ No newline at end of file
......@@ -46,6 +46,7 @@ export const fetchCalendarTasks = () => {
}
}
};
export const fetchAllTasks = () => {
return async (dispatch) => {
dispatch(fetchCalendarTasksRequest());
......@@ -69,34 +70,26 @@ const addTaskFailure = (error) => {
return {type: ADD_NEW_TASK_FAILURE, error}
};
// export const addTask = (task) => {
// return async (dispatch, getState) => {
// dispatch(addTaskRequest());
// const token = getState().users?.user?.token;
// try {
// await axios.post("/tasks", task, {
// headers: {
// Authorization: token,
// },
// });
// dispatch(addTaskSuccess());
// dispatch(fetchCalendarTasks());
// } catch (error) {
// dispatch(addTaskFailure(error.response.data));
// }
// };
// };
export const addCalendarTask = (task) => {
return async (dispatch, getState) => {
dispatch(addTaskRequest());
try {
await axios.post("/tasks", task);
dispatch(addTaskSuccess())
dispatch(fetchCalendarTasks())
} catch (error) {
dispatch(addTaskFailure(error.response.data));
}
}
}
export const addTask = (task) => {
return async (dispatch, getState) => {
dispatch(addTaskRequest());
// const token = getState().users?.user?.token;
try {
await axios.post("/tasks", task);
dispatch(addTaskSuccess())
dispatch(fetchAllTasks())
dispatch(fetchCalendarTasks())
} catch (error) {
dispatch(addTaskFailure(error.response.data));
}
......@@ -122,6 +115,18 @@ export const editTask = (task) => {
await axios.put("/tasks", task);
dispatch(editTaskSuccess())
dispatch(fetchAllTasks())
} catch (error) {
dispatch(editTaskFailure(error.response.data));
}
}
}
export const editCalendarTask = (task) => {
return async (dispatch, getState) => {
dispatch(editTaskRequest());
try {
await axios.put("/tasks", task);
dispatch(editTaskSuccess())
dispatch(fetchCalendarTasks())
} catch (error) {
dispatch(editTaskFailure(error.response.data));
......@@ -144,11 +149,9 @@ const deleteTaskFailure = (error) => {
export const deleteTask = (taskId) => {
return async (dispatch, getState) => {
dispatch(deleteTaskRequest());
// const token = getState().users?.user?.token;
try {
await axios.delete(`/tasks/${taskId}`);
dispatch(deleteTaskSuccess())
dispatch(fetchCalendarTasks())
dispatch(fetchAllTasks())
} catch (error) {
dispatch(deleteTaskFailure(error.response.data));
......@@ -156,6 +159,20 @@ export const deleteTask = (taskId) => {
}
}
export const deleteCalendarTask = (taskId) => {
return async (dispatch, getState) => {
dispatch(deleteTaskRequest());
try {
await axios.delete(`/tasks/${taskId}`);
dispatch(deleteTaskSuccess())
dispatch(fetchCalendarTasks())
} catch (error) {
dispatch(deleteTaskFailure(error.response.data));
}
}
}
const fetchTasksByProjectRequest = () => {
return {type: FETCH_TASKS_BY_PROJECT_REQUEST}
......
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