feature: add projects & quick card members button
This commit is contained in:
		@@ -137,6 +137,7 @@ type ComplexityRoot struct {
 | 
				
			|||||||
		Me          func(childComplexity int) int
 | 
							Me          func(childComplexity int) int
 | 
				
			||||||
		Projects    func(childComplexity int, input *ProjectsFilter) int
 | 
							Projects    func(childComplexity int, input *ProjectsFilter) int
 | 
				
			||||||
		TaskGroups  func(childComplexity int) int
 | 
							TaskGroups  func(childComplexity int) int
 | 
				
			||||||
 | 
							Teams       func(childComplexity int) int
 | 
				
			||||||
		Users       func(childComplexity int) int
 | 
							Users       func(childComplexity int) int
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -184,6 +185,11 @@ type ComplexityRoot struct {
 | 
				
			|||||||
		Task   func(childComplexity int) int
 | 
							Task   func(childComplexity int) int
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UpdateTaskLocationPayload struct {
 | 
				
			||||||
 | 
							PreviousTaskGroupID func(childComplexity int) int
 | 
				
			||||||
 | 
							Task                func(childComplexity int) int
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	UserAccount struct {
 | 
						UserAccount struct {
 | 
				
			||||||
		CreatedAt   func(childComplexity int) int
 | 
							CreatedAt   func(childComplexity int) int
 | 
				
			||||||
		Email       func(childComplexity int) int
 | 
							Email       func(childComplexity int) int
 | 
				
			||||||
@@ -217,7 +223,7 @@ type MutationResolver interface {
 | 
				
			|||||||
	ToggleTaskLabel(ctx context.Context, input ToggleTaskLabelInput) (*ToggleTaskLabelPayload, error)
 | 
						ToggleTaskLabel(ctx context.Context, input ToggleTaskLabelInput) (*ToggleTaskLabelPayload, error)
 | 
				
			||||||
	CreateTask(ctx context.Context, input NewTask) (*pg.Task, error)
 | 
						CreateTask(ctx context.Context, input NewTask) (*pg.Task, error)
 | 
				
			||||||
	UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*pg.Task, error)
 | 
						UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*pg.Task, error)
 | 
				
			||||||
	UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*pg.Task, error)
 | 
						UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error)
 | 
				
			||||||
	UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error)
 | 
						UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error)
 | 
				
			||||||
	DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error)
 | 
						DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error)
 | 
				
			||||||
	AssignTask(ctx context.Context, input *AssignTaskInput) (*pg.Task, error)
 | 
						AssignTask(ctx context.Context, input *AssignTaskInput) (*pg.Task, error)
 | 
				
			||||||
