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'; ...@@ -3,6 +3,7 @@ import {User} from './models/User';
import {Task} from './models/Task'; import {Task} from './models/Task';
import { Project } from "./models/Project"; import { Project } from "./models/Project";
import { Member } from "./models/Member"; import { Member } from "./models/Member";
import { DateTimeTask } from "./models/DateTimeTask";
export const myDataSource = new DataSource({ export const myDataSource = new DataSource({
type: "postgres", type: "postgres",
...@@ -11,7 +12,7 @@ export const myDataSource = new DataSource({ ...@@ -11,7 +12,7 @@ export const myDataSource = new DataSource({
username: "pluser", username: "pluser",
password: "pluser", password: "pluser",
database: "planner", database: "planner",
entities: [User,Task,Project,Member], entities: [User,Task,Project,Member,DateTimeTask],
logging: true, logging: true,
synchronize: true, // in build switch to false synchronize: true, // in build switch to false
migrationsRun: false migrationsRun: false
......
...@@ -2,13 +2,47 @@ import { myDataSource } from "./app-data-source"; ...@@ -2,13 +2,47 @@ import { myDataSource } from "./app-data-source";
import { User, UserRole } from "./models/User"; import { User, UserRole } from "./models/User";
import { faker } from '@faker-js/faker'; import { faker } from '@faker-js/faker';
import { Task } from "./models/Task"; import { priorityType, Task, taskFinishType } from "./models/Task";
import { Project } from "./models/Project"; import { Project } from "./models/Project";
import { Member, MemberRole } from "./models/Member"; import { Member, MemberRole } from "./models/Member";
import { DateTimeTask } from "./models/DateTimeTask";
function randomIntFromInterval(min:number, max:number) { function randomIntFromInterval(min:number, max:number) {
return Math.floor(Math.random() * (max - min + 1) + min) 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 () => { const loadFixtures = async () => {
myDataSource myDataSource
...@@ -18,25 +52,27 @@ const loadFixtures = async () => { ...@@ -18,25 +52,27 @@ const loadFixtures = async () => {
await repositoryMember.delete({}) await repositoryMember.delete({})
const repositoryTask = myDataSource.getRepository(Task); const repositoryTask = myDataSource.getRepository(Task);
await repositoryTask.delete({}); await repositoryTask.delete({});
const repositoryDateTimeTask = myDataSource.getRepository(DateTimeTask);
await repositoryDateTimeTask.delete({})
const repositoryProject = myDataSource.getRepository(Project); const repositoryProject = myDataSource.getRepository(Project);
await repositoryProject.delete({}) await repositoryProject.delete({})
const repositoryUser = myDataSource.getRepository(User); const repositoryUser = myDataSource.getRepository(User);
await repositoryUser.delete({}); await repositoryUser.delete({});
console.log('========================== ' + '\n' + 'Data Source has been cleared!' +'\n' + '==========================') 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 = [] const users = []
for (let i = 0; i < 4; i++) { for (let i = 0; i < 3; i++) {
const name = faker.name.firstName() const name = faker.name.firstName()
const surname = faker.name.lastName() const surname = faker.name.lastName()
const displayName = name + ' ' + surname[0] + '.' const displayName = name + ' ' + surname[0] + '.'
const user = new User() const user = new User()
user.name = name; user.name = name;
user.surname = surname; user.surname = surname;
user.password = '12345qwert'; user.password = '123';
user.displayName= displayName; user.displayName= displayName;
user.phone = faker.phone.number('+77#########') user.phone = faker.phone.number('+77#########')
user.email = faker.internet.email(); user.email = 'a@a.a'+i;
user.role = userRoles[i].role; user.role = userRoles[i].role;
user.generateToken() user.generateToken()
await user.save(); await user.save();
...@@ -44,21 +80,29 @@ const loadFixtures = async () => { ...@@ -44,21 +80,29 @@ const loadFixtures = async () => {
} }
const tasks:Task[] = [] const tasks:Task[] = []
type taskFinishType = "opened" | "done" |"failed";
type priorityType = "A" | "B" |"C";
const priorities:priorityType[] = ["A", "B" , "C"] const priorities:priorityType[] = ["A", "B" , "C"]
const accomplish:taskFinishType[] = ["opened", "done" , "failed"] const accomplish:taskFinishType[] = ["opened", "done" , "failed"]
for (let i = 0; i < 20; i++) { 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) { if (i <= 15) {
const newTask = new Task(); const newTask = new Task();
newTask.title = `Buy ${faker.commerce.productName()}`; newTask.title = `Buy ${faker.commerce.productName()}`;
newTask.description = faker.random.words(4); 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.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.accomplish = faker.helpers.arrayElement(accomplish);
newTask.priority = faker.helpers.arrayElement(priorities); newTask.priority = faker.helpers.arrayElement(priorities);
await newTask.save(); await newTask.save();
...@@ -68,8 +112,6 @@ const loadFixtures = async () => { ...@@ -68,8 +112,6 @@ const loadFixtures = async () => {
newTask.title = `Buy ${faker.commerce.productName()}`; newTask.title = `Buy ${faker.commerce.productName()}`;
newTask.description = faker.random.words(4); newTask.description = faker.random.words(4);
newTask.executor = faker.helpers.arrayElement(users); newTask.executor = faker.helpers.arrayElement(users);
// newTask.dateTimeDue = null;
// newTask.dateTimeStart = null;
newTask.author = faker.helpers.arrayElement(users); newTask.author = faker.helpers.arrayElement(users);
newTask.accomplish = accomplish[0]; newTask.accomplish = accomplish[0];
newTask.priority = faker.helpers.arrayElement(priorities); newTask.priority = faker.helpers.arrayElement(priorities);
...@@ -79,32 +121,27 @@ const loadFixtures = async () => { ...@@ -79,32 +121,27 @@ const loadFixtures = async () => {
} }
const members:Member[]=[] const members:Member[]=[]
for (let i = 0; i < 5; i++) { for (let i = 0; i <3; i++) {
const newMember = new Member(); const newMember = new Member();
newMember.user = faker.helpers.arrayElement(users); newMember.user = faker.helpers.arrayElement(users);
newMember.roleProject=faker.helpers.objectValue(MemberRole); newMember.roleProject=cycleThroughObject(countRolesProject, MemberRole);
await newMember.save(); await newMember.save();
members.push(newMember) members.push(newMember)
} }
const projects:Project[] = [] const projects:Project[] = []
for (let i = 0; i < 5; i++) { for (let i = 0; i < 3; i++) {
const newProject = new Project(); const newProject = new Project();
newProject.title = `Project ${faker.random.words(1)}`; newProject.title = `Project ${faker.random.words(1)}`;
newProject.color = faker.random.words(1); newProject.color = faker.random.words(1);
newProject.members = faker.helpers.arrayElements(members,randomIntFromInterval(1, 3)) newProject.members = [cycleArrayOfMembers(countMembers,members), cycleArrayOfMembers(countMembers,members),cycleArrayOfMembers(countMembers,members)]
// newProject.admin = faker.helpers.arrayElement(users); newProject.tasks= faker.helpers.arrayElements(tasks, randomIntFromInterval(2, 19));
newProject.tasks= faker.helpers.arrayElements(tasks, randomIntFromInterval(1, 3));
// newProject.workers = faker.helpers.arrayElements(users, randomIntFromInterval(1, 3));
await newProject.save(); await newProject.save();
projects.push(newProject) projects.push(newProject)
} }
console.log('========================== ' + '\n' + 'Fixtures done!' +'\n' + '==========================') console.log('========================== ' + '\n' + 'Fixtures done!' +'\n' + '==========================')
}) })
......
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 { ...@@ -5,9 +5,7 @@ import {
CreateDateColumn, CreateDateColumn,
BaseEntity, BaseEntity,
ManyToOne, ManyToOne,
ManyToMany,
OneToMany,
JoinTable
} from 'typeorm'; } from 'typeorm';
import {User} from './User'; import {User} from './User';
import {Project} from './Project'; import {Project} from './Project';
......
...@@ -6,27 +6,30 @@ import { ...@@ -6,27 +6,30 @@ import {
BaseEntity, BaseEntity,
ManyToOne, ManyToOne,
OneToOne, OneToOne,
JoinTable JoinTable,
OneToMany
} from 'typeorm'; } from 'typeorm';
import {User} from './User'; import {User} from './User';
import {Project} from './Project'; import {Project} from './Project';
import { DateTimeTask } from './DateTimeTask';
type taskFinishType = "opened" | "done" |"failed"; export type taskFinishType = "opened"| "progress" | "done" |"failed";
type priorityType = "A" | "B" |"C"; export type priorityType = "A" | "B" |"C";
interface ITask{ interface ITask{
id: string; id: string;
title: string; title: string;
description: string; description: string;
note: string;
createdAt: Date; createdAt: Date;
dateTimeStart:Date| null;
dateTimeDue:Date| null;
dateTimeDeadLine: Date| null; dateTimeDeadLine: Date| null;
dateTimeFactDeadLine: Date| null; dateTimeFactDeadLine: Date| null;
accomplish: taskFinishType; accomplish: taskFinishType;
priority: priorityType | null; priority: priorityType | null;
archive:boolean,
author: User; author: User;
project:Project|null; project:Project|null;
dateTimeTasks:DateTimeTask[]|null;
executor:User; executor:User;
} }
...@@ -38,16 +41,16 @@ import { ...@@ -38,16 +41,16 @@ import {
title!: string title!: string
@Column({ name: 'description', type: 'varchar', length:50,nullable: true }) @Column({ name: 'description', type: 'varchar', length:50,nullable: true })
description!: string description!: string
@Column({ name: 'note', type: 'varchar', length:100,nullable: true })
note!: string
@CreateDateColumn({ name: 'created_at', type: Date, default: new Date() }) @CreateDateColumn({ name: 'created_at', type: Date, default: new Date() })
createdAt!: 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 }) @Column({ name: 'dateTimeDeadLine', type: Date,nullable: true })
dateTimeDeadLine!: Date; dateTimeDeadLine!: Date;
@Column({ name: 'dateTimeFactDeadLine', type: Date,nullable: true }) @Column({ name: 'dateTimeFactDeadLine', type: Date,nullable: true })
dateTimeFactDeadLine!: Date; dateTimeFactDeadLine!: Date;
@Column({ name: 'archive', type: 'varchar', length:50,nullable: false, default:false })
archive!: boolean
@Column({ @Column({
type: "enum", type: "enum",
...@@ -76,4 +79,7 @@ import { ...@@ -76,4 +79,7 @@ import {
@ManyToOne(()=>Project,(project:{tasks: Task[]}) => project.tasks,{eager : true,nullable: true,onUpdate:'CASCADE'}) @ManyToOne(()=>Project,(project:{tasks: Task[]}) => project.tasks,{eager : true,nullable: true,onUpdate:'CASCADE'})
project!: Project; project!: Project;
@OneToMany(() => DateTimeTask, (dateTimeTask: { task: Task }) => dateTimeTask.task,{eager : true,nullable: true,onUpdate:'CASCADE'})
dateTimeTasks!: DateTimeTask[];
} }
...@@ -35,9 +35,7 @@ interface IUser { ...@@ -35,9 +35,7 @@ interface IUser {
token: string; token: string;
createdAt: Date; createdAt: Date;
createdTasks:Task[]; createdTasks:Task[];
// workerInProjects:Project[]; members: Member[];
// adminInProjects:Project[];
membership: Member[];
} }
...@@ -88,7 +86,7 @@ export class User extends BaseEntity implements IUser { ...@@ -88,7 +86,7 @@ export class User extends BaseEntity implements IUser {
@OneToMany(() => Member, (member: { user: User }) => member.user) @OneToMany(() => Member, (member: { user: User }) => member.user)
membership!: Member[]; members!: Member[];
// @ManyToMany(() => Project,(project: { user: User }) => project.user) // @ManyToMany(() => Project,(project: { user: User }) => project.user)
// @JoinTable() // @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'; ...@@ -5,7 +5,7 @@ import { User } from '../models/User';
import { Member, MemberRole } from '../models/Member'; import { Member, MemberRole } from '../models/Member';
import { userInfo } from 'os'; import { userInfo } from 'os';
import { Task } from '../models/Task'; import { Task } from '../models/Task';
import { getRepository } from 'typeorm'; import { auth } from '../helpers';
const router:Router = express.Router(); const router:Router = express.Router();
const dataSource = myDataSource; const dataSource = myDataSource;
...@@ -15,33 +15,42 @@ router.get('/',async (req:Request, res:Response): Promise<Response>=> { ...@@ -15,33 +15,42 @@ router.get('/',async (req:Request, res:Response): Promise<Response>=> {
const projects:Project[] = await dataSource.manager.find(Project) const projects:Project[] = await dataSource.manager.find(Project)
return res.send({projects}) return res.send({projects})
}) })
/**get projects were user is member, by user token */
router.get('/my',async (req:Request, res:Response): Promise<Response>=> { router.get('/my',auth, async (req:Request, res:Response): Promise<Response>=> {
const token = req.get('Authorization'); const user = req.body.user
const user = await dataSource const rawProjects = await dataSource
.createQueryBuilder() .createQueryBuilder()
.select("user") .from(Project, "project")
.from(User, "user") .select(["project.id"])
.where("user.token = :token", { token: token }) .leftJoinAndSelect('project.members', 'member')
.getOne(); .loadRelationCountAndMap('project.tasks', 'project.tasks')
if(!user) return res.status(404).send({Message:'user not found'}) .leftJoinAndSelect('member.user', 'user' )
.where('member.userId = :userId',{userId:user.id})
const projects:Project[] = await dataSource.manager.find(Project) .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}) return res.send({projects})
}) })
/**create new project */
router.post('/', async (req:Request, res:Response): Promise<Response> => { router.post('/',auth, async (req:Request, res:Response): Promise<Response> => {
if (!req.body) return res.status(400).send({Message:'problem in incoming req.body'}) if (!req.body) return res.status(400).send({Message:'problem in incoming req.body'})
const token = req.get('Authorization'); const {user, title,color}= req.body;
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 member:Member = new Member; const member:Member = new Member;
member.user= user; member.user= user;
member.roleProject= MemberRole.ADMIN; member.roleProject= MemberRole.ADMIN;
...@@ -53,7 +62,7 @@ router.post('/', async (req:Request, res:Response): Promise<Response> => { ...@@ -53,7 +62,7 @@ router.post('/', async (req:Request, res:Response): Promise<Response> => {
await project.save() await project.save()
return res.send({project}) 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> => { router.get("/:id",async (req:Request, res:Response): Promise<Response> => {
const project = await dataSource const project = await dataSource
.createQueryBuilder() .createQueryBuilder()
...@@ -81,6 +90,20 @@ router.get("/:id",async (req:Request, res:Response): Promise<Response> => { ...@@ -81,6 +90,20 @@ router.get("/:id",async (req:Request, res:Response): Promise<Response> => {
return res.send({project, tasks}) 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>=>{ router.get('/user/:userId', async (req : Request, res : Response): Promise<Response>=>{
const userId:string = req.params.userId const userId:string = req.params.userId
...@@ -93,4 +116,57 @@ router.get('/user/:userId', async (req : Request, res : Response): Promise<Respo ...@@ -93,4 +116,57 @@ router.get('/user/:userId', async (req : Request, res : Response): Promise<Respo
return res.send({userProjects}) 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; export default router;
import express,{Router, Request, Response} from 'express'; import express,{Router, Request, Response } from 'express';
import {Task} from '../models/Task'; import {Task} from '../models/Task';
import {myDataSource} from '../app-data-source'; import {myDataSource} from '../app-data-source';
import { User } from '../models/User'; 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 router:Router = express.Router();
const dataSource = myDataSource; const dataSource = myDataSource;
/**get all tasks */
router.get('/', async(req:Request, res:Response):Promise<Response> => { router.get('/', async(req:Request, res:Response):Promise<Response> => {
const tasks = await dataSource const tasks = await dataSource
.getRepository(Task) .getRepository(Task)
...@@ -17,29 +22,24 @@ router.get('/', async(req:Request, res:Response):Promise<Response> => { ...@@ -17,29 +22,24 @@ router.get('/', async(req:Request, res:Response):Promise<Response> => {
return res.send({tasks}) 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 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.title = title;
newTask.description = description; newTask.description = description;
newTask.project = project; newTask.project = project;
newTask.dateTimeDue = dateTimeDue; newTask.dateTimeDeadLine=dateTimeDeadLine;
newTask.dateTimeStart = dateTimeStart;
newTask.author= user; newTask.author= user;
newTask.accomplish = accomplish; newTask.executor= executor;
newTask.priority = priority; newTask.priority = priority;
await newTask.save(); await newTask.save();
return res.send({newTask}) return res.send({newTask});
}) })
/**check tasks of specific user by userID */
router.get('/user/:userId', async (req: Request, res: Response):Promise<Response>=>{ router.get('/user/:userId', async (req: Request, res: Response):Promise<Response>=>{
const userId = req.params.userId; const userId = req.params.userId;
const tasks = await dataSource const tasks = await dataSource
...@@ -65,15 +65,10 @@ router.get('/user/:userId', async (req: Request, res: Response):Promise<Response ...@@ -65,15 +65,10 @@ router.get('/user/:userId', async (req: Request, res: Response):Promise<Response
return res.send({tasks}) return res.send({tasks})
}) })
router.get('/my', async (req: Request, res: Response):Promise<Response>=>{
const token = req.get('Authorization'); /**check tasks of current user where he is author or executor, search by id*/
const user = await dataSource router.get('/my',auth, async (req: Request, res: Response):Promise<Response>=>{
.createQueryBuilder() const user = req.body.user
.select("user")
.from(User, "user")
.where("user.token = :token", { token: token })
.getOne()
if(!user) return res.status(404).send({Message:'user not found'})
const tasks = await dataSource const tasks = await dataSource
.getRepository(Task) .getRepository(Task)
.find({ .find({
...@@ -97,6 +92,54 @@ router.get('/my', async (req: Request, res: Response):Promise<Response>=>{ ...@@ -97,6 +92,54 @@ router.get('/my', async (req: Request, res: Response):Promise<Response>=>{
return res.send({tasks}) 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>=>{ router.delete('/:taskId',async (req: Request, res: Response):Promise<Response>=>{
const taskId = req.params.taskId; const taskId = req.params.taskId;
await myDataSource await myDataSource
...@@ -109,49 +152,57 @@ router.delete('/:taskId',async (req: Request, res: Response):Promise<Response>=> ...@@ -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 /**change of task by task id */
.createQueryBuilder() router.put('/',authAuthorOrExecutorOfTask,async(req:Request, res:Response)=> {
.select("user") const {authorStatus,executorStatus,taskId,title,description,note, archive,project,dateTimeTaskId,start,due,executor,accomplish,dateTimeDeadLine, dateTimeFactDeadLine,priority} = req.body;
.from(User, "user") const task = await taskFinderById(taskId)
.where("user.token = :token", { token: token }) if (!task) return res.status(404).send({Message:'task not found'})
.getOne() let dateTimeTask = null;
if (!user) return res.status(404).send({Message:'user not found'}) if (dateTimeTaskId) {
const {id,title,description,project,dateTimeDue,dateTimeStart,executors,accomplish,priority} = req.body; const dateTimeTaskData = await dataSource
const task = await dataSource
.createQueryBuilder() .createQueryBuilder()
.select("task") .select("dateTimeTask")
.from(Task, "task") .from(DateTimeTask, "dateTimeTask")
.where("task.id = :id", { id }) .where("dateTimeTask,id=:dateTimeTaskId",{dateTimeTaskId})
.getOne() .getOne()
if (!dateTimeTask) return res.status(404).send({Message:'dateTimeTask not found'})
dateTimeTask = dateTimeTaskData
}
if (!task) return res.status(404).send({Message:'task not found'}) if (authorStatus){
task.title= title task.title = title
task.description= description task.description= description
task.project= project task.archive=archive
task.dateTimeDue= dateTimeDue task.dateTimeDeadLine=dateTimeDeadLine;
task.dateTimeStart= dateTimeStart task.project=project
task.author=user task.executor=executor
// task.executors=executors
task.accomplish= accomplish
task.priority= priority 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
await task.save() await task.save()
res.send({message:'update task successfully'}) 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>=>{ router.post('/project',async (req: Request, res: Response):Promise<Response>=>{
let projectArray :string[]= req.body; let projectArray :string[]= req.body;
console.log('projectArray ', projectArray)
if (projectArray.length===0) { if (projectArray.length===0) {
const rawTasks = await const rawTasks = await
dataSource dataSource
.getRepository(Task) .getRepository(Task)
.find({ .find({
relations:{ relations:{
// executors:true, executor:true,
author:true, author:true,
project:true project:true
} }
...@@ -171,4 +222,6 @@ router.post('/project',async (req: Request, res: Response):Promise<Response>=>{ ...@@ -171,4 +222,6 @@ router.post('/project',async (req: Request, res: Response):Promise<Response>=>{
export default router; export default router;
...@@ -4,6 +4,7 @@ import users from './routers/users'; ...@@ -4,6 +4,7 @@ import users from './routers/users';
import tasks from './routers/tasks'; import tasks from './routers/tasks';
import projects from './routers/projects'; import projects from './routers/projects';
import {myDataSource} from './app-data-source'; import {myDataSource} from './app-data-source';
import copyTasks from './routers/copyTasks';
myDataSource myDataSource
...@@ -22,6 +23,7 @@ app.use(express.json()); ...@@ -22,6 +23,7 @@ app.use(express.json());
const PORT = 8000; const PORT = 8000;
app.use('/users',users) app.use('/users',users)
app.use('/tasks',tasks) app.use('/tasks',tasks)
app.use('/copy-tasks',copyTasks)
app.use('/projects',projects) app.use('/projects',projects)
const run = async() => { const run = async() => {
......
import { Grid } from "@mui/material"; import { Grid } from "@mui/material";
import { memo, useEffect, useMemo, useState } from "react"; import { memo, useMemo} from "react";
import CalendarStandartCell from "../CalendarStandartCell.js/CalendarStandartCell"; import CalendarStandartCell from "../CalendarStandartCell.js/CalendarStandartCell";
import CalendarTask from "../CalendarTask/CalendarTask"; import CalendarTask from "../CalendarTask/CalendarTask";
import EmptyBox from "./EmptyBox/EmptyBox"; 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(()=>{ const hours = useMemo(()=>{
return hoursInDay.map((hour)=>parseInt(hour.split(':')[0]))}, return hoursInDay.map((hour)=>parseInt(hour.split(':')[0]))},
[hoursInDay]) [hoursInDay])
const availableTasks = useMemo(() => { const availableTasks = useMemo(() => {
const tasksInDay = tasks.filter((task)=> { return getAvailableTasks(tasks, year, month, day.dayNumber)
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
}, [tasks, month, year, day.dayNumber]) }, [tasks, month, year, day.dayNumber])
const sortedTasks = useMemo(() => { const sortedTasks = useMemo(() => {
if (availableTasks.length) { return getSortedTasks(availableTasks)
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
}
}, [availableTasks]) }, [availableTasks])
const linesInDay = useMemo(() => { const linesInDay = useMemo(() => {
let hourDiff return getLinesInDay(availableTasks, sortedTasks, hoursInDay, hours, hourFormat)
let hourDiffEnd }, [availableTasks, hourFormat, hours, hoursInDay, sortedTasks])
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 <> return <>
<Grid <Grid
...@@ -136,9 +29,28 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h ...@@ -136,9 +29,28 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
item item
xs={10.8} xs={10.8}
align='center' 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)=>{ {linesInDay?.map((line, i)=>{
const boxes = getBoxesInLine(line) const boxes = getBoxesInLine(line, hoursInDay, sortedTasks)
return( return(
<Grid key={i} container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginBottom: '5px'}}> <Grid key={i} container sx={{height: '35px', backgroundColor: 'rgb(0,0,0,0)', marginBottom: '5px'}}>
{boxes.map((box, index)=>{ {boxes.map((box, index)=>{
...@@ -156,11 +68,8 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h ...@@ -156,11 +68,8 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
hour={parseInt(hours[index])} hour={parseInt(hours[index])}
line={day.dayNumber} line={day.dayNumber}
task={box.task} task={box.task}
hourFormat={hourFormat}
setCurrentTask={setCurrentTask} setCurrentTask={setCurrentTask}
handleOpen={handleOpen} handleOpen={handleOpen}
increaseTaskHandler={increaseTaskHandler}
reduceTaskHandler={reduceTaskHandler}
setCopyTask={setCopyTask} setCopyTask={setCopyTask}
/> />
</Grid>) </Grid>)
...@@ -182,8 +91,9 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h ...@@ -182,8 +91,9 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h
})} })}
</Grid>) </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)=>{ {hoursInDay.map((hour, i)=>{
const hourNumber = parseInt(hour) const hourNumber = parseInt(hour)
return(<EmptyBox return(<EmptyBox
...@@ -205,5 +115,8 @@ const CalendarRowDay = ({xs, hoursInDay, createTaskInCellHandler, currentTask, h ...@@ -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 ...@@ -34,7 +34,12 @@ const EmptyBox = ({hourNumber, handleOpen, dayNumber, xs, dragTaskHandler, modal
onDragOver={(e)=>{dragOverHandler(e)}} onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}} onDrop={(e)=>{dropHandler(e)}}
onClick={(e)=>{onClickHandler(e, dayNumber, hourNumber)}} onClick={(e)=>{onClickHandler(e, dayNumber, hourNumber)}}
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 ? {isThisCell ?
<DefaultTask/> : null} <DefaultTask/> : null}
</Grid>) </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"; ...@@ -3,15 +3,14 @@ import { memo, useEffect, useState } from "react";
import DefaultTask from "../DefaultTask/DefaultTask"; import DefaultTask from "../DefaultTask/DefaultTask";
const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, dragTaskHandler, linesInDay}) => {
const [isThisCell, setIsThisCell] = useState(false)
const cellClass = { const cellClass = {
transition: '0.3s',
position: 'relative', position: 'relative',
height: '35px', height: linesInDay?.length ? `${40*linesInDay.length+35}px` : `${35+35}px`,
borderRight: '1px solid black',
} }
const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCellHandler, handleOpen, modal, divRef, dragTaskHandler, linesInDay}) => {
const [isThisCell, setIsThisCell] = useState(false)
useEffect(()=>{ useEffect(()=>{
if(!modal) { if(!modal) {
setIsThisCell(false); setIsThisCell(false);
...@@ -27,21 +26,26 @@ const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCell ...@@ -27,21 +26,26 @@ const CalendarStandartCell = ({children, xs, hours, dayNumber, createTaskInCell
e.preventDefault(); e.preventDefault();
dragTaskHandler(dayNumber, parseInt(hours.split(':')[0])) dragTaskHandler(dayNumber, parseInt(hours.split(':')[0]))
} }
const onClickHandler = (e) => {
if (!linesInDay?.length) {
createTaskInCellHandler(dayNumber, hours);
setIsThisCell(true);
handleOpen(e)
}
}
return <> return <>
<Grid <Grid
item xs={xs} item xs={xs}
sx={cellClass} sx={cellClass}
onClick={(e)=>{createTaskInCellHandler(dayNumber, hours); setIsThisCell(true); handleOpen(e)}} onClick={(e)=>{onClickHandler(e)}}
onDragOver={(e)=>{dragOverHandler(e)}} onDragOver={(e)=>{dragOverHandler(e)}}
onDrop={(e)=>{dropHandler(e)}} onDrop={(e)=>{dropHandler(e)}}
> >
{children} {children}
{isThisCell ? {isThisCell ?
<DefaultTask/> : null} <DefaultTask/> : null}
<div style={{position: 'absolute', height: children ? divRef : 0, width: '1px', backgroundColor: 'black', right: '0', top: '0', zIndex: '3'}}>
</div>
</Grid> </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 { Grid} from "@mui/material";
import React, { memo, useEffect, useRef, useState} from "react"; import React, { memo, useEffect, useState} from "react";
import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
const arrowClass = { const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, setCopyTask}) => {
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 [color, setColor] = useState('') const [color, setColor] = useState('')
useEffect(() => { useEffect(() => {
...@@ -28,7 +17,15 @@ const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, i ...@@ -28,7 +17,15 @@ const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, i
const onClickTaskHandler = (e, task) => { const onClickTaskHandler = (e, task) => {
e.stopPropagation(); e.stopPropagation();
setCurrentTask(task); setCurrentTask((prevState)=>{
return {
...task,
infoForCell: {
...task.infoForCell,
endHour: task.infoForCell.endHour + 1
}
}
});
handleOpen(e) handleOpen(e)
} }
...@@ -49,45 +46,16 @@ const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, i ...@@ -49,45 +46,16 @@ const CalendarTask = ({setCurrentTask, handleOpen, task, line, setCurrentLine, i
onDragLeave={(e)=>{dragLeaveHandler(e)}} onDragLeave={(e)=>{dragLeaveHandler(e)}}
onDragStart={(e)=>{dragStartHandler(e, line, task)}} onDragStart={(e)=>{dragStartHandler(e, line, task)}}
onDragEnd={(e)=>{dragEndHandler(e)}} 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)}} onClick={(e)=>{onClickTaskHandler(e, task)}}
> >
{task.infoForCell.endHour === task.infoForCell.startHour && hourFormat ? <span style={{maxWidth: '60%', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>
<>
<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>
{task.title} {task.title}
</span> </span>
</div> <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> </Grid>
</>) </>)
}; };
......
import { Box } from "@mui/material"; import { Box } from "@mui/material";
import { memo } from "react"; import { memo } from "react";
const DefaultTask = ({}) => { 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 (<> return (<>
<Box <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> <span>
Задача Задача
......
import { FormControlLabel, Switch} from "@mui/material"; import { Box, FormControlLabel, Switch} from "@mui/material";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useState } from "react";
import CalendarRow from "./CalendarRow/CalendarRow"; import CalendarRow from "./CalendarRow/CalendarRow";
import CalendarSmallCell from "./CalendarSmallCell/CalendarSmallCell"; import CalendarSmallCell from "./CalendarSmallCell/CalendarSmallCell";
import CalendarStandartCell from "./CalendarStandartCell.js/CalendarStandartCell"; import CalendarStandartCell from "./CalendarStandartCell.js/CalendarStandartCell";
...@@ -8,15 +8,15 @@ import MonthCalendarModalContent from "../MonthCalendarModalContent/MonthCalenda ...@@ -8,15 +8,15 @@ import MonthCalendarModalContent from "../MonthCalendarModalContent/MonthCalenda
import CalendarRowDay from "./CalendarRowDay/CalendarRowDay"; 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 [currentLine, setCurrentLine] = useState('')
const [modal, setModal] = useState({open:false, y: 0, x: 0,}); const [modal, setModal] = useState({open:false, y: 0, x: 0,});
const handleOpen = (e) => { const handleOpen = (e) => {
setModal( { setModal( {
open: true, open: true,
yPage: e.clientY, yClickСordinates: e.clientY,
xPage: e.clientX, xClickСordinates: e.clientX,
yDivClick: e.nativeEvent.offsetY, yDivClick: e.nativeEvent.offsetY,
xDivClick: e.nativeEvent.offsetX, xDivClick: e.nativeEvent.offsetX,
yDiv: e.target.offsetHeight, yDiv: e.target.offsetHeight,
...@@ -29,20 +29,9 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current ...@@ -29,20 +29,9 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
setCurrentTask({}) setCurrentTask({})
}; };
const divRef = useRef(null)
const [divHeight, setDivHeight] = useState('')
useEffect(() => {
if (divRef) {
setDivHeight(()=>{
return divRef.current?.offsetHeight
})
}
}, [divRef.current?.offsetHeight, hourFormat, month, tasks]);
return ( return (
<div ref={divRef} style={{marginBottom: '30px'}}> <Box style={{marginBottom: '30px'}}>
<div style={{position: 'sticky', top: '0px', zIndex: '10', backgroundColor: 'lightgrey'}}>
<CalendarRow <CalendarRow
> >
<CalendarSmallCell xs={1.2}> <CalendarSmallCell xs={1.2}>
...@@ -54,12 +43,13 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current ...@@ -54,12 +43,13 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
</CalendarSmallCell> </CalendarSmallCell>
{hoursInDay?.map((hours, i)=>{ {hoursInDay?.map((hours, i)=>{
return ( return (
<CalendarStandartCell key={i} xs={cellSizes.standarCell} divRef={divHeight}> <CalendarStandartCell key={i} xs={cellSizes.standarCell}>
{hours} {hours}
</CalendarStandartCell> </CalendarStandartCell>
) )
})} })}
</CalendarRow> </CalendarRow>
</div>
{daysInMonth?.map((day, i)=>{ {daysInMonth?.map((day, i)=>{
return ( return (
<CalendarRow <CalendarRow
...@@ -83,8 +73,6 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current ...@@ -83,8 +73,6 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
tasks={tasks} tasks={tasks}
day={day} day={day}
hourFormat={hourFormat} hourFormat={hourFormat}
increaseTaskHandler={increaseTaskHandler}
reduceTaskHandler={reduceTaskHandler}
createCopyTask={createCopyTask} createCopyTask={createCopyTask}
copyTask={copyTask} copyTask={copyTask}
setCopyTask={setCopyTask} setCopyTask={setCopyTask}
...@@ -101,12 +89,14 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current ...@@ -101,12 +89,14 @@ function MonthCalendarBody({month, year, tasks, createTaskInCellHandler, current
title={currentTask.title} title={currentTask.title}
description={currentTask.description} description={currentTask.description}
priority={currentTask.priority} priority={currentTask.priority}
startHour={currentTask.infoForCell?.startHour}
endHour={currentTask.infoForCell?.endHour}
onChangeCurrentTaskHandler={(e)=>{ onChangeCurrentTaskHandler(e)}} onChangeCurrentTaskHandler={(e)=>{ onChangeCurrentTaskHandler(e)}}
sendNewTaskHandler={()=>{sendNewTaskHandler(); handleClose()}} sendNewTaskHandler={()=>{sendNewTaskHandler(); handleClose()}}
deleteTaskHandler={()=>{deleteTaskHandler(currentTask.id); handleClose()}} deleteTaskHandler={()=>{deleteTaskHandler(currentTask.id); handleClose()}}
/> />
</ModalTask> </ModalTask>
</div> </Box>
); );
} }
......
...@@ -3,7 +3,7 @@ import { Box, Typography } from '@mui/material'; ...@@ -3,7 +3,7 @@ import { Box, Typography } from '@mui/material';
import { memo } from 'react'; import { memo } from 'react';
import MonthDecrementButton from './MonthDecrementButton/MonthDecrementButton'; import MonthDecrementButton from './MonthDecrementButton/MonthDecrementButton';
import MonthIncrementButton from './MonthIncrementButton/MonthIncrementButton'; import MonthIncrementButton from './MonthIncrementButton/MonthIncrementButton';
function MonthAndYearInfo({getCurrentMonthString, year, incrementMonth, decrementMonth}) { function MonthAndYearInfo({currentMonthString, year, incrementMonth, decrementMonth}) {
return ( return (
<> <>
...@@ -26,7 +26,7 @@ function MonthAndYearInfo({getCurrentMonthString, year, incrementMonth, decremen ...@@ -26,7 +26,7 @@ function MonthAndYearInfo({getCurrentMonthString, year, incrementMonth, decremen
justifyContent: 'center', justifyContent: 'center',
}} }}
> >
{getCurrentMonthString} {currentMonthString}
</Typography> </Typography>
<Typography align='center'>{year}</Typography> <Typography align='center'>{year}</Typography>
</Box> </Box>
......
...@@ -3,10 +3,10 @@ import { Box } from '@mui/system'; ...@@ -3,10 +3,10 @@ import { Box } from '@mui/system';
import MonthAndYearInfo from './MonthAndYearInfo/MonthAndYearInfo'; import MonthAndYearInfo from './MonthAndYearInfo/MonthAndYearInfo';
import СustomSelect from '../UI/СustomSelect/СustomSelect' import СustomSelect from '../UI/СustomSelect/СustomSelect'
const workers = [{value: '', text: '--выберите сотрудника'}, {value: 'Василий', text: 'Василий'}, {value: 'Никита', text: 'Никита'}] const workers = [{value: '', text: '--выберите сотрудника--'}, {value: 'Василий', text: 'Василий'}, {value: 'Никита', text: 'Никита'}]
const types = [{value: 'Месяц', text: 'Месяц'}, {value: 'Неделя', text: 'Неделя'}] const types = [{value: 'Месяц', text: 'Месяц'}, {value: 'Неделя', text: 'Неделя'}]
function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementMonth, calendarType, onChangeWorkerHandler, onChangeCalendarTypeHandler, worker, year}) { function MonthCalendarHeader({ currentMonthString, decrementMonth, incrementMonth, calendarType, onChangeWorkerHandler, onChangeCalendarTypeHandler, worker, year}) {
return ( return (
<> <>
...@@ -15,7 +15,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM ...@@ -15,7 +15,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM
<Toolbar> <Toolbar>
<MonthAndYearInfo <MonthAndYearInfo
getCurrentMonthString={getCurrentMonthString} currentMonthString={currentMonthString}
decrementMonth={decrementMonth} decrementMonth={decrementMonth}
incrementMonth={incrementMonth} incrementMonth={incrementMonth}
year={year} year={year}
...@@ -27,7 +27,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM ...@@ -27,7 +27,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM
id={'worker'} id={'worker'}
items={workers} items={workers}
/> />
<div style={{marginLeft: '20px'}}>
<СustomSelect <СustomSelect
value={calendarType} value={calendarType}
onChange={(e)=>{onChangeCalendarTypeHandler(e)}} onChange={(e)=>{onChangeCalendarTypeHandler(e)}}
...@@ -35,6 +35,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM ...@@ -35,6 +35,7 @@ function MonthCalendarHeader({ getCurrentMonthString, decrementMonth, incrementM
id={'calendar-type'} id={'calendar-type'}
items={types} items={types}
/> />
</div>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
</Box> </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 (<> return (<>
<TextField <TextField
id="task-description-title" id="task-description-title"
...@@ -22,28 +27,39 @@ function MonthCalendarModalContent({title, onChangeCurrentTaskHandler, descripti ...@@ -22,28 +27,39 @@ function MonthCalendarModalContent({title, onChangeCurrentTaskHandler, descripti
name='description' name='description'
onChange={(e)=>{onChangeCurrentTaskHandler(e)}} onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
/> />
<FormControl variant="outlined" sx={{width: '160px', marginBottom: '30px'}}> <CustomSelect
<InputLabel id="priority-type-label">Приоритет</InputLabel> defaultValue={null}
<Select
labelId="priority-type-label"
id="priority-type"
label="Приоритет"
sx={{width: '160px'}}
value={priority} value={priority}
name='priority' name={'priority'}
onChange={onChangeCurrentTaskHandler} variant={'outlined'}
> onChange={(e)=>{onChangeCurrentTaskHandler(e)}}
<MenuItem value={null}> label={'Приоритет'}
<em>-- Выберите Приоритет --</em> id={'priority-type'}
</MenuItem> items={priorities}
<MenuItem value={"A"}>A</MenuItem> />
<MenuItem value={"B"}>B</MenuItem> <div style={{display: 'flex', gap: '20px', margin: '20px 0'}}>
<MenuItem value={"C"}>C</MenuItem> <TextField
</Select> id="task-startHour"
</FormControl> value={startHour}
<Button sx={{marginRight: '40px'}} onClick={sendNewTaskHandler}>Сохранить</Button> 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> <Button onClick={deleteTaskHandler}>Удалить</Button>
</div>
</>); </>);
} }
export default MonthCalendarModalContent; export default memo(MonthCalendarModalContent);
\ No newline at end of file \ No newline at end of file
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Modal from '@mui/material/Modal'; import Modal from '@mui/material/Modal';
import { useEffect, useRef, useState } from 'react';
export default function ModalTask({modal, handleClose, children}) { 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 = { const style = {
display: 'flex',
flexDirection: 'column',
position: 'absolute', position: 'absolute',
top: modal.yPage - modal.yDiv - modal.yDivClick, top: getYCordinatesToModal(),
left: modal.xPage + modal.xDiv - modal.xDivClick + 10, left: getXCordinatesToModal(),
width: 250, width: 270,
height: 350, height: 450,
bgcolor: 'background.paper', bgcolor: 'background.paper',
border: '2px solid #000', border: '2px solid #000',
boxShadow: 24, boxShadow: 24,
...@@ -24,7 +62,7 @@ export default function ModalTask({modal, handleClose, children}) { ...@@ -24,7 +62,7 @@ export default function ModalTask({modal, handleClose, children}) {
aria-describedby="modal-modal-description" aria-describedby="modal-modal-description"
BackdropProps={{ style: { backgroundColor: 'rgba(255,255,255, 0)' } }} BackdropProps={{ style: { backgroundColor: 'rgba(255,255,255, 0)' } }}
> >
<Box sx={style}> <Box sx={style} ref={modalRef}>
{children} {children}
</Box> </Box>
</Modal> </Modal>
......
...@@ -8,7 +8,7 @@ import Select from "@mui/material/Select"; ...@@ -8,7 +8,7 @@ import Select from "@mui/material/Select";
export default function BasicSelect({value,label,name,onChange,task,items}) { export default function BasicSelect({value,label,name,onChange,task,items}) {
return ( return (
<Box sx={{ minWidth: 60 }}> <Box sx={{ minWidth: 60, m: 0}}>
<FormControl fullWidth> <FormControl fullWidth>
<InputLabel id="demo-simple-select-label"></InputLabel> <InputLabel id="demo-simple-select-label"></InputLabel>
<Select <Select
......
import { FormControl, InputLabel, MenuItem, Select} from '@mui/material'; import { FormControl, InputLabel, MenuItem, Select} from '@mui/material';
import { memo } from 'react'; 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 ( return (
<> <>
<FormControl variant={variant} sx={{ m: 1, minWidth: 120 }}> <FormControl variant={variant} sx={{ m: 0, minWidth: 120 }}>
<InputLabel id={`${id}-select-label`}>{label}</InputLabel> <InputLabel id={`${id}-select-label`}>{label}</InputLabel>
<Select <Select
labelId={`${id}-select-label`} labelId={`${id}-select-label`}
...@@ -13,6 +13,8 @@ function СustomSelect({value, onChange, label, variant='standard', items, id}) ...@@ -13,6 +13,8 @@ function СustomSelect({value, onChange, label, variant='standard', items, id})
value={value} value={value}
onChange={onChange} onChange={onChange}
label={label} label={label}
name={name}
defaultValue={defaultValue}
> >
{items.map((item, i)=>{ {items.map((item, i)=>{
return ( return (
......
...@@ -2,7 +2,8 @@ import { useEffect, useCallback, useState, useMemo } from 'react'; ...@@ -2,7 +2,8 @@ import { useEffect, useCallback, useState, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import MonthCalendarBody from '../../components/MonthCalendarBody/MonthCalendarBody'; import MonthCalendarBody from '../../components/MonthCalendarBody/MonthCalendarBody';
import MonthCalendarHeader from '../../components/MonthCalendarHeader/MonthCalendarHeader'; import MonthCalendarHeader from '../../components/MonthCalendarHeader/MonthCalendarHeader';
import { addTask, deleteTask, editTask, fetchCalendarTasks} from '../../store/actions/tasksActions'; import { dateToISOLikeButLocal, getCurrentMonthString, getDaysInMonth } from '../../helpers/CalendarHelpers';
import { addCalendarTask, deleteCalendarTask, editCalendarTask, fetchCalendarTasks} from '../../store/actions/tasksActions';
function MonthCalendar() { function MonthCalendar() {
const dispatch = useDispatch(); const dispatch = useDispatch();
...@@ -12,7 +13,7 @@ function MonthCalendar() { ...@@ -12,7 +13,7 @@ function MonthCalendar() {
const [dateNow, setDateNow] = useState({month: '', year: ''}) const [dateNow, setDateNow] = useState({month: '', year: ''})
const [worker, setWorker] = useState(''); const [worker, setWorker] = useState('');
const [calendarType, setCalendarType] = useState('Месяц'); const [calendarType, setCalendarType] = useState('Месяц');
const [currentTask, setCurrentTask] = useState({title: '', description: '', priority: ''}) const [currentTask, setCurrentTask] = useState({title: '', description: '', priority: null, infoForCell: {startHour: null, endHour: null}})
const [copyTask, setCopyTask] = useState(null) const [copyTask, setCopyTask] = useState(null)
const [cellSizes, setCellSizes] = useState({}) const [cellSizes, setCellSizes] = useState({})
...@@ -26,35 +27,27 @@ function MonthCalendar() { ...@@ -26,35 +27,27 @@ function MonthCalendar() {
const hoursInDay = useMemo(()=>{ const hoursInDay = useMemo(()=>{
let arr
if (hourFormat) { if (hourFormat) {
const 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'] 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']
const cells = arr.length
const xs = 10.8/cells
setCellSizes(()=>{
return {smallCell: 0.6, standarCell: xs, dayCell: 12/cells}
})
return arr
} else { } else {
const arr = ['8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'] arr = ['8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00']
}
const cells = arr.length const cells = arr.length
const xs = 10.8/cells const xs = 10.8/cells
setCellSizes(()=>{ setCellSizes(()=>{
return {smallCell: 0.6, standarCell: xs, dayCell: 12/cells} return {smallCell: 0.6, standarCell: xs, dayCell: 12/cells}
}) })
return arr return arr
}
}, [hourFormat]) }, [hourFormat])
const daysInMonth = useMemo(() => { const daysInMonth = useMemo(() => {
const newDaysInMonth = [] return getDaysInMonth(dateNow)
const daysInMonthNumber = new Date(dateNow.year, dateNow.month + 1, 0).getDate() }, [dateNow])
for (let i = 1; i <= daysInMonthNumber; i++) {
const dayOfWeekNumber = new Date(dateNow.year, dateNow.month, i).getDay() const currentMonthString = useMemo(() => {
const getDayOfWeekString = ["ВС","ПН","ВТ","СР","ЧТ","ПТ","СБ"][dayOfWeekNumber] return getCurrentMonthString(dateNow)
newDaysInMonth.push({dayNumber: i, dayOfWeek: getDayOfWeekString}) }, [dateNow])
}
return newDaysInMonth
}, [dateNow.month, dateNow.year])
const onChangeWorkerHandler = useCallback((event) => { const onChangeWorkerHandler = useCallback((event) => {
setWorker(event.target.value); setWorker(event.target.value);
...@@ -64,10 +57,6 @@ function MonthCalendar() { ...@@ -64,10 +57,6 @@ function MonthCalendar() {
setCalendarType(event.target.value); setCalendarType(event.target.value);
}, []); }, []);
const getCurrentMonthString = useMemo(() => {
return ["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь", "Декабрь"][dateNow.month];
}, [dateNow.month])
const incrementMonth = useCallback(() => { const incrementMonth = useCallback(() => {
setDateNow((prevState)=>{ setDateNow((prevState)=>{
if (prevState.month + 1 === 12 ) { if (prevState.month + 1 === 12 ) {
...@@ -76,7 +65,7 @@ function MonthCalendar() { ...@@ -76,7 +65,7 @@ function MonthCalendar() {
return {...prevState, month: prevState.month + 1} return {...prevState, month: prevState.month + 1}
}) })
}, []) }, [])
console.log(currentTask)
const decrementMonth = useCallback(() => { const decrementMonth = useCallback(() => {
setDateNow((prevState)=>{ setDateNow((prevState)=>{
if (prevState.month - 1 === -1) { if (prevState.month - 1 === -1) {
...@@ -88,20 +77,25 @@ function MonthCalendar() { ...@@ -88,20 +77,25 @@ function MonthCalendar() {
const onChangeCurrentTaskHandler = useCallback((e) => { const onChangeCurrentTaskHandler = useCallback((e) => {
const {name, value} = e.target; const {name, value} = e.target;
if (name === 'startHour' || name === 'endHour') {
setCurrentTask((prevState) => {
return {
...prevState,
infoForCell: {
...prevState.infoForCell,
[name]: parseInt(value)
}
}
});
} else {
setCurrentTask((prevState) => { setCurrentTask((prevState) => {
return { return {
...prevState, ...prevState,
[name]: value [name]: value
} }
}); });
}, []);
function dateToISOLikeButLocal(date) {
const offsetMs = date.getTimezoneOffset() * 60 * 1000;
const msLocal = date.getTime() - offsetMs;
const dateLocal = new Date(msLocal);
const iso = dateLocal.toISOString();
return iso;
} }
}, []);
const createTaskInCellHandler = (dayNumber, dayHour) => { const createTaskInCellHandler = (dayNumber, dayHour) => {
let hour let hour
...@@ -112,9 +106,9 @@ function MonthCalendar() { ...@@ -112,9 +106,9 @@ function MonthCalendar() {
} }
let hourDue let hourDue
if (hourFormat) { if (hourFormat) {
hourDue = hour + 0
} else {
hourDue = hour + 1 hourDue = hour + 1
} else {
hourDue = hour + 2
} }
const newTask = { const newTask = {
title:"Задача", title:"Задача",
...@@ -122,132 +116,84 @@ function MonthCalendar() { ...@@ -122,132 +116,84 @@ function MonthCalendar() {
priority: null, priority: null,
dateTimeStart: dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour, 0)), dateTimeStart: dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour, 0)),
dateTimeDue: dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hourDue, 59)), dateTimeDue: dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hourDue, 59)),
infoForCell: {
startHour: hour,
endHour: hourDue,
startDay: dayNumber
}
} }
setCurrentTask((newTask)) setCurrentTask((newTask))
} }
const dragTaskHandler = async (dayNumber, hour) => { const dragTaskHandler = async (dayNumber, hour) => {
const hourDiff = currentTask.infoForCell.endHour - currentTask.infoForCell.startHour const hourDiff = currentTask.infoForCell.endHour - currentTask.infoForCell.startHour
const due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour + hourDiff, 59)) const lastHour = hoursInDay[hoursInDay.length - 1].split(':')[0]
const start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour, 0))
const newObj = {
...currentTask,
dateTimeStart: start,
dateTimeDue: due
}
delete newObj.infoForCell
await dispatch(editTask(newObj))
setCurrentTask({})
}
const increaseTaskHandler = async (dayNumber, task, isStartTask) => {
const timeEnd = task.dateTimeDue.split('T')[1]
const timeEndHour = parseInt(timeEnd.split(':')[0])
const timeStart = task.dateTimeStart.split('T')[1]
const timeStartHour = parseInt(timeStart.split(':')[0])
let hourDiff
if (hourFormat) {
hourDiff = 1
} else {
hourDiff = 2
}
let due let due
let start if (hour + hourDiff >= lastHour) {
if (isStartTask) { due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, lastHour, 59))
due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeEndHour, 59))
start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeStartHour - hourDiff, 0))
} else { } else {
due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeEndHour + hourDiff, 59)) due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour + hourDiff, 59))
start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeStartHour, 0))
} }
const start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour, 0))
const newObj = { const newObj = {
...task, ...currentTask,
dateTimeStart: start, dateTimeStart: start,
dateTimeDue: due dateTimeDue: due
} }
delete newObj.infoForCell delete newObj.infoForCell
await dispatch(editTask(newObj)) await dispatch(editCalendarTask(newObj))
setCurrentTask({}) setCurrentTask({})
} }
const reduceTaskHandler = async (dayNumber, task, isStartTask) => { const sendNewTaskHandler = async () => {
const timeEnd = task.dateTimeDue.split('T')[1] const timeEndHour = currentTask.infoForCell.endHour
const timeEndHour = parseInt(timeEnd.split(':')[0]) const timeStartHour = currentTask.infoForCell.startHour
const timeStart = task.dateTimeStart.split('T')[1] const day = currentTask.infoForCell.startDay
const timeStartHour = parseInt(timeStart.split(':')[0]) const due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, day, timeEndHour - 1, 59))
let hourDiff const start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, day, timeStartHour, 0))
let hourDiffCheck const newTask = {
if (hourFormat) { ...currentTask,
hourDiff = 1
hourDiffCheck = 0
} else {
hourDiff = 2
hourDiffCheck = 1
}
let due
let start
if (task.infoForCell.endHour - task.infoForCell.startHour - hourDiffCheck !== 0) {
if (isStartTask) {
due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeEndHour, 59))
start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeStartHour + hourDiff, 0))
} else {
due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeEndHour - hourDiff, 59))
start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, timeStartHour, 0))
}
const newObj = {
...task,
dateTimeStart: start, dateTimeStart: start,
dateTimeDue: due dateTimeDue: due
} }
delete newObj.infoForCell delete newTask.infoForCell
await dispatch(editTask(newObj))
setCurrentTask({})
}
}
const sendNewTaskHandler = async () => {
if (currentTask.id) { if (currentTask.id) {
delete currentTask.infoForCell await dispatch(editCalendarTask(newTask))
setCurrentTask(() => {
return{
...currentTask,
}}
)
await dispatch(editTask(currentTask))
} else { } else {
setCurrentTask(() => { await dispatch(addCalendarTask(newTask))
return{
...currentTask,
}}
)
delete currentTask.infoForCell
await dispatch(addTask(currentTask))
} }
} }
const createCopyTask = async (dayNumber, hour) => { const createCopyTask = async (dayNumber, hour) => {
const hourDiff = copyTask.infoForCell.endHour - copyTask.infoForCell.startHour const hourDiff = copyTask.infoForCell.endHour - copyTask.infoForCell.startHour
const lastHour = hoursInDay[hoursInDay.length - 1].split(':')[0]
let due
if (hour + hourDiff >= lastHour) {
due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, lastHour, 59))
} else {
due = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour + hourDiff, 59))
}
const start = dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour, 0))
const newTask = { const newTask = {
...copyTask, ...copyTask,
dateTimeStart: dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour, 0)), dateTimeStart: start,
dateTimeDue: dateToISOLikeButLocal(new Date(dateNow.year, dateNow.month, dayNumber, hour + hourDiff, 59)), dateTimeDue: due,
} }
delete newTask.infoForCell delete newTask.infoForCell
delete newTask.id delete newTask.id
await dispatch(addTask(newTask)) await dispatch(addCalendarTask(newTask))
setCopyTask(null) setCopyTask(null)
} }
const deleteTaskHandler = async (taskId) => { const deleteTaskHandler = async (taskId) => {
dispatch(deleteTask(taskId)) dispatch(deleteCalendarTask(taskId))
} }
return ( return (
<> <>
<MonthCalendarHeader <MonthCalendarHeader
year={dateNow.year} year={dateNow.year}
getCurrentMonthString={getCurrentMonthString} currentMonthString={currentMonthString}
decrementMonth={decrementMonth} decrementMonth={decrementMonth}
incrementMonth={incrementMonth} incrementMonth={incrementMonth}
onChangeCalendarTypeHandler={onChangeCalendarTypeHandler} onChangeCalendarTypeHandler={onChangeCalendarTypeHandler}
...@@ -271,8 +217,6 @@ function MonthCalendar() { ...@@ -271,8 +217,6 @@ function MonthCalendar() {
hoursInDay={hoursInDay} hoursInDay={hoursInDay}
daysInMonth={daysInMonth} daysInMonth={daysInMonth}
dragTaskHandler={dragTaskHandler} dragTaskHandler={dragTaskHandler}
increaseTaskHandler={increaseTaskHandler}
reduceTaskHandler={reduceTaskHandler}
createCopyTask={createCopyTask} createCopyTask={createCopyTask}
copyTask={copyTask} copyTask={copyTask}
setCopyTask={setCopyTask} setCopyTask={setCopyTask}
......
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 = () => { ...@@ -46,6 +46,7 @@ export const fetchCalendarTasks = () => {
} }
} }
}; };
export const fetchAllTasks = () => { export const fetchAllTasks = () => {
return async (dispatch) => { return async (dispatch) => {
dispatch(fetchCalendarTasksRequest()); dispatch(fetchCalendarTasksRequest());
...@@ -69,34 +70,26 @@ const addTaskFailure = (error) => { ...@@ -69,34 +70,26 @@ const addTaskFailure = (error) => {
return {type: ADD_NEW_TASK_FAILURE, error} return {type: ADD_NEW_TASK_FAILURE, error}
}; };
// export const addTask = (task) => { export const addCalendarTask = (task) => {
// return async (dispatch, getState) => { return async (dispatch, getState) => {
// dispatch(addTaskRequest()); dispatch(addTaskRequest());
// const token = getState().users?.user?.token; try {
// try { await axios.post("/tasks", task);
// await axios.post("/tasks", task, { dispatch(addTaskSuccess())
// headers: { dispatch(fetchCalendarTasks())
// Authorization: token, } catch (error) {
// }, dispatch(addTaskFailure(error.response.data));
// }); }
// dispatch(addTaskSuccess()); }
// dispatch(fetchCalendarTasks()); }
// } catch (error) {
// dispatch(addTaskFailure(error.response.data));
// }
// };
// };
export const addTask = (task) => { export const addTask = (task) => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
dispatch(addTaskRequest()); dispatch(addTaskRequest());
// const token = getState().users?.user?.token;
try { try {
await axios.post("/tasks", task); await axios.post("/tasks", task);
dispatch(addTaskSuccess()) dispatch(addTaskSuccess())
dispatch(fetchAllTasks()) dispatch(fetchAllTasks())
dispatch(fetchCalendarTasks())
} catch (error) { } catch (error) {
dispatch(addTaskFailure(error.response.data)); dispatch(addTaskFailure(error.response.data));
} }
...@@ -122,6 +115,18 @@ export const editTask = (task) => { ...@@ -122,6 +115,18 @@ export const editTask = (task) => {
await axios.put("/tasks", task); await axios.put("/tasks", task);
dispatch(editTaskSuccess()) dispatch(editTaskSuccess())
dispatch(fetchAllTasks()) 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()) dispatch(fetchCalendarTasks())
} catch (error) { } catch (error) {
dispatch(editTaskFailure(error.response.data)); dispatch(editTaskFailure(error.response.data));
...@@ -144,11 +149,9 @@ const deleteTaskFailure = (error) => { ...@@ -144,11 +149,9 @@ const deleteTaskFailure = (error) => {
export const deleteTask = (taskId) => { export const deleteTask = (taskId) => {
return async (dispatch, getState) => { return async (dispatch, getState) => {
dispatch(deleteTaskRequest()); dispatch(deleteTaskRequest());
// const token = getState().users?.user?.token;
try { try {
await axios.delete(`/tasks/${taskId}`); await axios.delete(`/tasks/${taskId}`);
dispatch(deleteTaskSuccess()) dispatch(deleteTaskSuccess())
dispatch(fetchCalendarTasks())
dispatch(fetchAllTasks()) dispatch(fetchAllTasks())
} catch (error) { } catch (error) {
dispatch(deleteTaskFailure(error.response.data)); dispatch(deleteTaskFailure(error.response.data));
...@@ -156,6 +159,20 @@ export const deleteTask = (taskId) => { ...@@ -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 = () => { const fetchTasksByProjectRequest = () => {
return {type: FETCH_TASKS_BY_PROJECT_REQUEST} 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