@@ -245,6 +251,7 @@ type QueryResolver interface {
 | 
				
			|||||||
	FindProject(ctx context.Context, input FindProject) (*pg.Project, error)
 | 
						FindProject(ctx context.Context, input FindProject) (*pg.Project, error)
 | 
				
			||||||
	FindTask(ctx context.Context, input FindTask) (*pg.Task, error)
 | 
						FindTask(ctx context.Context, input FindTask) (*pg.Task, error)
 | 
				
			||||||
	Projects(ctx context.Context, input *ProjectsFilter) ([]pg.Project, error)
 | 
						Projects(ctx context.Context, input *ProjectsFilter) ([]pg.Project, error)
 | 
				
			||||||
 | 
						Teams(ctx context.Context) ([]pg.Team, error)
 | 
				
			||||||
	LabelColors(ctx context.Context) ([]pg.LabelColor, error)
 | 
						LabelColors(ctx context.Context) ([]pg.LabelColor, error)
 | 
				
			||||||
	TaskGroups(ctx context.Context) ([]pg.TaskGroup, error)
 | 
						TaskGroups(ctx context.Context) ([]pg.TaskGroup, error)
 | 
				
			||||||
	Me(ctx context.Context) (*pg.UserAccount, error)
 | 
						Me(ctx context.Context) (*pg.UserAccount, error)
 | 
				
			||||||
@@ -840,6 +847,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return e.complexity.Query.TaskGroups(childComplexity), true
 | 
							return e.complexity.Query.TaskGroups(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case "Query.teams":
 | 
				
			||||||
 | 
							if e.complexity.Query.Teams == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return e.complexity.Query.Teams(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case "Query.users":
 | 
						case "Query.users":
 | 
				
			||||||
		if e.complexity.Query.Users == nil {
 | 
							if e.complexity.Query.Users == nil {
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
@@ -1029,6 +1043,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		return e.complexity.ToggleTaskLabelPayload.Task(childComplexity), true
 | 
							return e.complexity.ToggleTaskLabelPayload.Task(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case "UpdateTaskLocationPayload.previousTaskGroupID":
 | 
				
			||||||
 | 
							if e.complexity.UpdateTaskLocationPayload.PreviousTaskGroupID == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return e.complexity.UpdateTaskLocationPayload.PreviousTaskGroupID(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case "UpdateTaskLocationPayload.task":
 | 
				
			||||||
 | 
							if e.complexity.UpdateTaskLocationPayload.Task == nil {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return e.complexity.UpdateTaskLocationPayload.Task(childComplexity), true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case "UserAccount.createdAt":
 | 
						case "UserAccount.createdAt":
 | 
				
			||||||
		if e.complexity.UserAccount.CreatedAt == nil {
 | 
							if e.complexity.UserAccount.CreatedAt == nil {
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
@@ -1254,6 +1282,7 @@ type Query {
 | 
				
			|||||||
  findProject(input: FindProject!): Project!
 | 
					  findProject(input: FindProject!): Project!
 | 
				
			||||||
  findTask(input: FindTask!): Task!
 | 
					  findTask(input: FindTask!): Task!
 | 
				
			||||||
  projects(input: ProjectsFilter): [Project!]!
 | 
					  projects(input: ProjectsFilter): [Project!]!
 | 
				
			||||||
 | 
					  teams: [Team!]!
 | 
				
			||||||
  labelColors: [LabelColor!]!
 | 
					  labelColors: [LabelColor!]!
 | 
				
			||||||
  taskGroups: [TaskGroup!]!
 | 
					  taskGroups: [TaskGroup!]!
 | 
				
			||||||
  me: UserAccount!
 | 
					  me: UserAccount!
 | 
				
			||||||
@@ -1297,8 +1326,8 @@ input NewTask {
 | 
				
			|||||||
  position: Float!
 | 
					  position: Float!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
input NewTaskLocation {
 | 
					input NewTaskLocation {
 | 
				
			||||||
  taskID: String!
 | 
					  taskID: UUID!
 | 
				
			||||||
  taskGroupID: String!
 | 
					  taskGroupID: UUID!
 | 
				
			||||||
  position: Float!
 | 
					  position: Float!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1394,6 +1423,10 @@ input UpdateProjectName {
 | 
				
			|||||||
  name: String!
 | 
					  name: String!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UpdateTaskLocationPayload {
 | 
				
			||||||
 | 
					  previousTaskGroupID: UUID!
 | 
				
			||||||
 | 
					  task: Task!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
type Mutation {
 | 
					type Mutation {
 | 
				
			||||||
  createRefreshToken(input: NewRefreshToken!): RefreshToken!
 | 
					  createRefreshToken(input: NewRefreshToken!): RefreshToken!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1420,7 +1453,7 @@ type Mutation {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  createTask(input: NewTask!): Task!
 | 
					  createTask(input: NewTask!): Task!
 | 
				
			||||||
  updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
 | 
					  updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
 | 
				
			||||||
  updateTaskLocation(input: NewTaskLocation!): Task!
 | 
					  updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
 | 
				
			||||||
  updateTaskName(input: UpdateTaskName!): Task!
 | 
					  updateTaskName(input: UpdateTaskName!): Task!
 | 
				
			||||||
  deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
 | 
					  deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
 | 
				
			||||||
  assignTask(input: AssignTaskInput): Task!
 | 
					  assignTask(input: AssignTaskInput): Task!
 | 
				
			||||||
@@ -2924,9 +2957,9 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		return graphql.Null
 | 
							return graphql.Null
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	res := resTmp.(*pg.Task)
 | 
						res := resTmp.(*UpdateTaskLocationPayload)
 | 
				
			||||||
	fc.Result = res
 | 
						fc.Result = res
 | 
				
			||||||
	return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res)
 | 
						return ec.marshalNUpdateTaskLocationPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskLocationPayload(ctx, field.Selections, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _Mutation_updateTaskName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
 | 
					func (ec *executionContext) _Mutation_updateTaskName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
 | 
				
			||||||
@@ -3966,6 +3999,40 @@ func (ec *executionContext) _Query_projects(ctx context.Context, field graphql.C
 | 
				
			|||||||
	return ec.marshalNProject2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐProjectᚄ(ctx, field.Selections, res)
 | 
						return ec.marshalNProject2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐProjectᚄ(ctx, field.Selections, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _Query_teams(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
								ret = graphql.Null
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
							Object:   "Query",
 | 
				
			||||||
 | 
							Field:    field,
 | 
				
			||||||
 | 
							Args:     nil,
 | 
				
			||||||
 | 
							IsMethod: true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx = graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
						resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 | 
				
			||||||
 | 
							ctx = rctx // use context from middleware stack in children
 | 
				
			||||||
 | 
							return ec.resolvers.Query().Teams(rctx)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ec.Error(ctx, err)
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resTmp == nil {
 | 
				
			||||||
 | 
							if !graphql.HasFieldError(ctx, fc) {
 | 
				
			||||||
 | 
								ec.Errorf(ctx, "must not be null")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := resTmp.([]pg.Team)
 | 
				
			||||||
 | 
						fc.Result = res
 | 
				
			||||||
 | 
						return ec.marshalNTeam2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTeamᚄ(ctx, field.Selections, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _Query_labelColors(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
 | 
					func (ec *executionContext) _Query_labelColors(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if r := recover(); r != nil {
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
@@ -5018,6 +5085,74 @@ func (ec *executionContext) _ToggleTaskLabelPayload_task(ctx context.Context, fi
 | 
				
			|||||||
	return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res)
 | 
						return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _UpdateTaskLocationPayload_previousTaskGroupID(ctx context.Context, field graphql.CollectedField, obj *UpdateTaskLocationPayload) (ret graphql.Marshaler) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
								ret = graphql.Null
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
							Object:   "UpdateTaskLocationPayload",
 | 
				
			||||||
 | 
							Field:    field,
 | 
				
			||||||
 | 
							Args:     nil,
 | 
				
			||||||
 | 
							IsMethod: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx = graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
						resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 | 
				
			||||||
 | 
							ctx = rctx // use context from middleware stack in children
 | 
				
			||||||
 | 
							return obj.PreviousTaskGroupID, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ec.Error(ctx, err)
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resTmp == nil {
 | 
				
			||||||
 | 
							if !graphql.HasFieldError(ctx, fc) {
 | 
				
			||||||
 | 
								ec.Errorf(ctx, "must not be null")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := resTmp.(uuid.UUID)
 | 
				
			||||||
 | 
						fc.Result = res
 | 
				
			||||||
 | 
						return ec.marshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, field.Selections, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _UpdateTaskLocationPayload_task(ctx context.Context, field graphql.CollectedField, obj *UpdateTaskLocationPayload) (ret graphql.Marshaler) {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
 | 
								ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
								ret = graphql.Null
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
						fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
							Object:   "UpdateTaskLocationPayload",
 | 
				
			||||||
 | 
							Field:    field,
 | 
				
			||||||
 | 
							Args:     nil,
 | 
				
			||||||
 | 
							IsMethod: false,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx = graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
						resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
 | 
				
			||||||
 | 
							ctx = rctx // use context from middleware stack in children
 | 
				
			||||||
 | 
							return obj.Task, nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ec.Error(ctx, err)
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if resTmp == nil {
 | 
				
			||||||
 | 
							if !graphql.HasFieldError(ctx, fc) {
 | 
				
			||||||
 | 
								ec.Errorf(ctx, "must not be null")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						res := resTmp.(*pg.Task)
 | 
				
			||||||
 | 
						fc.Result = res
 | 
				
			||||||
 | 
						return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _UserAccount_id(ctx context.Context, field graphql.CollectedField, obj *pg.UserAccount) (ret graphql.Marshaler) {
 | 
					func (ec *executionContext) _UserAccount_id(ctx context.Context, field graphql.CollectedField, obj *pg.UserAccount) (ret graphql.Marshaler) {
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
		if r := recover(); r != nil {
 | 
							if r := recover(); r != nil {
 | 
				
			||||||
@@ -6655,13 +6790,13 @@ func (ec *executionContext) unmarshalInputNewTaskLocation(ctx context.Context, o
 | 
				
			|||||||
		switch k {
 | 
							switch k {
 | 
				
			||||||
		case "taskID":
 | 
							case "taskID":
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			it.TaskID, err = ec.unmarshalNString2string(ctx, v)
 | 
								it.TaskID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return it, err
 | 
									return it, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case "taskGroupID":
 | 
							case "taskGroupID":
 | 
				
			||||||
			var err error
 | 
								var err error
 | 
				
			||||||
			it.TaskGroupID, err = ec.unmarshalNString2string(ctx, v)
 | 
								it.TaskGroupID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return it, err
 | 
									return it, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -7583,6 +7718,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				return res
 | 
									return res
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
 | 
							case "teams":
 | 
				
			||||||
 | 
								field := field
 | 
				
			||||||
 | 
								out.Concurrently(i, func() (res graphql.Marshaler) {
 | 
				
			||||||
 | 
									defer func() {
 | 
				
			||||||
 | 
										if r := recover(); r != nil {
 | 
				
			||||||
 | 
											ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}()
 | 
				
			||||||
 | 
									res = ec._Query_teams(ctx, field)
 | 
				
			||||||
 | 
									if res == graphql.Null {
 | 
				
			||||||
 | 
										atomic.AddUint32(&invalids, 1)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return res
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
		case "labelColors":
 | 
							case "labelColors":
 | 
				
			||||||
			field := field
 | 
								field := field
 | 
				
			||||||
			out.Concurrently(i, func() (res graphql.Marshaler) {
 | 
								out.Concurrently(i, func() (res graphql.Marshaler) {
 | 
				
			||||||
@@ -8007,6 +8156,38 @@ func (ec *executionContext) _ToggleTaskLabelPayload(ctx context.Context, sel ast
 | 
				
			|||||||
	return out
 | 
						return out
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var updateTaskLocationPayloadImplementors = []string{"UpdateTaskLocationPayload"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) _UpdateTaskLocationPayload(ctx context.Context, sel ast.SelectionSet, obj *UpdateTaskLocationPayload) graphql.Marshaler {
 | 
				
			||||||
 | 
						fields := graphql.CollectFields(ec.OperationContext, sel, updateTaskLocationPayloadImplementors)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out := graphql.NewFieldSet(fields)
 | 
				
			||||||
 | 
						var invalids uint32
 | 
				
			||||||
 | 
						for i, field := range fields {
 | 
				
			||||||
 | 
							switch field.Name {
 | 
				
			||||||
 | 
							case "__typename":
 | 
				
			||||||
 | 
								out.Values[i] = graphql.MarshalString("UpdateTaskLocationPayload")
 | 
				
			||||||
 | 
							case "previousTaskGroupID":
 | 
				
			||||||
 | 
								out.Values[i] = ec._UpdateTaskLocationPayload_previousTaskGroupID(ctx, field, obj)
 | 
				
			||||||
 | 
								if out.Values[i] == graphql.Null {
 | 
				
			||||||
 | 
									invalids++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case "task":
 | 
				
			||||||
 | 
								out.Values[i] = ec._UpdateTaskLocationPayload_task(ctx, field, obj)
 | 
				
			||||||
 | 
								if out.Values[i] == graphql.Null {
 | 
				
			||||||
 | 
									invalids++
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								panic("unknown field " + strconv.Quote(field.Name))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						out.Dispatch()
 | 
				
			||||||
 | 
						if invalids > 0 {
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var userAccountImplementors = []string{"UserAccount"}
 | 
					var userAccountImplementors = []string{"UserAccount"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) _UserAccount(ctx context.Context, sel ast.SelectionSet, obj *pg.UserAccount) graphql.Marshaler {
 | 
					func (ec *executionContext) _UserAccount(ctx context.Context, sel ast.SelectionSet, obj *pg.UserAccount) graphql.Marshaler {
 | 
				
			||||||
@@ -8868,6 +9049,43 @@ func (ec *executionContext) marshalNTeam2githubᚗcomᚋjordanknottᚋprojectᚑ
 | 
				
			|||||||
	return ec._Team(ctx, sel, &v)
 | 
						return ec._Team(ctx, sel, &v)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) marshalNTeam2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTeamᚄ(ctx context.Context, sel ast.SelectionSet, v []pg.Team) graphql.Marshaler {
 | 
				
			||||||
 | 
						ret := make(graphql.Array, len(v))
 | 
				
			||||||
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
 | 
						isLen1 := len(v) == 1
 | 
				
			||||||
 | 
						if !isLen1 {
 | 
				
			||||||
 | 
							wg.Add(len(v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i := range v {
 | 
				
			||||||
 | 
							i := i
 | 
				
			||||||
 | 
							fc := &graphql.FieldContext{
 | 
				
			||||||
 | 
								Index:  &i,
 | 
				
			||||||
 | 
								Result: &v[i],
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx := graphql.WithFieldContext(ctx, fc)
 | 
				
			||||||
 | 
							f := func(i int) {
 | 
				
			||||||
 | 
								defer func() {
 | 
				
			||||||
 | 
									if r := recover(); r != nil {
 | 
				
			||||||
 | 
										ec.Error(ctx, ec.Recover(ctx, r))
 | 
				
			||||||
 | 
										ret = nil
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}()
 | 
				
			||||||
 | 
								if !isLen1 {
 | 
				
			||||||
 | 
									defer wg.Done()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ret[i] = ec.marshalNTeam2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTeam(ctx, sel, v[i])
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if isLen1 {
 | 
				
			||||||
 | 
								f(i)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								go f(i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						wg.Wait()
 | 
				
			||||||
 | 
						return ret
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) marshalNTeam2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTeam(ctx context.Context, sel ast.SelectionSet, v *pg.Team) graphql.Marshaler {
 | 
					func (ec *executionContext) marshalNTeam2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTeam(ctx context.Context, sel ast.SelectionSet, v *pg.Team) graphql.Marshaler {
 | 
				
			||||||
	if v == nil {
 | 
						if v == nil {
 | 
				
			||||||
		if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
 | 
							if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
 | 
				
			||||||
@@ -8940,6 +9158,20 @@ func (ec *executionContext) unmarshalNUpdateTaskDescriptionInput2githubᚗcomᚋ
 | 
				
			|||||||
	return ec.unmarshalInputUpdateTaskDescriptionInput(ctx, v)
 | 
						return ec.unmarshalInputUpdateTaskDescriptionInput(ctx, v)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) marshalNUpdateTaskLocationPayload2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskLocationPayload(ctx context.Context, sel ast.SelectionSet, v UpdateTaskLocationPayload) graphql.Marshaler {
 | 
				
			||||||
 | 
						return ec._UpdateTaskLocationPayload(ctx, sel, &v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ec *executionContext) marshalNUpdateTaskLocationPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskLocationPayload(ctx context.Context, sel ast.SelectionSet, v *UpdateTaskLocationPayload) graphql.Marshaler {
 | 
				
			||||||
 | 
						if v == nil {
 | 
				
			||||||
 | 
							if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
 | 
				
			||||||
 | 
								ec.Errorf(ctx, "must not be null")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return graphql.Null
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ec._UpdateTaskLocationPayload(ctx, sel, v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ec *executionContext) unmarshalNUpdateTaskName2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskName(ctx context.Context, v interface{}) (UpdateTaskName, error) {
 | 
					func (ec *executionContext) unmarshalNUpdateTaskName2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskName(ctx context.Context, v interface{}) (UpdateTaskName, error) {
 | 
				
			||||||
	return ec.unmarshalInputUpdateTaskName(ctx, v)
 | 
						return ec.unmarshalInputUpdateTaskName(ctx, v)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -89,9 +89,9 @@ type NewTaskGroupLocation struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type NewTaskLocation struct {
 | 
					type NewTaskLocation struct {
 | 
				
			||||||
	TaskID      string  `json:"taskID"`
 | 
						TaskID      uuid.UUID `json:"taskID"`
 | 
				
			||||||
	TaskGroupID string  `json:"taskGroupID"`
 | 
						TaskGroupID uuid.UUID `json:"taskGroupID"`
 | 
				
			||||||
	Position    float64 `json:"position"`
 | 
						Position    float64   `json:"position"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type NewTeam struct {
 | 
					type NewTeam struct {
 | 
				
			||||||
@@ -169,6 +169,11 @@ type UpdateTaskDescriptionInput struct {
 | 
				
			|||||||
	Description string    `json:"description"`
 | 
						Description string    `json:"description"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UpdateTaskLocationPayload struct {
 | 
				
			||||||
 | 
						PreviousTaskGroupID uuid.UUID `json:"previousTaskGroupID"`
 | 
				
			||||||
 | 
						Task                *pg.Task  `json:"task"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type UpdateTaskName struct {
 | 
					type UpdateTaskName struct {
 | 
				
			||||||
	TaskID string `json:"taskID"`
 | 
						TaskID string `json:"taskID"`
 | 
				
			||||||
	Name   string `json:"name"`
 | 
						Name   string `json:"name"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,6 +110,7 @@ type Query {
 | 
				
			|||||||
  findProject(input: FindProject!): Project!
 | 
					  findProject(input: FindProject!): Project!
 | 
				
			||||||
  findTask(input: FindTask!): Task!
 | 
					  findTask(input: FindTask!): Task!
 | 
				
			||||||
  projects(input: ProjectsFilter): [Project!]!
 | 
					  projects(input: ProjectsFilter): [Project!]!
 | 
				
			||||||
 | 
					  teams: [Team!]!
 | 
				
			||||||
  labelColors: [LabelColor!]!
 | 
					  labelColors: [LabelColor!]!
 | 
				
			||||||
  taskGroups: [TaskGroup!]!
 | 
					  taskGroups: [TaskGroup!]!
 | 
				
			||||||
  me: UserAccount!
 | 
					  me: UserAccount!
 | 
				
			||||||
@@ -153,8 +154,8 @@ input NewTask {
 | 
				
			|||||||
  position: Float!
 | 
					  position: Float!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
input NewTaskLocation {
 | 
					input NewTaskLocation {
 | 
				
			||||||
  taskID: String!
 | 
					  taskID: UUID!
 | 
				
			||||||
  taskGroupID: String!
 | 
					  taskGroupID: UUID!
 | 
				
			||||||
  position: Float!
 | 
					  position: Float!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -250,6 +251,10 @@ input UpdateProjectName {
 | 
				
			|||||||
  name: String!
 | 
					  name: String!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UpdateTaskLocationPayload {
 | 
				
			||||||
 | 
					  previousTaskGroupID: UUID!
 | 
				
			||||||
 | 
					  task: Task!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
type Mutation {
 | 
					type Mutation {
 | 
				
			||||||
  createRefreshToken(input: NewRefreshToken!): RefreshToken!
 | 
					  createRefreshToken(input: NewRefreshToken!): RefreshToken!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -276,7 +281,7 @@ type Mutation {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  createTask(input: NewTask!): Task!
 | 
					  createTask(input: NewTask!): Task!
 | 
				
			||||||
  updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
 | 
					  updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
 | 
				
			||||||
  updateTaskLocation(input: NewTaskLocation!): Task!
 | 
					  updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
 | 
				
			||||||
  updateTaskName(input: UpdateTaskName!): Task!
 | 
					  updateTaskName(input: UpdateTaskName!): Task!
 | 
				
			||||||
  deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
 | 
					  deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
 | 
				
			||||||
  assignTask(input: AssignTaskInput): Task!
 | 
					  assignTask(input: AssignTaskInput): Task!
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,18 +215,14 @@ func (r *mutationResolver) UpdateTaskDescription(ctx context.Context, input Upda
 | 
				
			|||||||
	return &task, err
 | 
						return &task, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*pg.Task, error) {
 | 
					func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error) {
 | 
				
			||||||
	taskID, err := uuid.Parse(input.TaskID)
 | 
						previousTask, err := r.Repository.GetTaskByID(ctx, input.TaskID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return &pg.Task{}, err
 | 
							return &UpdateTaskLocationPayload{}, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	taskGroupID, err := uuid.Parse(input.TaskGroupID)
 | 
						task, err := r.Repository.UpdateTaskLocation(ctx, pg.UpdateTaskLocationParams{input.TaskID, input.TaskGroupID, input.Position})
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return &pg.Task{}, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	task, err := r.Repository.UpdateTaskLocation(ctx, pg.UpdateTaskLocationParams{taskID, taskGroupID, input.Position})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &task, err
 | 
						return &UpdateTaskLocationPayload{Task: &task, PreviousTaskGroupID: previousTask.TaskGroupID}, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error) {
 | 
					func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error) {
 | 
				
			||||||
@@ -405,6 +401,10 @@ func (r *queryResolver) Projects(ctx context.Context, input *ProjectsFilter) ([]
 | 
				
			|||||||
	return r.Repository.GetAllProjects(ctx)
 | 
						return r.Repository.GetAllProjects(ctx)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *queryResolver) Teams(ctx context.Context) ([]pg.Team, error) {
 | 
				
			||||||
 | 
						return r.Repository.GetAllTeams(ctx)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *queryResolver) LabelColors(ctx context.Context) ([]pg.LabelColor, error) {
 | 
					func (r *queryResolver) LabelColors(ctx context.Context) ([]pg.LabelColor, error) {
 | 
				
			||||||
	return r.Repository.GetLabelColors(ctx)
 | 
						return r.Repository.GetLabelColors(ctx)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@
 | 
				
			|||||||
    "@types/react-dom": "^16.9.5",
 | 
					    "@types/react-dom": "^16.9.5",
 | 
				
			||||||
    "@types/react-router": "^5.1.4",
 | 
					    "@types/react-router": "^5.1.4",
 | 
				
			||||||
    "@types/react-router-dom": "^5.1.3",
 | 
					    "@types/react-router-dom": "^5.1.3",
 | 
				
			||||||
 | 
					    "@types/react-select": "^3.0.13",
 | 
				
			||||||
    "@types/styled-components": "^5.0.0",
 | 
					    "@types/styled-components": "^5.0.0",
 | 
				
			||||||
    "@welldone-software/why-did-you-render": "^4.2.2",
 | 
					    "@welldone-software/why-did-you-render": "^4.2.2",
 | 
				
			||||||
    "apollo-cache-inmemory": "^1.6.5",
 | 
					    "apollo-cache-inmemory": "^1.6.5",
 | 
				
			||||||
@@ -57,6 +58,7 @@
 | 
				
			|||||||
    "react-router": "^5.1.2",
 | 
					    "react-router": "^5.1.2",
 | 
				
			||||||
    "react-router-dom": "^5.1.2",
 | 
					    "react-router-dom": "^5.1.2",
 | 
				
			||||||
    "react-scripts": "3.4.0",
 | 
					    "react-scripts": "3.4.0",
 | 
				
			||||||
 | 
					    "react-select": "^3.1.0",
 | 
				
			||||||
    "styled-components": "^5.0.1",
 | 
					    "styled-components": "^5.0.1",
 | 
				
			||||||
    "typescript": "~3.7.2"
 | 
					    "typescript": "~3.7.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
import React, { useState, useContext } from 'react';
 | 
					import React, { useState, useContext } from 'react';
 | 
				
			||||||
import TopNavbar from 'shared/components/TopNavbar';
 | 
					import TopNavbar from 'shared/components/TopNavbar';
 | 
				
			||||||
import DropdownMenu from 'shared/components/DropdownMenu';
 | 
					import DropdownMenu from 'shared/components/DropdownMenu';
 | 
				
			||||||
 | 
					import ProjectSettings from 'shared/components/ProjectSettings';
 | 
				
			||||||
import { useHistory } from 'react-router';
 | 
					import { useHistory } from 'react-router';
 | 
				
			||||||
import UserIDContext from 'App/context';
 | 
					import UserIDContext from 'App/context';
 | 
				
			||||||
import { useMeQuery } from 'shared/generated/graphql';
 | 
					import { useMeQuery } from 'shared/generated/graphql';
 | 
				
			||||||
 | 
					import { usePopup, Popup } from 'shared/components/PopupMenu';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type GlobalTopNavbarProps = {
 | 
					type GlobalTopNavbarProps = {
 | 
				
			||||||
  name: string | null;
 | 
					  name: string | null;
 | 
				
			||||||
@@ -12,6 +14,7 @@ type GlobalTopNavbarProps = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ name, projectMembers, onSaveProjectName }) => {
 | 
					const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ name, projectMembers, onSaveProjectName }) => {
 | 
				
			||||||
  const { loading, data } = useMeQuery();
 | 
					  const { loading, data } = useMeQuery();
 | 
				
			||||||
 | 
					  const { showPopup } = usePopup();
 | 
				
			||||||
  const history = useHistory();
 | 
					  const history = useHistory();
 | 
				
			||||||
  const { userID, setUserID } = useContext(UserIDContext);
 | 
					  const { userID, setUserID } = useContext(UserIDContext);
 | 
				
			||||||
  const [menu, setMenu] = useState({
 | 
					  const [menu, setMenu] = useState({
 | 
				
			||||||
@@ -27,6 +30,16 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ name, projectMembers,
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const onOpenSettings = ($target: React.RefObject<HTMLElement>) => {
 | 
				
			||||||
 | 
					    showPopup(
 | 
				
			||||||
 | 
					      $target,
 | 
				
			||||||
 | 
					      <Popup title={null} tab={0}>
 | 
				
			||||||
 | 
					        <ProjectSettings />
 | 
				
			||||||
 | 
					      </Popup>,
 | 
				
			||||||
 | 
					      185,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onLogout = () => {
 | 
					  const onLogout = () => {
 | 
				
			||||||
    fetch('http://localhost:3333/auth/logout', {
 | 
					    fetch('http://localhost:3333/auth/logout', {
 | 
				
			||||||
      method: 'POST',
 | 
					      method: 'POST',
 | 
				
			||||||
@@ -54,6 +67,7 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ name, projectMembers,
 | 
				
			|||||||
        projectMembers={projectMembers}
 | 
					        projectMembers={projectMembers}
 | 
				
			||||||
        onProfileClick={onProfileClick}
 | 
					        onProfileClick={onProfileClick}
 | 
				
			||||||
        onSaveProjectName={onSaveProjectName}
 | 
					        onSaveProjectName={onSaveProjectName}
 | 
				
			||||||
 | 
					        onOpenSettings={onOpenSettings}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      {menu.isOpen && (
 | 
					      {menu.isOpen && (
 | 
				
			||||||
        <DropdownMenu
 | 
					        <DropdownMenu
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { useState, useRef } from 'react';
 | 
					import React, { useState, useRef, useContext } from 'react';
 | 
				
			||||||
import GlobalTopNavbar from 'App/TopNavbar';
 | 
					import GlobalTopNavbar from 'App/TopNavbar';
 | 
				
			||||||
import styled from 'styled-components/macro';
 | 
					import styled from 'styled-components/macro';
 | 
				
			||||||
import { Bolt, ToggleOn, Tags } from 'shared/icons';
 | 
					import { Bolt, ToggleOn, Tags } from 'shared/icons';
 | 
				
			||||||
@@ -22,6 +22,7 @@ import {
 | 
				
			|||||||
  DeleteTaskDocument,
 | 
					  DeleteTaskDocument,
 | 
				
			||||||
  FindProjectDocument,
 | 
					  FindProjectDocument,
 | 
				
			||||||
  useCreateProjectLabelMutation,
 | 
					  useCreateProjectLabelMutation,
 | 
				
			||||||
 | 
					  useUnassignTaskMutation,
 | 
				
			||||||
} from 'shared/generated/graphql';
 | 
					} from 'shared/generated/graphql';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import TaskAssignee from 'shared/components/TaskAssignee';
 | 
					import TaskAssignee from 'shared/components/TaskAssignee';
 | 
				
			||||||
@@ -38,6 +39,7 @@ import produce from 'immer';
 | 
				
			|||||||
import MiniProfile from 'shared/components/MiniProfile';
 | 
					import MiniProfile from 'shared/components/MiniProfile';
 | 
				
			||||||
import Details from './Details';
 | 
					import Details from './Details';
 | 
				
			||||||
import { useApolloClient } from '@apollo/react-hooks';
 | 
					import { useApolloClient } from '@apollo/react-hooks';
 | 
				
			||||||
 | 
					import UserIDContext from 'App/context';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getCacheData = (client: any, projectID: string) => {
 | 
					const getCacheData = (client: any, projectID: string) => {
 | 
				
			||||||
  const cacheData: any = client.readQuery({
 | 
					  const cacheData: any = client.readQuery({
 | 
				
			||||||
@@ -249,7 +251,31 @@ const Project = () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const [updateTaskDescription] = useUpdateTaskDescriptionMutation();
 | 
					  const [updateTaskDescription] = useUpdateTaskDescriptionMutation();
 | 
				
			||||||
  const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
 | 
					  const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
 | 
				
			||||||
  const [updateTaskLocation] = useUpdateTaskLocationMutation();
 | 
					  const [updateTaskLocation] = useUpdateTaskLocationMutation({
 | 
				
			||||||
 | 
					    update: (client, newTask) => {
 | 
				
			||||||
 | 
					      const cacheData = getCacheData(client, projectID);
 | 
				
			||||||
 | 
					      console.log(cacheData);
 | 
				
			||||||
 | 
					      console.log(newTask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const newTaskGroups = produce(cacheData.findProject.taskGroups, (draftState: Array<TaskGroup>) => {
 | 
				
			||||||
 | 
					        const { previousTaskGroupID, task } = newTask.data.updateTaskLocation;
 | 
				
			||||||
 | 
					        if (previousTaskGroupID !== task.taskGroup.id) {
 | 
				
			||||||
 | 
					          const oldTaskGroupIdx = draftState.findIndex((t: TaskGroup) => t.id === previousTaskGroupID);
 | 
				
			||||||
 | 
					          const newTaskGroupIdx = draftState.findIndex((t: TaskGroup) => t.id === task.taskGroup.id);
 | 
				
			||||||
 | 
					          if (oldTaskGroupIdx !== -1 && newTaskGroupIdx !== -1) {
 | 
				
			||||||
 | 
					            draftState[oldTaskGroupIdx].tasks = draftState[oldTaskGroupIdx].tasks.filter((t: Task) => t.id !== task.id);
 | 
				
			||||||
 | 
					            draftState[newTaskGroupIdx].tasks = [...draftState[newTaskGroupIdx].tasks, { ...task }];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const newData = {
 | 
				
			||||||
 | 
					        ...cacheData.findProject,
 | 
				
			||||||
 | 
					        taskGroups: newTaskGroups,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      writeCacheData(client, projectID, cacheData, newData);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
  const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation({});
 | 
					  const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [deleteTaskGroup] = useDeleteTaskGroupMutation({
 | 
					  const [deleteTaskGroup] = useDeleteTaskGroupMutation({
 | 
				
			||||||
@@ -351,23 +377,6 @@ const Project = () => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onCardDrop = (droppedTask: Task) => {
 | 
					 | 
				
			||||||
    updateTaskLocation({
 | 
					 | 
				
			||||||
      variables: {
 | 
					 | 
				
			||||||
        taskID: droppedTask.id,
 | 
					 | 
				
			||||||
        taskGroupID: droppedTask.taskGroup.id,
 | 
					 | 
				
			||||||
        position: droppedTask.position,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      optimisticResponse: {
 | 
					 | 
				
			||||||
        updateTaskLocation: {
 | 
					 | 
				
			||||||
          name: droppedTask.name,
 | 
					 | 
				
			||||||
          id: droppedTask.id,
 | 
					 | 
				
			||||||
          position: droppedTask.position,
 | 
					 | 
				
			||||||
          createdAt: '',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  const onListDrop = (droppedColumn: TaskGroup) => {
 | 
					  const onListDrop = (droppedColumn: TaskGroup) => {
 | 
				
			||||||
    console.log(`list drop ${droppedColumn.id}`);
 | 
					    console.log(`list drop ${droppedColumn.id}`);
 | 
				
			||||||
    const cacheData = getCacheData(client, projectID);
 | 
					    const cacheData = getCacheData(client, projectID);
 | 
				
			||||||
@@ -399,10 +408,21 @@ const Project = () => {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [assignTask] = useAssignTaskMutation();
 | 
					  const [assignTask] = useAssignTaskMutation();
 | 
				
			||||||
 | 
					  const [unassignTask] = useUnassignTaskMutation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [updateProjectName] = useUpdateProjectNameMutation();
 | 
					  const [updateProjectName] = useUpdateProjectNameMutation({
 | 
				
			||||||
 | 
					    update: (client, newName) => {
 | 
				
			||||||
 | 
					      const cacheData = getCacheData(client, projectID);
 | 
				
			||||||
 | 
					      const newData = {
 | 
				
			||||||
 | 
					        ...cacheData.findProject,
 | 
				
			||||||
 | 
					        name: newName.data.updateProjectName.name,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      writeCacheData(client, projectID, cacheData, newData);
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const client = useApolloClient();
 | 
					  const client = useApolloClient();
 | 
				
			||||||
 | 
					  const { userID } = useContext(UserIDContext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { showPopup, hidePopup } = usePopup();
 | 
					  const { showPopup, hidePopup } = usePopup();
 | 
				
			||||||
  const $labelsRef = useRef<HTMLDivElement>(null);
 | 
					  const $labelsRef = useRef<HTMLDivElement>(null);
 | 
				
			||||||
@@ -482,7 +502,7 @@ const Project = () => {
 | 
				
			|||||||
          onTaskClick={task => {
 | 
					          onTaskClick={task => {
 | 
				
			||||||
            history.push(`${match.url}/c/${task.id}`);
 | 
					            history.push(`${match.url}/c/${task.id}`);
 | 
				
			||||||
          }}
 | 
					          }}
 | 
				
			||||||
          onTaskDrop={droppedTask => {
 | 
					          onTaskDrop={(droppedTask, previousTaskGroupID) => {
 | 
				
			||||||
            updateTaskLocation({
 | 
					            updateTaskLocation({
 | 
				
			||||||
              variables: {
 | 
					              variables: {
 | 
				
			||||||
                taskID: droppedTask.id,
 | 
					                taskID: droppedTask.id,
 | 
				
			||||||
@@ -492,11 +512,18 @@ const Project = () => {
 | 
				
			|||||||
              optimisticResponse: {
 | 
					              optimisticResponse: {
 | 
				
			||||||
                __typename: 'Mutation',
 | 
					                __typename: 'Mutation',
 | 
				
			||||||
                updateTaskLocation: {
 | 
					                updateTaskLocation: {
 | 
				
			||||||
                  name: droppedTask.name,
 | 
					                  previousTaskGroupID,
 | 
				
			||||||
                  id: droppedTask.id,
 | 
					                  task: {
 | 
				
			||||||
                  position: droppedTask.position,
 | 
					                    name: droppedTask.name,
 | 
				
			||||||
                  createdAt: '',
 | 
					                    id: droppedTask.id,
 | 
				
			||||||
                  __typename: 'Task',
 | 
					                    position: droppedTask.position,
 | 
				
			||||||
 | 
					                    taskGroup: {
 | 
				
			||||||
 | 
					                      id: droppedTask.taskGroup.id,
 | 
				
			||||||
 | 
					                      __typename: 'TaskGroup',
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    createdAt: '',
 | 
				
			||||||
 | 
					                    __typename: 'Task',
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@@ -558,6 +585,42 @@ const Project = () => {
 | 
				
			|||||||
            onEditCard={(_listId: string, cardId: string, cardName: string) => {
 | 
					            onEditCard={(_listId: string, cardId: string, cardName: string) => {
 | 
				
			||||||
              updateTaskName({ variables: { taskID: cardId, name: cardName } });
 | 
					              updateTaskName({ variables: { taskID: cardId, name: cardName } });
 | 
				
			||||||
            }}
 | 
					            }}
 | 
				
			||||||
 | 
					            onOpenMembersPopup={($targetRef, task) => {
 | 
				
			||||||
 | 
					              showPopup(
 | 
				
			||||||
 | 
					                $targetRef,
 | 
				
			||||||
 | 
					                <Popup title="Members" tab={0} onClose={() => {}}>
 | 
				
			||||||
 | 
					                  <MemberManager
 | 
				
			||||||
 | 
					                    availableMembers={data.findProject.members}
 | 
				
			||||||
 | 
					                    activeMembers={task.assigned ?? []}
 | 
				
			||||||
 | 
					                    onMemberChange={(member, isActive) => {
 | 
				
			||||||
 | 
					                      if (isActive) {
 | 
				
			||||||
 | 
					                        assignTask({ variables: { taskID: task.id, userID: userID ?? '' } });
 | 
				
			||||||
 | 
					                      } else {
 | 
				
			||||||
 | 
					                        unassignTask({ variables: { taskID: task.id, userID: userID ?? '' } });
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                </Popup>,
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            onCardMemberClick={($targetRef, taskID, memberID) => {
 | 
				
			||||||
 | 
					              const member = data.findProject.members.find(m => m.id === memberID);
 | 
				
			||||||
 | 
					              const profileIcon = member ? member.profileIcon : null;
 | 
				
			||||||
 | 
					              showPopup(
 | 
				
			||||||
 | 
					                $targetRef,
 | 
				
			||||||
 | 
					                <Popup title={null} onClose={() => {}} tab={0}>
 | 
				
			||||||
 | 
					                  <MiniProfile
 | 
				
			||||||
 | 
					                    profileIcon={profileIcon}
 | 
				
			||||||
 | 
					                    displayName="Jordan Knott"
 | 
				
			||||||
 | 
					                    username="@jordanthedev"
 | 
				
			||||||
 | 
					                    bio="None"
 | 
				
			||||||
 | 
					                    onRemoveFromTask={() => {
 | 
				
			||||||
 | 
					                      /* unassignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } }); */
 | 
				
			||||||
 | 
					                    }}
 | 
				
			||||||
 | 
					                  />
 | 
				
			||||||
 | 
					                </Popup>,
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
            onOpenLabelsPopup={($targetRef, task) => {
 | 
					            onOpenLabelsPopup={($targetRef, task) => {
 | 
				
			||||||
              taskLabelsRef.current = task.labels;
 | 
					              taskLabelsRef.current = task.labels;
 | 
				
			||||||
              showPopup(
 | 
					              showPopup(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,13 @@
 | 
				
			|||||||
import React, { useState } from 'react';
 | 
					import React, { useState, useContext } from 'react';
 | 
				
			||||||
import styled from 'styled-components/macro';
 | 
					import styled from 'styled-components/macro';
 | 
				
			||||||
import GlobalTopNavbar from 'App/TopNavbar';
 | 
					import GlobalTopNavbar from 'App/TopNavbar';
 | 
				
			||||||
import { useGetProjectsQuery } from 'shared/generated/graphql';
 | 
					import { useGetProjectsQuery, useCreateProjectMutation, GetProjectsDocument } from 'shared/generated/graphql';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ProjectGridItem from 'shared/components/ProjectGridItem';
 | 
					import ProjectGridItem, { AddProjectItem } from 'shared/components/ProjectGridItem';
 | 
				
			||||||
import { Link } from 'react-router-dom';
 | 
					import { Link } from 'react-router-dom';
 | 
				
			||||||
import Navbar from 'App/Navbar';
 | 
					import Navbar from 'App/Navbar';
 | 
				
			||||||
 | 
					import NewProject from 'shared/components/NewProject';
 | 
				
			||||||
 | 
					import UserIDContext from 'App/context';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MainContent = styled.div`
 | 
					const MainContent = styled.div`
 | 
				
			||||||
  padding: 0 0 50px 80px;
 | 
					  padding: 0 0 50px 80px;
 | 
				
			||||||
@@ -17,19 +19,37 @@ const ProjectGrid = styled.div`
 | 
				
			|||||||
  width: 60%;
 | 
					  width: 60%;
 | 
				
			||||||
  max-width: 780px;
 | 
					  max-width: 780px;
 | 
				
			||||||
  margin: 25px auto;
 | 
					  margin: 25px auto;
 | 
				
			||||||
  display: flex;
 | 
					  display: grid;
 | 
				
			||||||
  flex-wrap: wrap;
 | 
					  grid-template-columns: 240px 240px 240px;
 | 
				
			||||||
  align-items: center;
 | 
					  gap: 20px 10px;
 | 
				
			||||||
  justify-content: center;
 | 
					 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ProjectLink = styled(Link)`
 | 
					const ProjectLink = styled(Link)``;
 | 
				
			||||||
  flex: 1 0 33%;
 | 
					 | 
				
			||||||
  margin-bottom: 20px;
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Projects = () => {
 | 
					const Projects = () => {
 | 
				
			||||||
  const { loading, data } = useGetProjectsQuery();
 | 
					  const { loading, data } = useGetProjectsQuery();
 | 
				
			||||||
 | 
					  const [createProject] = useCreateProjectMutation({
 | 
				
			||||||
 | 
					    update: (client, newProject) => {
 | 
				
			||||||
 | 
					      const cacheData: any = client.readQuery({
 | 
				
			||||||
 | 
					        query: GetProjectsDocument,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      console.log(cacheData);
 | 
				
			||||||
 | 
					      console.log(newProject);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const newData = {
 | 
				
			||||||
 | 
					        ...cacheData,
 | 
				
			||||||
 | 
					        projects: [...cacheData.projects, { ...newProject.data.createProject }],
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      console.log(newData);
 | 
				
			||||||
 | 
					      client.writeQuery({
 | 
				
			||||||
 | 
					        query: GetProjectsDocument,
 | 
				
			||||||
 | 
					        data: newData,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  const [showNewProject, setShowNewProject] = useState(false);
 | 
				
			||||||
 | 
					  const { userID, setUserID } = useContext(UserIDContext);
 | 
				
			||||||
  if (loading) {
 | 
					  if (loading) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <>
 | 
					      <>
 | 
				
			||||||
@@ -38,7 +58,7 @@ const Projects = () => {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (data) {
 | 
					  if (data) {
 | 
				
			||||||
    const { projects } = data;
 | 
					    const { projects, teams } = data;
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <>
 | 
					      <>
 | 
				
			||||||
        <GlobalTopNavbar onSaveProjectName={() => {}} name={null} />
 | 
					        <GlobalTopNavbar onSaveProjectName={() => {}} name={null} />
 | 
				
			||||||
@@ -50,7 +70,26 @@ const Projects = () => {
 | 
				
			|||||||
              />
 | 
					              />
 | 
				
			||||||
            </ProjectLink>
 | 
					            </ProjectLink>
 | 
				
			||||||
          ))}
 | 
					          ))}
 | 
				
			||||||
 | 
					          <AddProjectItem
 | 
				
			||||||
 | 
					            onAddProject={() => {
 | 
				
			||||||
 | 
					              setShowNewProject(true);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
        </ProjectGrid>
 | 
					        </ProjectGrid>
 | 
				
			||||||
 | 
					        {showNewProject && (
 | 
				
			||||||
 | 
					          <NewProject
 | 
				
			||||||
 | 
					            onCreateProject={(name, teamID) => {
 | 
				
			||||||
 | 
					              if (userID) {
 | 
				
			||||||
 | 
					                createProject({ variables: { teamID, name, userID } });
 | 
				
			||||||
 | 
					                setShowNewProject(false);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            onClose={() => {
 | 
				
			||||||
 | 
					              setShowNewProject(false);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					            teams={teams}
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
      </>
 | 
					      </>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								web/src/projects.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								web/src/projects.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -54,8 +54,9 @@ type Organization = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Team = {
 | 
					type Team = {
 | 
				
			||||||
 | 
					  id: string;
 | 
				
			||||||
  name: string;
 | 
					  name: string;
 | 
				
			||||||
  projects: Project[];
 | 
					  createdAt: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ProjectLabel = {
 | 
					type ProjectLabel = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -128,28 +128,3 @@ export const CardMembers = styled.div`
 | 
				
			|||||||
  margin: 0 -2px 0 0;
 | 
					  margin: 0 -2px 0 0;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CardMember = styled.div<{ bgColor: string; ref: any }>`
 | 
					 | 
				
			||||||
  height: 28px;
 | 
					 | 
				
			||||||
  width: 28px;
 | 
					 | 
				
			||||||
  float: right;
 | 
					 | 
				
			||||||
  margin: 0 0 4px 4px;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  background-color: ${props => props.bgColor};
 | 
					 | 
				
			||||||
  color: #fff;
 | 
					 | 
				
			||||||
  border-radius: 25em;
 | 
					 | 
				
			||||||
  cursor: pointer;
 | 
					 | 
				
			||||||
  display: block;
 | 
					 | 
				
			||||||
  overflow: visible;
 | 
					 | 
				
			||||||
  position: relative;
 | 
					 | 
				
			||||||
  text-decoration: none;
 | 
					 | 
				
			||||||
  z-index: 0;
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const CardMemberInitials = styled.div`
 | 
					 | 
				
			||||||
  height: 28px;
 | 
					 | 
				
			||||||
  width: 28px;
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  justify-content: center;
 | 
					 | 
				
			||||||
  font-size: 14px;
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ import React, { useState, useRef } from 'react';
 | 
				
			|||||||
import { DraggableProvidedDraggableProps } from 'react-beautiful-dnd';
 | 
					import { DraggableProvidedDraggableProps } from 'react-beautiful-dnd';
 | 
				
			||||||
import PropTypes from 'prop-types';
 | 
					import PropTypes from 'prop-types';
 | 
				
			||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 | 
					import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 | 
				
			||||||
 | 
					import Member from 'shared/components/Member';
 | 
				
			||||||
import { faPencilAlt, faList } from '@fortawesome/free-solid-svg-icons';
 | 
					import { faPencilAlt, faList } from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
import { faClock, faCheckSquare, faEye } from '@fortawesome/free-regular-svg-icons';
 | 
					import { faClock, faCheckSquare, faEye } from '@fortawesome/free-regular-svg-icons';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@@ -19,8 +20,6 @@ import {
 | 
				
			|||||||
  ListCardOperation,
 | 
					  ListCardOperation,
 | 
				
			||||||
  CardTitle,
 | 
					  CardTitle,
 | 
				
			||||||
  CardMembers,
 | 
					  CardMembers,
 | 
				
			||||||
  CardMember,
 | 
					 | 
				
			||||||
  CardMemberInitials,
 | 
					 | 
				
			||||||
} from './Styles';
 | 
					} from './Styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DueDate = {
 | 
					type DueDate = {
 | 
				
			||||||
@@ -33,31 +32,6 @@ type Checklist = {
 | 
				
			|||||||
  total: number;
 | 
					  total: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type MemberProps = {
 | 
					 | 
				
			||||||
  onCardMemberClick?: OnCardMemberClick;
 | 
					 | 
				
			||||||
  taskID: string;
 | 
					 | 
				
			||||||
  member: TaskUser;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const Member: React.FC<MemberProps> = ({ onCardMemberClick, taskID, member }) => {
 | 
					 | 
				
			||||||
  const $targetRef = useRef<HTMLDivElement>();
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <CardMember
 | 
					 | 
				
			||||||
      ref={$targetRef}
 | 
					 | 
				
			||||||
      onClick={e => {
 | 
					 | 
				
			||||||
        if (onCardMemberClick) {
 | 
					 | 
				
			||||||
          e.stopPropagation();
 | 
					 | 
				
			||||||
          onCardMemberClick($targetRef, taskID, member.id);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }}
 | 
					 | 
				
			||||||
      key={member.id}
 | 
					 | 
				
			||||||
      bgColor={member.profileIcon.bgColor ?? '#7367F0'}
 | 
					 | 
				
			||||||
    >
 | 
					 | 
				
			||||||
      <CardMemberInitials>{member.profileIcon.initials}</CardMemberInitials>
 | 
					 | 
				
			||||||
    </CardMember>
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
  title: string;
 | 
					  title: string;
 | 
				
			||||||
  description: string;
 | 
					  description: string;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ import { Container, BoardWrapper } from './Styles';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
interface SimpleProps {
 | 
					interface SimpleProps {
 | 
				
			||||||
  taskGroups: Array<TaskGroup>;
 | 
					  taskGroups: Array<TaskGroup>;
 | 
				
			||||||
  onTaskDrop: (task: Task) => void;
 | 
					  onTaskDrop: (task: Task, previousTaskGroupID: string) => void;
 | 
				
			||||||
  onTaskGroupDrop: (taskGroup: TaskGroup) => void;
 | 
					  onTaskGroupDrop: (taskGroup: TaskGroup) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onTaskClick: (task: Task) => void;
 | 
					  onTaskClick: (task: Task) => void;
 | 
				
			||||||
@@ -110,7 +110,7 @@ const SimpleLists: React.FC<SimpleProps> = ({
 | 
				
			|||||||
            id: destination.droppableId,
 | 
					            id: destination.droppableId,
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        onTaskDrop(newTask);
 | 
					        onTaskDrop(newTask, droppedTask.taskGroup.id);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										55
									
								
								web/src/shared/components/Member/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								web/src/shared/components/Member/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					import React, { useRef } from 'react';
 | 
				
			||||||
 | 
					import styled from 'styled-components';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CardMember = styled.div<{ bgColor: string; ref: any }>`
 | 
				
			||||||
 | 
					  height: 28px;
 | 
				
			||||||
 | 
					  width: 28px;
 | 
				
			||||||
 | 
					  float: right;
 | 
				
			||||||
 | 
					  margin: 0 0 4px 4px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  background-color: ${props => props.bgColor};
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  border-radius: 25em;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  overflow: visible;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  z-index: 0;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CardMemberInitials = styled.div`
 | 
				
			||||||
 | 
					  height: 28px;
 | 
				
			||||||
 | 
					  width: 28px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MemberProps = {
 | 
				
			||||||
 | 
					  onCardMemberClick?: OnCardMemberClick;
 | 
				
			||||||
 | 
					  taskID: string;
 | 
				
			||||||
 | 
					  member: TaskUser;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Member: React.FC<MemberProps> = ({ onCardMemberClick, taskID, member }) => {
 | 
				
			||||||
 | 
					  const $targetRef = useRef<HTMLDivElement>();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <CardMember
 | 
				
			||||||
 | 
					      ref={$targetRef}
 | 
				
			||||||
 | 
					      onClick={e => {
 | 
				
			||||||
 | 
					        if (onCardMemberClick) {
 | 
				
			||||||
 | 
					          e.stopPropagation();
 | 
				
			||||||
 | 
					          onCardMemberClick($targetRef, taskID, member.id);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					      key={member.id}
 | 
				
			||||||
 | 
					      bgColor={member.profileIcon.bgColor ?? '#7367F0'}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <CardMemberInitials>{member.profileIcon.initials}</CardMemberInitials>
 | 
				
			||||||
 | 
					    </CardMember>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Member;
 | 
				
			||||||
							
								
								
									
										32
									
								
								web/src/shared/components/NewProject/NewProject.stories.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								web/src/shared/components/NewProject/NewProject.stories.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					import React, { useState, useRef, createRef } from 'react';
 | 
				
			||||||
 | 
					import { action } from '@storybook/addon-actions';
 | 
				
			||||||
 | 
					import styled from 'styled-components';
 | 
				
			||||||
 | 
					import NormalizeStyles from 'App/NormalizeStyles';
 | 
				
			||||||
 | 
					import BaseStyles from 'App/BaseStyles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import NewProject from '.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  component: NewProject,
 | 
				
			||||||
 | 
					  title: 'NewProject',
 | 
				
			||||||
 | 
					  parameters: {
 | 
				
			||||||
 | 
					    backgrounds: [
 | 
				
			||||||
 | 
					      { name: 'white', value: '#ffffff', default: true },
 | 
				
			||||||
 | 
					      { name: 'gray', value: '#f8f8f8' },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Default = () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <NormalizeStyles />
 | 
				
			||||||
 | 
					      <BaseStyles />
 | 
				
			||||||
 | 
					      <NewProject
 | 
				
			||||||
 | 
					        onCreateProject={action('create project')}
 | 
				
			||||||
 | 
					        teams={[{ name: 'General', id: 'general', createdAt: '' }]}
 | 
				
			||||||
 | 
					        onClose={() => {}}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										280
									
								
								web/src/shared/components/NewProject/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								web/src/shared/components/NewProject/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,280 @@
 | 
				
			|||||||
 | 
					import React, { useState } from 'react';
 | 
				
			||||||
 | 
					import styled from 'styled-components';
 | 
				
			||||||
 | 
					import { mixin } from 'shared/utils/styles';
 | 
				
			||||||
 | 
					import Select from 'react-select';
 | 
				
			||||||
 | 
					import { ArrowLeft, Cross } from 'shared/icons';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Overlay = styled.div`
 | 
				
			||||||
 | 
					  z-index: 10000;
 | 
				
			||||||
 | 
					  background: #262c49;
 | 
				
			||||||
 | 
					  bottom: 0;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  left: 0;
 | 
				
			||||||
 | 
					  position: fixed;
 | 
				
			||||||
 | 
					  right: 0;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Content = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					const Header = styled.div`
 | 
				
			||||||
 | 
					  height: 64px;
 | 
				
			||||||
 | 
					  padding: 0 24px;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex: 0 0 auto;
 | 
				
			||||||
 | 
					  justify-content: space-between;
 | 
				
			||||||
 | 
					  transition: box-shadow 250ms;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const HeaderLeft = styled.div`
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					const HeaderRight = styled.div`
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Container = styled.div`
 | 
				
			||||||
 | 
					  padding: 32px 0;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ContainerContent = styled.div`
 | 
				
			||||||
 | 
					  width: 520px;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Title = styled.h1`
 | 
				
			||||||
 | 
					  font-size: 24px;
 | 
				
			||||||
 | 
					  font-weight: 500;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  margin-bottom: 25px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ProjectName = styled.input`
 | 
				
			||||||
 | 
					  margin: 0 0 12px;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  line-height: 20px;
 | 
				
			||||||
 | 
					  margin-bottom: 12px;
 | 
				
			||||||
 | 
					  padding: 8px 12px;
 | 
				
			||||||
 | 
					  background: #262c49;
 | 
				
			||||||
 | 
					  outline: none;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  border-width: 1px;
 | 
				
			||||||
 | 
					  border-style: solid;
 | 
				
			||||||
 | 
					  border-color: transparent;
 | 
				
			||||||
 | 
					  border-image: initial;
 | 
				
			||||||
 | 
					  border-color: #414561;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					  font-weight: 400;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:focus {
 | 
				
			||||||
 | 
					    background: ${mixin.darken('#262c49', 0.15)};
 | 
				
			||||||
 | 
					    box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					const ProjectNameLabel = styled.label`
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  margin-bottom: 4px;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					const ProjectInfo = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ProjectField = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  margin-right: 15px;
 | 
				
			||||||
 | 
					  flex-grow: 1;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					const ProjectTeamField = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  flex-grow: 1;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const colourStyles = {
 | 
				
			||||||
 | 
					  control: (styles: any, data: any) => {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      ...styles,
 | 
				
			||||||
 | 
					      backgroundColor: data.isMenuOpen ? mixin.darken('#262c49', 0.15) : '#262c49',
 | 
				
			||||||
 | 
					      boxShadow: data.menuIsOpen ? 'rgb(115, 103, 240) 0px 0px 0px 1px' : 'none',
 | 
				
			||||||
 | 
					      borderRadius: '3px',
 | 
				
			||||||
 | 
					      borderWidth: '1px',
 | 
				
			||||||
 | 
					      borderStyle: 'solid',
 | 
				
			||||||
 | 
					      borderImage: 'initial',
 | 
				
			||||||
 | 
					      borderColor: '#414561',
 | 
				
			||||||
 | 
					      ':hover': {
 | 
				
			||||||
 | 
					        boxShadow: 'rgb(115, 103, 240) 0px 0px 0px 1px',
 | 
				
			||||||
 | 
					        borderRadius: '3px',
 | 
				
			||||||
 | 
					        borderWidth: '1px',
 | 
				
			||||||
 | 
					        borderStyle: 'solid',
 | 
				
			||||||
 | 
					        borderImage: 'initial',
 | 
				
			||||||
 | 
					        borderColor: '#414561',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      ':active': {
 | 
				
			||||||
 | 
					        boxShadow: 'rgb(115, 103, 240) 0px 0px 0px 1px',
 | 
				
			||||||
 | 
					        borderRadius: '3px',
 | 
				
			||||||
 | 
					        borderWidth: '1px',
 | 
				
			||||||
 | 
					        borderStyle: 'solid',
 | 
				
			||||||
 | 
					        borderImage: 'initial',
 | 
				
			||||||
 | 
					        borderColor: 'rgb(115, 103, 240)',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  menu: (styles: any) => {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      ...styles,
 | 
				
			||||||
 | 
					      backgroundColor: mixin.darken('#262c49', 0.15),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  dropdownIndicator: (styles: any) => ({ ...styles, color: '#c2c6dc', ':hover': { color: '#c2c6dc' } }),
 | 
				
			||||||
 | 
					  indicatorSeparator: (styles: any) => ({ ...styles, color: '#c2c6dc' }),
 | 
				
			||||||
 | 
					  option: (styles: any, { data, isDisabled, isFocused, isSelected }: any) => {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      ...styles,
 | 
				
			||||||
 | 
					      backgroundColor: isDisabled
 | 
				
			||||||
 | 
					        ? null
 | 
				
			||||||
 | 
					        : isSelected
 | 
				
			||||||
 | 
					        ? mixin.darken('#262c49', 0.25)
 | 
				
			||||||
 | 
					        : isFocused
 | 
				
			||||||
 | 
					        ? mixin.darken('#262c49', 0.15)
 | 
				
			||||||
 | 
					        : null,
 | 
				
			||||||
 | 
					      color: isDisabled ? '#ccc' : isSelected ? '#fff' : '#c2c6dc',
 | 
				
			||||||
 | 
					      cursor: isDisabled ? 'not-allowed' : 'default',
 | 
				
			||||||
 | 
					      ':active': {
 | 
				
			||||||
 | 
					        ...styles[':active'],
 | 
				
			||||||
 | 
					        backgroundColor: !isDisabled && (isSelected ? mixin.darken('#262c49', 0.25) : '#fff'),
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      ':hover': {
 | 
				
			||||||
 | 
					        ...styles[':hover'],
 | 
				
			||||||
 | 
					        backgroundColor: !isDisabled && (isSelected ? 'rgb(115, 103, 240)' : 'rgb(115, 103, 240)'),
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  placeholder: (styles: any) => ({ ...styles, color: '#c2c6dc' }),
 | 
				
			||||||
 | 
					  clearIndicator: (styles: any) => ({ ...styles, color: '#c2c6dc', ':hover': { color: '#c2c6dc' } }),
 | 
				
			||||||
 | 
					  input: (styles: any) => ({
 | 
				
			||||||
 | 
					    ...styles,
 | 
				
			||||||
 | 
					    color: '#fff',
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  singleValue: (styles: any) => {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      ...styles,
 | 
				
			||||||
 | 
					      color: '#fff',
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					const CreateButton = styled.button`
 | 
				
			||||||
 | 
					  outline: none;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  line-height: 20px;
 | 
				
			||||||
 | 
					  padding: 6px 12px;
 | 
				
			||||||
 | 
					  background-color: none;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  border-radius: 3px;
 | 
				
			||||||
 | 
					  border-width: 1px;
 | 
				
			||||||
 | 
					  border-style: solid;
 | 
				
			||||||
 | 
					  border-color: transparent;
 | 
				
			||||||
 | 
					  border-image: initial;
 | 
				
			||||||
 | 
					  border-color: #414561;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    color: #fff;
 | 
				
			||||||
 | 
					    background: rgb(115, 103, 240);
 | 
				
			||||||
 | 
					    border-color: rgb(115, 103, 240);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					type NewProjectProps = {
 | 
				
			||||||
 | 
					  teams: Array<Team>;
 | 
				
			||||||
 | 
					  onClose: () => void;
 | 
				
			||||||
 | 
					  onCreateProject: (projectName: string, teamID: string) => void;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const NewProject: React.FC<NewProjectProps> = ({ teams, onClose, onCreateProject }) => {
 | 
				
			||||||
 | 
					  const [projectName, setProjectName] = useState('');
 | 
				
			||||||
 | 
					  const [team, setTeam] = useState<null | string>(null);
 | 
				
			||||||
 | 
					  const options = teams.map(t => ({ label: t.name, value: t.id }));
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <Overlay>
 | 
				
			||||||
 | 
					      <Content>
 | 
				
			||||||
 | 
					        <Header>
 | 
				
			||||||
 | 
					          <HeaderLeft
 | 
				
			||||||
 | 
					            onClick={() => {
 | 
				
			||||||
 | 
					              onClose();
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <ArrowLeft color="#c2c6dc" />
 | 
				
			||||||
 | 
					          </HeaderLeft>
 | 
				
			||||||
 | 
					          <HeaderRight
 | 
				
			||||||
 | 
					            onClick={() => {
 | 
				
			||||||
 | 
					              onClose();
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <Cross color="#c2c6dc" />
 | 
				
			||||||
 | 
					          </HeaderRight>
 | 
				
			||||||
 | 
					        </Header>
 | 
				
			||||||
 | 
					        <Container>
 | 
				
			||||||
 | 
					          <ContainerContent>
 | 
				
			||||||
 | 
					            <Title>Add project details</Title>
 | 
				
			||||||
 | 
					            <ProjectInfo>
 | 
				
			||||||
 | 
					              <ProjectField>
 | 
				
			||||||
 | 
					                <ProjectNameLabel>Project name</ProjectNameLabel>
 | 
				
			||||||
 | 
					                <ProjectName
 | 
				
			||||||
 | 
					                  value={projectName}
 | 
				
			||||||
 | 
					                  onChange={(e: any) => {
 | 
				
			||||||
 | 
					                    setProjectName(e.currentTarget.value);
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </ProjectField>
 | 
				
			||||||
 | 
					              <ProjectTeamField>
 | 
				
			||||||
 | 
					                <ProjectNameLabel>Team</ProjectNameLabel>
 | 
				
			||||||
 | 
					                <Select
 | 
				
			||||||
 | 
					                  onChange={(e: any) => {
 | 
				
			||||||
 | 
					                    setTeam(e.value);
 | 
				
			||||||
 | 
					                  }}
 | 
				
			||||||
 | 
					                  value={options.filter(d => d.value === team)}
 | 
				
			||||||
 | 
					                  styles={colourStyles}
 | 
				
			||||||
 | 
					                  classNamePrefix="teamSelect"
 | 
				
			||||||
 | 
					                  options={options}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </ProjectTeamField>
 | 
				
			||||||
 | 
					            </ProjectInfo>
 | 
				
			||||||
 | 
					            <CreateButton
 | 
				
			||||||
 | 
					              onClick={() => {
 | 
				
			||||||
 | 
					                if (team && projectName !== '') {
 | 
				
			||||||
 | 
					                  onCreateProject(projectName, team);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              Create project
 | 
				
			||||||
 | 
					            </CreateButton>
 | 
				
			||||||
 | 
					          </ContainerContent>
 | 
				
			||||||
 | 
					        </Container>
 | 
				
			||||||
 | 
					      </Content>
 | 
				
			||||||
 | 
					    </Overlay>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default NewProject;
 | 
				
			||||||
@@ -1,12 +1,12 @@
 | 
				
			|||||||
import styled, { css } from 'styled-components';
 | 
					import styled, { css } from 'styled-components';
 | 
				
			||||||
import { mixin } from 'shared/utils/styles';
 | 
					import { mixin } from 'shared/utils/styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Container = styled.div<{ invert: boolean; top: number; left: number; ref: any }>`
 | 
					export const Container = styled.div<{ invert: boolean; top: number; left: number; ref: any; width: number | string }>`
 | 
				
			||||||
  left: ${props => props.left}px;
 | 
					  left: ${props => props.left}px;
 | 
				
			||||||
  top: ${props => props.top}px;
 | 
					  top: ${props => props.top}px;
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  width: 316px;
 | 
					  width: ${props => props.width}px;
 | 
				
			||||||
  padding-top: 10px;
 | 
					  padding-top: 10px;
 | 
				
			||||||
  height: auto;
 | 
					  height: auto;
 | 
				
			||||||
  z-index: 40000;
 | 
					  z-index: 40000;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,7 @@ import {
 | 
				
			|||||||
} from './Styles';
 | 
					} from './Styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type PopupContextState = {
 | 
					type PopupContextState = {
 | 
				
			||||||
  show: (target: RefObject<HTMLElement>, content: JSX.Element) => void;
 | 
					  show: (target: RefObject<HTMLElement>, content: JSX.Element, width?: string | number) => void;
 | 
				
			||||||
  setTab: (newTab: number) => void;
 | 
					  setTab: (newTab: number) => void;
 | 
				
			||||||
  getCurrentTab: () => number;
 | 
					  getCurrentTab: () => number;
 | 
				
			||||||
  hide: () => void;
 | 
					  hide: () => void;
 | 
				
			||||||
@@ -23,7 +23,7 @@ type PopupContextState = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type PopupProps = {
 | 
					type PopupProps = {
 | 
				
			||||||
  title: string | null;
 | 
					  title: string | null;
 | 
				
			||||||
  onClose: () => void;
 | 
					  onClose?: () => void;
 | 
				
			||||||
  tab: number;
 | 
					  tab: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,17 +32,23 @@ type PopupContainerProps = {
 | 
				
			|||||||
  left: number;
 | 
					  left: number;
 | 
				
			||||||
  invert: boolean;
 | 
					  invert: boolean;
 | 
				
			||||||
  onClose: () => void;
 | 
					  onClose: () => void;
 | 
				
			||||||
 | 
					  width?: string | number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PopupContainer: React.FC<PopupContainerProps> = ({ top, left, onClose, children, invert }) => {
 | 
					const PopupContainer: React.FC<PopupContainerProps> = ({ width, top, left, onClose, children, invert }) => {
 | 
				
			||||||
  const $containerRef = useRef();
 | 
					  const $containerRef = useRef();
 | 
				
			||||||
  useOnOutsideClick($containerRef, true, onClose, null);
 | 
					  useOnOutsideClick($containerRef, true, onClose, null);
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Container left={left} top={top} ref={$containerRef} invert={invert}>
 | 
					    <Container width={width ?? 316} left={left} top={top} ref={$containerRef} invert={invert}>
 | 
				
			||||||
      {children}
 | 
					      {children}
 | 
				
			||||||
    </Container>
 | 
					    </Container>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PopupContainer.defaultProps = {
 | 
				
			||||||
 | 
					  width: 316,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PopupContext = createContext<PopupContextState>({
 | 
					const PopupContext = createContext<PopupContextState>({
 | 
				
			||||||
  show: () => {},
 | 
					  show: () => {},
 | 
				
			||||||
  setTab: () => {},
 | 
					  setTab: () => {},
 | 
				
			||||||
@@ -63,6 +69,7 @@ type PopupState = {
 | 
				
			|||||||
  currentTab: number;
 | 
					  currentTab: number;
 | 
				
			||||||
  previousTab: number;
 | 
					  previousTab: number;
 | 
				
			||||||
  content: JSX.Element | null;
 | 
					  content: JSX.Element | null;
 | 
				
			||||||
 | 
					  width?: string | number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { Provider, Consumer } = PopupContext;
 | 
					const { Provider, Consumer } = PopupContext;
 | 
				
			||||||
@@ -81,7 +88,7 @@ const defaultState = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const PopupProvider: React.FC = ({ children }) => {
 | 
					export const PopupProvider: React.FC = ({ children }) => {
 | 
				
			||||||
  const [currentState, setState] = useState<PopupState>(defaultState);
 | 
					  const [currentState, setState] = useState<PopupState>(defaultState);
 | 
				
			||||||
  const show = (target: RefObject<HTMLElement>, content: JSX.Element) => {
 | 
					  const show = (target: RefObject<HTMLElement>, content: JSX.Element, width?: number | string) => {
 | 
				
			||||||
    if (target && target.current) {
 | 
					    if (target && target.current) {
 | 
				
			||||||
      const bounds = target.current.getBoundingClientRect();
 | 
					      const bounds = target.current.getBoundingClientRect();
 | 
				
			||||||
      if (bounds.left + 304 + 30 > window.innerWidth) {
 | 
					      if (bounds.left + 304 + 30 > window.innerWidth) {
 | 
				
			||||||
@@ -93,6 +100,7 @@ export const PopupProvider: React.FC = ({ children }) => {
 | 
				
			|||||||
          currentTab: 0,
 | 
					          currentTab: 0,
 | 
				
			||||||
          previousTab: 0,
 | 
					          previousTab: 0,
 | 
				
			||||||
          content,
 | 
					          content,
 | 
				
			||||||
 | 
					          width: width ?? 316,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        setState({
 | 
					        setState({
 | 
				
			||||||
@@ -103,6 +111,7 @@ export const PopupProvider: React.FC = ({ children }) => {
 | 
				
			|||||||
          currentTab: 0,
 | 
					          currentTab: 0,
 | 
				
			||||||
          previousTab: 0,
 | 
					          previousTab: 0,
 | 
				
			||||||
          content,
 | 
					          content,
 | 
				
			||||||
 | 
					          width: width ?? 316,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -144,6 +153,7 @@ export const PopupProvider: React.FC = ({ children }) => {
 | 
				
			|||||||
            top={currentState.top}
 | 
					            top={currentState.top}
 | 
				
			||||||
            left={currentState.left}
 | 
					            left={currentState.left}
 | 
				
			||||||
            onClose={() => setState(defaultState)}
 | 
					            onClose={() => setState(defaultState)}
 | 
				
			||||||
 | 
					            width={currentState.width ?? 316}
 | 
				
			||||||
          >
 | 
					          >
 | 
				
			||||||
            {currentState.content}
 | 
					            {currentState.content}
 | 
				
			||||||
            <ContainerDiamond invert={currentState.invert} />
 | 
					            <ContainerDiamond invert={currentState.invert} />
 | 
				
			||||||
@@ -162,14 +172,15 @@ type Props = {
 | 
				
			|||||||
  onClose: () => void;
 | 
					  onClose: () => void;
 | 
				
			||||||
  onPrevious?: () => void | null;
 | 
					  onPrevious?: () => void | null;
 | 
				
			||||||
  noHeader?: boolean | null;
 | 
					  noHeader?: boolean | null;
 | 
				
			||||||
 | 
					  width?: string | number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PopupMenu: React.FC<Props> = ({ title, top, left, onClose, noHeader, children, onPrevious }) => {
 | 
					const PopupMenu: React.FC<Props> = ({ width, title, top, left, onClose, noHeader, children, onPrevious }) => {
 | 
				
			||||||
  const $containerRef = useRef();
 | 
					  const $containerRef = useRef();
 | 
				
			||||||
  useOnOutsideClick($containerRef, true, onClose, null);
 | 
					  useOnOutsideClick($containerRef, true, onClose, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Container invert={false} left={left} top={top} ref={$containerRef}>
 | 
					    <Container width={width ?? 316} invert={false} left={left} top={top} ref={$containerRef}>
 | 
				
			||||||
      <Wrapper>
 | 
					      <Wrapper>
 | 
				
			||||||
        {onPrevious && (
 | 
					        {onPrevious && (
 | 
				
			||||||
          <PreviousButton onClick={onPrevious}>
 | 
					          <PreviousButton onClick={onPrevious}>
 | 
				
			||||||
@@ -217,9 +228,11 @@ export const Popup: React.FC<PopupProps> = ({ title, onClose, tab, children }) =
 | 
				
			|||||||
            <HeaderTitle>{title}</HeaderTitle>
 | 
					            <HeaderTitle>{title}</HeaderTitle>
 | 
				
			||||||
          </Header>
 | 
					          </Header>
 | 
				
			||||||
        )}
 | 
					        )}
 | 
				
			||||||
        <CloseButton onClick={() => onClose()}>
 | 
					        {onClose && (
 | 
				
			||||||
          <Cross color="#c2c6dc" />
 | 
					          <CloseButton onClick={() => onClose()}>
 | 
				
			||||||
        </CloseButton>
 | 
					            <Cross color="#c2c6dc" />
 | 
				
			||||||
 | 
					          </CloseButton>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
        <Content>{children}</Content>
 | 
					        <Content>{children}</Content>
 | 
				
			||||||
      </Wrapper>
 | 
					      </Wrapper>
 | 
				
			||||||
    </>
 | 
					    </>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,12 @@
 | 
				
			|||||||
import styled from 'styled-components';
 | 
					import styled from 'styled-components';
 | 
				
			||||||
import { mixin } from 'shared/utils/styles';
 | 
					import { mixin } from 'shared/utils/styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const AddProjectLabel = styled.span`
 | 
				
			||||||
 | 
					  padding-top: 4px;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ProjectContent = styled.div`
 | 
					export const ProjectContent = styled.div`
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
@@ -20,8 +26,7 @@ export const TeamTitle = styled.span`
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const ProjectWrapper = styled.div<{ color: string }>`
 | 
					export const ProjectWrapper = styled.div<{ color: string }>`
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  padding: 15px 25px;
 | 
					  padding: 15px 25px; border-radius: 20px;
 | 
				
			||||||
  border-radius: 20px;
 | 
					 | 
				
			||||||
  ${mixin.boxShadowCard}
 | 
					  ${mixin.boxShadowCard}
 | 
				
			||||||
  background: ${props => mixin.darken(props.color, 0.35)};
 | 
					  background: ${props => mixin.darken(props.color, 0.35)};
 | 
				
			||||||
  color: #fff;
 | 
					  color: #fff;
 | 
				
			||||||
@@ -31,8 +36,30 @@ export const ProjectWrapper = styled.div<{ color: string }>`
 | 
				
			|||||||
  height: 100px;
 | 
					  height: 100px;
 | 
				
			||||||
  transition: transform 0.25s ease;
 | 
					  transition: transform 0.25s ease;
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  &:hover {
 | 
					  &:hover {
 | 
				
			||||||
    transform: translateY(-5px);
 | 
					    transform: translateY(-5px);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const AddProjectWrapper = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  padding: 15px 25px;
 | 
				
			||||||
 | 
					  border-radius: 20px;
 | 
				
			||||||
 | 
					  ${mixin.boxShadowCard}
 | 
				
			||||||
 | 
					  border: 1px dashed;
 | 
				
			||||||
 | 
					  border-color: #c2c6dc;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  margin: 0 10px;
 | 
				
			||||||
 | 
					  width: 240px;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  height: 100px;
 | 
				
			||||||
 | 
					  transition: transform 0.25s ease;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    transform: translateY(-5px);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,23 @@
 | 
				
			|||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ProjectWrapper, ProjectContent, ProjectTitle, TeamTitle } from './Styles';
 | 
					import { Plus } from 'shared/icons';
 | 
				
			||||||
 | 
					import { AddProjectWrapper, AddProjectLabel, ProjectWrapper, ProjectContent, ProjectTitle, TeamTitle } from './Styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AddProjectItemProps = {
 | 
				
			||||||
 | 
					  onAddProject: () => void;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export const AddProjectItem: React.FC<AddProjectItemProps> = ({ onAddProject }) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <AddProjectWrapper
 | 
				
			||||||
 | 
					      onClick={() => {
 | 
				
			||||||
 | 
					        onAddProject();
 | 
				
			||||||
 | 
					      }}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Plus size={20} color="#c2c6dc" />
 | 
				
			||||||
 | 
					      <AddProjectLabel>New Project</AddProjectLabel>
 | 
				
			||||||
 | 
					    </AddProjectWrapper>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
  project: Project;
 | 
					  project: Project;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										51
									
								
								web/src/shared/components/ProjectSettings/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								web/src/shared/components/ProjectSettings/index.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import styled from 'styled-components';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ListActionsWrapper = styled.ul`
 | 
				
			||||||
 | 
					  list-style-type: none;
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ListActionItemWrapper = styled.li`
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					export const ListActionItem = styled.span`
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  font-size: 14px;
 | 
				
			||||||
 | 
					  color: #c2c6dc;
 | 
				
			||||||
 | 
					  font-weight: 400;
 | 
				
			||||||
 | 
					  padding: 6px 12px;
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  margin: 0 -12px;
 | 
				
			||||||
 | 
					  text-decoration: none;
 | 
				
			||||||
 | 
					  &:hover {
 | 
				
			||||||
 | 
					    background: rgb(115, 103, 240);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const ListSeparator = styled.hr`
 | 
				
			||||||
 | 
					  background-color: #414561;
 | 
				
			||||||
 | 
					  border: 0;
 | 
				
			||||||
 | 
					  height: 1px;
 | 
				
			||||||
 | 
					  margin: 8px 0;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {};
 | 
				
			||||||
 | 
					const ProjectSettings: React.FC<Props> = () => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <ListActionsWrapper>
 | 
				
			||||||
 | 
					        <ListActionItemWrapper onClick={() => {}}>
 | 
				
			||||||
 | 
					          <ListActionItem>Delete Project</ListActionItem>
 | 
				
			||||||
 | 
					        </ListActionItemWrapper>
 | 
				
			||||||
 | 
					      </ListActionsWrapper>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export default ProjectSettings;
 | 
				
			||||||
@@ -56,6 +56,7 @@ export const Default = () => {
 | 
				
			|||||||
          onCloseEditor={() => setEditorOpen(false)}
 | 
					          onCloseEditor={() => setEditorOpen(false)}
 | 
				
			||||||
          onEditCard={action('edit card')}
 | 
					          onEditCard={action('edit card')}
 | 
				
			||||||
          onOpenLabelsPopup={action('open popup')}
 | 
					          onOpenLabelsPopup={action('open popup')}
 | 
				
			||||||
 | 
					          onOpenMembersPopup={action('open popup')}
 | 
				
			||||||
          onArchiveCard={action('archive card')}
 | 
					          onArchiveCard={action('archive card')}
 | 
				
			||||||
          top={top}
 | 
					          top={top}
 | 
				
			||||||
          left={left}
 | 
					          left={left}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
import React, { useRef, useState, useEffect } from 'react';
 | 
					import React, { useRef, useState, useEffect } from 'react';
 | 
				
			||||||
import Cross from 'shared/icons/Cross';
 | 
					import Cross from 'shared/icons/Cross';
 | 
				
			||||||
 | 
					import styled from 'styled-components';
 | 
				
			||||||
 | 
					import Member from 'shared/components/Member';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Wrapper,
 | 
					  Wrapper,
 | 
				
			||||||
  Container,
 | 
					  Container,
 | 
				
			||||||
@@ -14,20 +16,39 @@ import {
 | 
				
			|||||||
  ListCardLabel,
 | 
					  ListCardLabel,
 | 
				
			||||||
} from './Styles';
 | 
					} from './Styles';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CardMembers = styled.div`
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  right: 0;
 | 
				
			||||||
 | 
					  bottom: 0;
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Props = {
 | 
					type Props = {
 | 
				
			||||||
  task: Task;
 | 
					  task: Task;
 | 
				
			||||||
  onCloseEditor: () => void;
 | 
					  onCloseEditor: () => void;
 | 
				
			||||||
  onEditCard: (taskGroupID: string, taskID: string, cardName: string) => void;
 | 
					  onEditCard: (taskGroupID: string, taskID: string, cardName: string) => void;
 | 
				
			||||||
  onOpenLabelsPopup: ($targetRef: React.RefObject<HTMLElement>, task: Task) => void;
 | 
					  onOpenLabelsPopup: ($targetRef: React.RefObject<HTMLElement>, task: Task) => void;
 | 
				
			||||||
 | 
					  onOpenMembersPopup: ($targetRef: React.RefObject<HTMLElement>, task: Task) => void;
 | 
				
			||||||
  onArchiveCard: (taskGroupID: string, taskID: string) => void;
 | 
					  onArchiveCard: (taskGroupID: string, taskID: string) => void;
 | 
				
			||||||
 | 
					  onCardMemberClick?: OnCardMemberClick;
 | 
				
			||||||
  top: number;
 | 
					  top: number;
 | 
				
			||||||
  left: number;
 | 
					  left: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const QuickCardEditor = ({ task, onCloseEditor, onOpenLabelsPopup, onArchiveCard, onEditCard, top, left }: Props) => {
 | 
					const QuickCardEditor = ({
 | 
				
			||||||
 | 
					  task,
 | 
				
			||||||
 | 
					  onCloseEditor,
 | 
				
			||||||
 | 
					  onOpenLabelsPopup,
 | 
				
			||||||
 | 
					  onOpenMembersPopup,
 | 
				
			||||||
 | 
					  onCardMemberClick,
 | 
				
			||||||
 | 
					  onArchiveCard,
 | 
				
			||||||
 | 
					  onEditCard,
 | 
				
			||||||
 | 
					  top,
 | 
				
			||||||
 | 
					  left,
 | 
				
			||||||
 | 
					}: Props) => {
 | 
				
			||||||
  const [currentCardTitle, setCardTitle] = useState(task.name);
 | 
					  const [currentCardTitle, setCardTitle] = useState(task.name);
 | 
				
			||||||
  const $editorRef: any = useRef();
 | 
					  const $editorRef: any = useRef();
 | 
				
			||||||
  const $labelsRef: any = useRef();
 | 
					  const $labelsRef: any = useRef();
 | 
				
			||||||
 | 
					  const $membersRef: any = useRef();
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    $editorRef.current.focus();
 | 
					    $editorRef.current.focus();
 | 
				
			||||||
    $editorRef.current.select();
 | 
					    $editorRef.current.select();
 | 
				
			||||||
@@ -71,10 +92,25 @@ const QuickCardEditor = ({ task, onCloseEditor, onOpenLabelsPopup, onArchiveCard
 | 
				
			|||||||
              value={currentCardTitle}
 | 
					              value={currentCardTitle}
 | 
				
			||||||
              ref={$editorRef}
 | 
					              ref={$editorRef}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
 | 
					            <CardMembers>
 | 
				
			||||||
 | 
					              {task.assigned &&
 | 
				
			||||||
 | 
					                task.assigned.map(member => (
 | 
				
			||||||
 | 
					                  <Member key={member.id} taskID={task.id} member={member} onCardMemberClick={onCardMemberClick} />
 | 
				
			||||||
 | 
					                ))}
 | 
				
			||||||
 | 
					            </CardMembers>
 | 
				
			||||||
          </EditorDetails>
 | 
					          </EditorDetails>
 | 
				
			||||||
        </Editor>
 | 
					        </Editor>
 | 
				
			||||||
        <SaveButton onClick={e => onEditCard(task.taskGroup.id, task.id, currentCardTitle)}>Save</SaveButton>
 | 
					        <SaveButton onClick={e => onEditCard(task.taskGroup.id, task.id, currentCardTitle)}>Save</SaveButton>
 | 
				
			||||||
        <EditorButtons>
 | 
					        <EditorButtons>
 | 
				
			||||||
 | 
					          <EditorButton
 | 
				
			||||||
 | 
					            ref={$membersRef}
 | 
				
			||||||
 | 
					            onClick={e => {
 | 
				
			||||||
 | 
					              e.stopPropagation();
 | 
				
			||||||
 | 
					              onOpenMembersPopup($membersRef, task);
 | 
				
			||||||
 | 
					            }}
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            Edit Assigned
 | 
				
			||||||
 | 
					          </EditorButton>
 | 
				
			||||||
          <EditorButton
 | 
					          <EditorButton
 | 
				
			||||||
            ref={$labelsRef}
 | 
					            ref={$labelsRef}
 | 
				
			||||||
            onClick={e => {
 | 
					            onClick={e => {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ export const Default = () => {
 | 
				
			|||||||
        lastName="Knott"
 | 
					        lastName="Knott"
 | 
				
			||||||
        initials="JK"
 | 
					        initials="JK"
 | 
				
			||||||
        onNotificationClick={action('notifications click')}
 | 
					        onNotificationClick={action('notifications click')}
 | 
				
			||||||
 | 
					        onOpenSettings={action('open settings')}
 | 
				
			||||||
        onProfileClick={onClick}
 | 
					        onProfileClick={onClick}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      {menu.isOpen && (
 | 
					      {menu.isOpen && (
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,9 +32,14 @@ import MiniProfile from 'shared/components/MiniProfile';
 | 
				
			|||||||
type ProjectHeadingProps = {
 | 
					type ProjectHeadingProps = {
 | 
				
			||||||
  projectName: string;
 | 
					  projectName: string;
 | 
				
			||||||
  onSaveProjectName?: (projectName: string) => void;
 | 
					  onSaveProjectName?: (projectName: string) => void;
 | 
				
			||||||
 | 
					  onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const ProjectHeading: React.FC<ProjectHeadingProps> = ({ projectName: initialProjectName, onSaveProjectName }) => {
 | 
					const ProjectHeading: React.FC<ProjectHeadingProps> = ({
 | 
				
			||||||
 | 
					  projectName: initialProjectName,
 | 
				
			||||||
 | 
					  onSaveProjectName,
 | 
				
			||||||
 | 
					  onOpenSettings,
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
  const [isEditProjectName, setEditProjectName] = useState(false);
 | 
					  const [isEditProjectName, setEditProjectName] = useState(false);
 | 
				
			||||||
  const [projectName, setProjectName] = useState(initialProjectName);
 | 
					  const [projectName, setProjectName] = useState(initialProjectName);
 | 
				
			||||||
  const $projectName = useRef<HTMLTextAreaElement>(null);
 | 
					  const $projectName = useRef<HTMLTextAreaElement>(null);
 | 
				
			||||||
@@ -66,6 +71,7 @@ const ProjectHeading: React.FC<ProjectHeadingProps> = ({ projectName: initialPro
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const $settings = useRef<HTMLButtonElement>(null);
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
      <Separator>»</Separator>
 | 
					      <Separator>»</Separator>
 | 
				
			||||||
@@ -87,7 +93,12 @@ const ProjectHeading: React.FC<ProjectHeadingProps> = ({ projectName: initialPro
 | 
				
			|||||||
          {projectName}
 | 
					          {projectName}
 | 
				
			||||||
        </ProjectName>
 | 
					        </ProjectName>
 | 
				
			||||||
      )}
 | 
					      )}
 | 
				
			||||||
      <ProjectSettingsButton>
 | 
					      <ProjectSettingsButton
 | 
				
			||||||
 | 
					        onClick={() => {
 | 
				
			||||||
 | 
					          onOpenSettings($settings);
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					        ref={$settings}
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
        <AngleDown color="#c2c6dc" />
 | 
					        <AngleDown color="#c2c6dc" />
 | 
				
			||||||
      </ProjectSettingsButton>
 | 
					      </ProjectSettingsButton>
 | 
				
			||||||
      <ProjectSettingsButton>
 | 
					      <ProjectSettingsButton>
 | 
				
			||||||
@@ -103,6 +114,7 @@ type NavBarProps = {
 | 
				
			|||||||
  onSaveProjectName?: (projectName: string) => void;
 | 
					  onSaveProjectName?: (projectName: string) => void;
 | 
				
			||||||
  onNotificationClick: () => void;
 | 
					  onNotificationClick: () => void;
 | 
				
			||||||
  bgColor: string;
 | 
					  bgColor: string;
 | 
				
			||||||
 | 
					  onOpenSettings: ($target: React.RefObject<HTMLElement>) => void;
 | 
				
			||||||
  firstName: string;
 | 
					  firstName: string;
 | 
				
			||||||
  lastName: string;
 | 
					  lastName: string;
 | 
				
			||||||
  initials: string;
 | 
					  initials: string;
 | 
				
			||||||
@@ -119,6 +131,7 @@ const NavBar: React.FC<NavBarProps> = ({
 | 
				
			|||||||
  initials,
 | 
					  initials,
 | 
				
			||||||
  bgColor,
 | 
					  bgColor,
 | 
				
			||||||
  projectMembers,
 | 
					  projectMembers,
 | 
				
			||||||
 | 
					  onOpenSettings,
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
  const $profileRef: any = useRef(null);
 | 
					  const $profileRef: any = useRef(null);
 | 
				
			||||||
  const handleProfileClick = () => {
 | 
					  const handleProfileClick = () => {
 | 
				
			||||||
@@ -147,7 +160,13 @@ const NavBar: React.FC<NavBarProps> = ({
 | 
				
			|||||||
        <ProjectActions>
 | 
					        <ProjectActions>
 | 
				
			||||||
          <ProjectMeta>
 | 
					          <ProjectMeta>
 | 
				
			||||||
            <ProjectSwitcher>Projects</ProjectSwitcher>
 | 
					            <ProjectSwitcher>Projects</ProjectSwitcher>
 | 
				
			||||||
            {projectName && <ProjectHeading projectName={projectName} onSaveProjectName={onSaveProjectName} />}
 | 
					            {projectName && (
 | 
				
			||||||
 | 
					              <ProjectHeading
 | 
				
			||||||
 | 
					                onOpenSettings={onOpenSettings}
 | 
				
			||||||
 | 
					                projectName={projectName}
 | 
				
			||||||
 | 
					                onSaveProjectName={onSaveProjectName}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
          </ProjectMeta>
 | 
					          </ProjectMeta>
 | 
				
			||||||
          {projectName && (
 | 
					          {projectName && (
 | 
				
			||||||
            <ProjectTabs>
 | 
					            <ProjectTabs>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -136,6 +136,7 @@ export type Query = {
 | 
				
			|||||||
  findProject: Project;
 | 
					  findProject: Project;
 | 
				
			||||||
  findTask: Task;
 | 
					  findTask: Task;
 | 
				
			||||||
  projects: Array<Project>;
 | 
					  projects: Array<Project>;
 | 
				
			||||||
 | 
					  teams: Array<Team>;
 | 
				
			||||||
  labelColors: Array<LabelColor>;
 | 
					  labelColors: Array<LabelColor>;
 | 
				
			||||||
  taskGroups: Array<TaskGroup>;
 | 
					  taskGroups: Array<TaskGroup>;
 | 
				
			||||||
  me: UserAccount;
 | 
					  me: UserAccount;
 | 
				
			||||||
@@ -201,8 +202,8 @@ export type NewTask = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type NewTaskLocation = {
 | 
					export type NewTaskLocation = {
 | 
				
			||||||
  taskID: Scalars['String'];
 | 
					  taskID: Scalars['UUID'];
 | 
				
			||||||
  taskGroupID: Scalars['String'];
 | 
					  taskGroupID: Scalars['UUID'];
 | 
				
			||||||
  position: Scalars['Float'];
 | 
					  position: Scalars['Float'];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -302,6 +303,12 @@ export type UpdateProjectName = {
 | 
				
			|||||||
  name: Scalars['String'];
 | 
					  name: Scalars['String'];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type UpdateTaskLocationPayload = {
 | 
				
			||||||
 | 
					   __typename?: 'UpdateTaskLocationPayload';
 | 
				
			||||||
 | 
					  previousTaskGroupID: Scalars['UUID'];
 | 
				
			||||||
 | 
					  task: Task;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Mutation = {
 | 
					export type Mutation = {
 | 
				
			||||||
   __typename?: 'Mutation';
 | 
					   __typename?: 'Mutation';
 | 
				
			||||||
  createRefreshToken: RefreshToken;
 | 
					  createRefreshToken: RefreshToken;
 | 
				
			||||||
@@ -322,7 +329,7 @@ export type Mutation = {
 | 
				
			|||||||
  toggleTaskLabel: ToggleTaskLabelPayload;
 | 
					  toggleTaskLabel: ToggleTaskLabelPayload;
 | 
				
			||||||
  createTask: Task;
 | 
					  createTask: Task;
 | 
				
			||||||
  updateTaskDescription: Task;
 | 
					  updateTaskDescription: Task;
 | 
				
			||||||
  updateTaskLocation: Task;
 | 
					  updateTaskLocation: UpdateTaskLocationPayload;
 | 
				
			||||||
  updateTaskName: Task;
 | 
					  updateTaskName: Task;
 | 
				
			||||||
  deleteTask: DeleteTaskPayload;
 | 
					  deleteTask: DeleteTaskPayload;
 | 
				
			||||||
  assignTask: Task;
 | 
					  assignTask: Task;
 | 
				
			||||||
@@ -468,6 +475,25 @@ export type AssignTaskMutation = (
 | 
				
			|||||||
  ) }
 | 
					  ) }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CreateProjectMutationVariables = {
 | 
				
			||||||
 | 
					  teamID: Scalars['UUID'];
 | 
				
			||||||
 | 
					  userID: Scalars['UUID'];
 | 
				
			||||||
 | 
					  name: Scalars['String'];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CreateProjectMutation = (
 | 
				
			||||||
 | 
					  { __typename?: 'Mutation' }
 | 
				
			||||||
 | 
					  & { createProject: (
 | 
				
			||||||
 | 
					    { __typename?: 'Project' }
 | 
				
			||||||
 | 
					    & Pick<Project, 'id' | 'name'>
 | 
				
			||||||
 | 
					    & { team: (
 | 
				
			||||||
 | 
					      { __typename?: 'Team' }
 | 
				
			||||||
 | 
					      & Pick<Team, 'id' | 'name'>
 | 
				
			||||||
 | 
					    ) }
 | 
				
			||||||
 | 
					  ) }
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type CreateProjectLabelMutationVariables = {
 | 
					export type CreateProjectLabelMutationVariables = {
 | 
				
			||||||
  projectID: Scalars['UUID'];
 | 
					  projectID: Scalars['UUID'];
 | 
				
			||||||
  labelColorID: Scalars['UUID'];
 | 
					  labelColorID: Scalars['UUID'];
 | 
				
			||||||
@@ -686,7 +712,10 @@ export type GetProjectsQueryVariables = {};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export type GetProjectsQuery = (
 | 
					export type GetProjectsQuery = (
 | 
				
			||||||
  { __typename?: 'Query' }
 | 
					  { __typename?: 'Query' }
 | 
				
			||||||
  & { projects: Array<(
 | 
					  & { teams: Array<(
 | 
				
			||||||
 | 
					    { __typename?: 'Team' }
 | 
				
			||||||
 | 
					    & Pick<Team, 'id' | 'name' | 'createdAt'>
 | 
				
			||||||
 | 
					  )>, projects: Array<(
 | 
				
			||||||
    { __typename?: 'Project' }
 | 
					    { __typename?: 'Project' }
 | 
				
			||||||
    & Pick<Project, 'id' | 'name'>
 | 
					    & Pick<Project, 'id' | 'name'>
 | 
				
			||||||
    & { team: (
 | 
					    & { team: (
 | 
				
			||||||
@@ -821,8 +850,8 @@ export type UpdateTaskGroupLocationMutation = (
 | 
				
			|||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type UpdateTaskLocationMutationVariables = {
 | 
					export type UpdateTaskLocationMutationVariables = {
 | 
				
			||||||
  taskID: Scalars['String'];
 | 
					  taskID: Scalars['UUID'];
 | 
				
			||||||
  taskGroupID: Scalars['String'];
 | 
					  taskGroupID: Scalars['UUID'];
 | 
				
			||||||
  position: Scalars['Float'];
 | 
					  position: Scalars['Float'];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -830,8 +859,16 @@ export type UpdateTaskLocationMutationVariables = {
 | 
				
			|||||||
export type UpdateTaskLocationMutation = (
 | 
					export type UpdateTaskLocationMutation = (
 | 
				
			||||||
  { __typename?: 'Mutation' }
 | 
					  { __typename?: 'Mutation' }
 | 
				
			||||||
  & { updateTaskLocation: (
 | 
					  & { updateTaskLocation: (
 | 
				
			||||||
    { __typename?: 'Task' }
 | 
					    { __typename?: 'UpdateTaskLocationPayload' }
 | 
				
			||||||
    & Pick<Task, 'id' | 'createdAt' | 'name' | 'position'>
 | 
					    & Pick<UpdateTaskLocationPayload, 'previousTaskGroupID'>
 | 
				
			||||||
 | 
					    & { task: (
 | 
				
			||||||
 | 
					      { __typename?: 'Task' }
 | 
				
			||||||
 | 
					      & Pick<Task, 'id' | 'createdAt' | 'name' | 'position'>
 | 
				
			||||||
 | 
					      & { taskGroup: (
 | 
				
			||||||
 | 
					        { __typename?: 'TaskGroup' }
 | 
				
			||||||
 | 
					        & Pick<TaskGroup, 'id'>
 | 
				
			||||||
 | 
					      ) }
 | 
				
			||||||
 | 
					    ) }
 | 
				
			||||||
  ) }
 | 
					  ) }
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -888,6 +925,45 @@ export function useAssignTaskMutation(baseOptions?: ApolloReactHooks.MutationHoo
 | 
				
			|||||||
export type AssignTaskMutationHookResult = ReturnType<typeof useAssignTaskMutation>;
 | 
					export type AssignTaskMutationHookResult = ReturnType<typeof useAssignTaskMutation>;
 | 
				
			||||||
export type AssignTaskMutationResult = ApolloReactCommon.MutationResult<AssignTaskMutation>;
 | 
					export type AssignTaskMutationResult = ApolloReactCommon.MutationResult<AssignTaskMutation>;
 | 
				
			||||||
export type AssignTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<AssignTaskMutation, AssignTaskMutationVariables>;
 | 
					export type AssignTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<AssignTaskMutation, AssignTaskMutationVariables>;
 | 
				
			||||||
 | 
					export const CreateProjectDocument = gql`
 | 
				
			||||||
 | 
					    mutation createProject($teamID: UUID!, $userID: UUID!, $name: String!) {
 | 
				
			||||||
 | 
					  createProject(input: {teamID: $teamID, userID: $userID, name: $name}) {
 | 
				
			||||||
 | 
					    id
 | 
				
			||||||
 | 
					    name
 | 
				
			||||||
 | 
					    team {
 | 
				
			||||||
 | 
					      id
 | 
				
			||||||
 | 
					      name
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					    `;
 | 
				
			||||||
 | 
					export type CreateProjectMutationFn = ApolloReactCommon.MutationFunction<CreateProjectMutation, CreateProjectMutationVariables>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * __useCreateProjectMutation__
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * To run a mutation, you first call `useCreateProjectMutation` within a React component and pass it any options that fit your needs.
 | 
				
			||||||
 | 
					 * When your component renders, `useCreateProjectMutation` returns a tuple that includes:
 | 
				
			||||||
 | 
					 * - A mutate function that you can call at any time to execute the mutation
 | 
				
			||||||
 | 
					 * - An object with fields that represent the current status of the mutation's execution
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @example
 | 
				
			||||||
 | 
					 * const [createProjectMutation, { data, loading, error }] = useCreateProjectMutation({
 | 
				
			||||||
 | 
					 *   variables: {
 | 
				
			||||||
 | 
					 *      teamID: // value for 'teamID'
 | 
				
			||||||
 | 
					 *      userID: // value for 'userID'
 | 
				
			||||||
 | 
					 *      name: // value for 'name'
 | 
				
			||||||
 | 
					 *   },
 | 
				
			||||||
 | 
					 * });
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export function useCreateProjectMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateProjectMutation, CreateProjectMutationVariables>) {
 | 
				
			||||||
 | 
					        return ApolloReactHooks.useMutation<CreateProjectMutation, CreateProjectMutationVariables>(CreateProjectDocument, baseOptions);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					export type CreateProjectMutationHookResult = ReturnType<typeof useCreateProjectMutation>;
 | 
				
			||||||
 | 
					export type CreateProjectMutationResult = ApolloReactCommon.MutationResult<CreateProjectMutation>;
 | 
				
			||||||
 | 
					export type CreateProjectMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateProjectMutation, CreateProjectMutationVariables>;
 | 
				
			||||||
export const CreateProjectLabelDocument = gql`
 | 
					export const CreateProjectLabelDocument = gql`
 | 
				
			||||||
    mutation createProjectLabel($projectID: UUID!, $labelColorID: UUID!, $name: String!) {
 | 
					    mutation createProjectLabel($projectID: UUID!, $labelColorID: UUID!, $name: String!) {
 | 
				
			||||||
  createProjectLabel(input: {projectID: $projectID, labelColorID: $labelColorID, name: $name}) {
 | 
					  createProjectLabel(input: {projectID: $projectID, labelColorID: $labelColorID, name: $name}) {
 | 
				
			||||||
@@ -1304,6 +1380,11 @@ export type FindTaskLazyQueryHookResult = ReturnType<typeof useFindTaskLazyQuery
 | 
				
			|||||||
export type FindTaskQueryResult = ApolloReactCommon.QueryResult<FindTaskQuery, FindTaskQueryVariables>;
 | 
					export type FindTaskQueryResult = ApolloReactCommon.QueryResult<FindTaskQuery, FindTaskQueryVariables>;
 | 
				
			||||||
export const GetProjectsDocument = gql`
 | 
					export const GetProjectsDocument = gql`
 | 
				
			||||||
    query getProjects {
 | 
					    query getProjects {
 | 
				
			||||||
 | 
					  teams {
 | 
				
			||||||
 | 
					    id
 | 
				
			||||||
 | 
					    name
 | 
				
			||||||
 | 
					    createdAt
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  projects {
 | 
					  projects {
 | 
				
			||||||
    id
 | 
					    id
 | 
				
			||||||
    name
 | 
					    name
 | 
				
			||||||
@@ -1609,12 +1690,18 @@ export type UpdateTaskGroupLocationMutationHookResult = ReturnType<typeof useUpd
 | 
				
			|||||||
export type UpdateTaskGroupLocationMutationResult = ApolloReactCommon.MutationResult<UpdateTaskGroupLocationMutation>;
 | 
					export type UpdateTaskGroupLocationMutationResult = ApolloReactCommon.MutationResult<UpdateTaskGroupLocationMutation>;
 | 
				
			||||||
export type UpdateTaskGroupLocationMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskGroupLocationMutation, UpdateTaskGroupLocationMutationVariables>;
 | 
					export type UpdateTaskGroupLocationMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskGroupLocationMutation, UpdateTaskGroupLocationMutationVariables>;
 | 
				
			||||||
export const UpdateTaskLocationDocument = gql`
 | 
					export const UpdateTaskLocationDocument = gql`
 | 
				
			||||||
    mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) {
 | 
					    mutation updateTaskLocation($taskID: UUID!, $taskGroupID: UUID!, $position: Float!) {
 | 
				
			||||||
  updateTaskLocation(input: {taskID: $taskID, taskGroupID: $taskGroupID, position: $position}) {
 | 
					  updateTaskLocation(input: {taskID: $taskID, taskGroupID: $taskGroupID, position: $position}) {
 | 
				
			||||||
    id
 | 
					    previousTaskGroupID
 | 
				
			||||||
    createdAt
 | 
					    task {
 | 
				
			||||||
    name
 | 
					      id
 | 
				
			||||||
    position
 | 
					      createdAt
 | 
				
			||||||
 | 
					      name
 | 
				
			||||||
 | 
					      position
 | 
				
			||||||
 | 
					      taskGroup {
 | 
				
			||||||
 | 
					        id
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
    `;
 | 
					    `;
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								web/src/shared/graphql/createProject.graphqls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								web/src/shared/graphql/createProject.graphqls
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					mutation createProject($teamID: UUID!, $userID: UUID!, $name: String!) {
 | 
				
			||||||
 | 
					  createProject(input: {teamID: $teamID, userID: $userID, name: $name}) {
 | 
				
			||||||
 | 
					    id
 | 
				
			||||||
 | 
					    name
 | 
				
			||||||
 | 
					    team {
 | 
				
			||||||
 | 
					      id
 | 
				
			||||||
 | 
					      name
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,9 @@
 | 
				
			|||||||
query getProjects {
 | 
					query getProjects {
 | 
				
			||||||
 | 
					  teams {
 | 
				
			||||||
 | 
					    id
 | 
				
			||||||
 | 
					    name
 | 
				
			||||||
 | 
					    createdAt
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  projects {
 | 
					  projects {
 | 
				
			||||||
    id
 | 
					    id
 | 
				
			||||||
    name
 | 
					    name
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,14 @@
 | 
				
			|||||||
mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) {
 | 
					mutation updateTaskLocation($taskID: UUID!, $taskGroupID: UUID!, $position: Float!) {
 | 
				
			||||||
  updateTaskLocation(input: { taskID: $taskID, taskGroupID: $taskGroupID, position: $position }) {
 | 
					  updateTaskLocation(input: { taskID: $taskID, taskGroupID: $taskGroupID, position: $position }) {
 | 
				
			||||||
    id
 | 
					    previousTaskGroupID
 | 
				
			||||||
    createdAt
 | 
					    task {
 | 
				
			||||||
    name
 | 
					      id
 | 
				
			||||||
    position
 | 
					      createdAt
 | 
				
			||||||
 | 
					      name
 | 
				
			||||||
 | 
					      position
 | 
				
			||||||
 | 
					      taskGroup {
 | 
				
			||||||
 | 
					        id
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								web/src/shared/icons/ArrowLeft.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								web/src/shared/icons/ArrowLeft.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Props = {
 | 
				
			||||||
 | 
					  width: number | string;
 | 
				
			||||||
 | 
					  height: number | string;
 | 
				
			||||||
 | 
					  color: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ArrowLeft = ({ width, height, color }: Props) => {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
 | 
				
			||||||
 | 
					      <path
 | 
				
			||||||
 | 
					        fill={color}
 | 
				
			||||||
 | 
					        d="M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </svg>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ArrowLeft.defaultProps = {
 | 
				
			||||||
 | 
					  width: 16,
 | 
				
			||||||
 | 
					  height: 16,
 | 
				
			||||||
 | 
					  color: '#000',
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default ArrowLeft;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
import Cross from './Cross';
 | 
					import Cross from './Cross';
 | 
				
			||||||
import Cog from './Cog';
 | 
					import Cog from './Cog';
 | 
				
			||||||
 | 
					import ArrowLeft from './ArrowLeft';
 | 
				
			||||||
import Bolt from './Bolt';
 | 
					import Bolt from './Bolt';
 | 
				
			||||||
import Plus from './Plus';
 | 
					import Plus from './Plus';
 | 
				
			||||||
import Bell from './Bell';
 | 
					import Bell from './Bell';
 | 
				
			||||||
@@ -43,5 +44,6 @@ export {
 | 
				
			|||||||
  User,
 | 
					  User,
 | 
				
			||||||
  Users,
 | 
					  Users,
 | 
				
			||||||
  Lock,
 | 
					  Lock,
 | 
				
			||||||
 | 
					  ArrowLeft,
 | 
				
			||||||
  ToggleOn,
 | 
					  ToggleOn,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3224,6 +3224,15 @@
 | 
				
			|||||||
    "@types/history" "*"
 | 
					    "@types/history" "*"
 | 
				
			||||||
    "@types/react" "*"
 | 
					    "@types/react" "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@types/react-select@^3.0.13":
 | 
				
			||||||
 | 
					  version "3.0.13"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-3.0.13.tgz#b1a05eae0f65fb4f899b4db1f89b8420cb9f3656"
 | 
				
			||||||
 | 
					  integrity sha512-JxmSArGgzAOtb37+Jz2+3av8rVmp/3s3DGwlcP+g59/a3owkiuuU4/Jajd+qA32beDPHy4gJR2kkxagPY3j9kg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@types/react" "*"
 | 
				
			||||||
 | 
					    "@types/react-dom" "*"
 | 
				
			||||||
 | 
					    "@types/react-transition-group" "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@types/react-syntax-highlighter@11.0.2":
 | 
					"@types/react-syntax-highlighter@11.0.2":
 | 
				
			||||||
  version "11.0.2"
 | 
					  version "11.0.2"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz#a2e3ff657d7c47813f80ca930f3d959c31ec51e3"
 | 
					  resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.2.tgz#a2e3ff657d7c47813f80ca930f3d959c31ec51e3"
 | 
				
			||||||
@@ -3245,6 +3254,13 @@
 | 
				
			|||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    "@types/react" "*"
 | 
					    "@types/react" "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"@types/react-transition-group@*":
 | 
				
			||||||
 | 
					  version "4.4.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.0.tgz#882839db465df1320e4753e6e9f70ca7e9b4d46d"
 | 
				
			||||||
 | 
					  integrity sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    "@types/react" "*"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@types/react@*", "@types/react@^16.9.21":
 | 
					"@types/react@*", "@types/react@^16.9.21":
 | 
				
			||||||
  version "16.9.21"
 | 
					  version "16.9.21"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.21.tgz#99e274e2ecfab6bb93920e918341daa3198b348d"
 | 
					  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.21.tgz#99e274e2ecfab6bb93920e918341daa3198b348d"
 | 
				
			||||||
@@ -13677,7 +13693,7 @@ react-scripts@3.4.0:
 | 
				
			|||||||
  optionalDependencies:
 | 
					  optionalDependencies:
 | 
				
			||||||
    fsevents "2.1.2"
 | 
					    fsevents "2.1.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
react-select@^3.0.8:
 | 
					react-select@^3.0.8, react-select@^3.1.0:
 | 
				
			||||||
  version "3.1.0"
 | 
					  version "3.1.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27"
 | 
					  resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27"
 | 
				
			||||||
  integrity sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==
 | 
					  integrity sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user