feature: add more to project pane

This commit is contained in:
Jordan Knott 2020-05-26 19:53:31 -05:00
parent 7e78ee36b4
commit fba4de631f
64 changed files with 1845 additions and 582 deletions

View File

@ -41,9 +41,11 @@ type ResolverRoot interface {
Project() ProjectResolver Project() ProjectResolver
ProjectLabel() ProjectLabelResolver ProjectLabel() ProjectLabelResolver
Query() QueryResolver Query() QueryResolver
RefreshToken() RefreshTokenResolver
Task() TaskResolver Task() TaskResolver
TaskGroup() TaskGroupResolver TaskGroup() TaskGroupResolver
TaskLabel() TaskLabelResolver TaskLabel() TaskLabelResolver
Team() TeamResolver
UserAccount() UserAccountResolver UserAccount() UserAccountResolver
} }
@ -90,27 +92,27 @@ type ComplexityRoot struct {
Project struct { Project struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
ID func(childComplexity int) int
Labels func(childComplexity int) int Labels func(childComplexity int) int
Members func(childComplexity int) int Members func(childComplexity int) int
Name func(childComplexity int) int Name func(childComplexity int) int
Owner func(childComplexity int) int Owner func(childComplexity int) int
ProjectID func(childComplexity int) int
TaskGroups func(childComplexity int) int TaskGroups func(childComplexity int) int
Team func(childComplexity int) int Team func(childComplexity int) int
} }
ProjectLabel struct { ProjectLabel struct {
ColorHex func(childComplexity int) int ColorHex func(childComplexity int) int
CreatedDate func(childComplexity int) int CreatedDate func(childComplexity int) int
Name func(childComplexity int) int ID func(childComplexity int) int
ProjectLabelID func(childComplexity int) int Name func(childComplexity int) int
} }
ProjectMember struct { ProjectMember struct {
FirstName func(childComplexity int) int FirstName func(childComplexity int) int
ID func(childComplexity int) int
LastName func(childComplexity int) int LastName func(childComplexity int) int
ProfileIcon func(childComplexity int) int ProfileIcon func(childComplexity int) int
UserID func(childComplexity int) int
} }
Query struct { Query struct {
@ -126,7 +128,7 @@ type ComplexityRoot struct {
RefreshToken struct { RefreshToken struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
ExpiresAt func(childComplexity int) int ExpiresAt func(childComplexity int) int
TokenID func(childComplexity int) int ID func(childComplexity int) int
UserID func(childComplexity int) int UserID func(childComplexity int) int
} }
@ -134,43 +136,43 @@ type ComplexityRoot struct {
Assigned func(childComplexity int) int Assigned func(childComplexity int) int
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
Description func(childComplexity int) int Description func(childComplexity int) int
ID func(childComplexity int) int
Labels func(childComplexity int) int Labels func(childComplexity int) int
Name func(childComplexity int) int Name func(childComplexity int) int
Position func(childComplexity int) int Position func(childComplexity int) int
TaskGroup func(childComplexity int) int TaskGroup func(childComplexity int) int
TaskID func(childComplexity int) int
} }
TaskGroup struct { TaskGroup struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
Name func(childComplexity int) int ID func(childComplexity int) int
Position func(childComplexity int) int Name func(childComplexity int) int
ProjectID func(childComplexity int) int Position func(childComplexity int) int
TaskGroupID func(childComplexity int) int ProjectID func(childComplexity int) int
Tasks func(childComplexity int) int Tasks func(childComplexity int) int
} }
TaskLabel struct { TaskLabel struct {
AssignedDate func(childComplexity int) int AssignedDate func(childComplexity int) int
ColorHex func(childComplexity int) int ColorHex func(childComplexity int) int
ID func(childComplexity int) int
Name func(childComplexity int) int Name func(childComplexity int) int
ProjectLabelID func(childComplexity int) int ProjectLabelID func(childComplexity int) int
TaskLabelID func(childComplexity int) int
} }
Team struct { Team struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
ID func(childComplexity int) int
Name func(childComplexity int) int Name func(childComplexity int) int
TeamID 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
FirstName func(childComplexity int) int FirstName func(childComplexity int) int
ID func(childComplexity int) int
LastName func(childComplexity int) int LastName func(childComplexity int) int
ProfileIcon func(childComplexity int) int ProfileIcon func(childComplexity int) int
UserID func(childComplexity int) int
Username func(childComplexity int) int Username func(childComplexity int) int
} }
} }
@ -196,6 +198,8 @@ type MutationResolver interface {
LogoutUser(ctx context.Context, input LogoutUser) (bool, error) LogoutUser(ctx context.Context, input LogoutUser) (bool, error)
} }
type ProjectResolver interface { type ProjectResolver interface {
ID(ctx context.Context, obj *pg.Project) (uuid.UUID, error)
Team(ctx context.Context, obj *pg.Project) (*pg.Team, error) Team(ctx context.Context, obj *pg.Project) (*pg.Team, error)
Owner(ctx context.Context, obj *pg.Project) (*ProjectMember, error) Owner(ctx context.Context, obj *pg.Project) (*ProjectMember, error)
TaskGroups(ctx context.Context, obj *pg.Project) ([]pg.TaskGroup, error) TaskGroups(ctx context.Context, obj *pg.Project) ([]pg.TaskGroup, error)
@ -203,6 +207,8 @@ type ProjectResolver interface {
Labels(ctx context.Context, obj *pg.Project) ([]pg.ProjectLabel, error) Labels(ctx context.Context, obj *pg.Project) ([]pg.ProjectLabel, error)
} }
type ProjectLabelResolver interface { type ProjectLabelResolver interface {
ID(ctx context.Context, obj *pg.ProjectLabel) (uuid.UUID, error)
ColorHex(ctx context.Context, obj *pg.ProjectLabel) (string, error) ColorHex(ctx context.Context, obj *pg.ProjectLabel) (string, error)
Name(ctx context.Context, obj *pg.ProjectLabel) (*string, error) Name(ctx context.Context, obj *pg.ProjectLabel) (*string, error)
} }
@ -215,7 +221,11 @@ type QueryResolver interface {
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)
} }
type RefreshTokenResolver interface {
ID(ctx context.Context, obj *pg.RefreshToken) (uuid.UUID, error)
}
type TaskResolver interface { type TaskResolver interface {
ID(ctx context.Context, obj *pg.Task) (uuid.UUID, error)
TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error) TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error)
Description(ctx context.Context, obj *pg.Task) (*string, error) Description(ctx context.Context, obj *pg.Task) (*string, error)
@ -223,15 +233,23 @@ type TaskResolver interface {
Labels(ctx context.Context, obj *pg.Task) ([]pg.TaskLabel, error) Labels(ctx context.Context, obj *pg.Task) ([]pg.TaskLabel, error)
} }
type TaskGroupResolver interface { type TaskGroupResolver interface {
ID(ctx context.Context, obj *pg.TaskGroup) (uuid.UUID, error)
ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error) ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error)
Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.Task, error) Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.Task, error)
} }
type TaskLabelResolver interface { type TaskLabelResolver interface {
ID(ctx context.Context, obj *pg.TaskLabel) (uuid.UUID, error)
ColorHex(ctx context.Context, obj *pg.TaskLabel) (string, error) ColorHex(ctx context.Context, obj *pg.TaskLabel) (string, error)
Name(ctx context.Context, obj *pg.TaskLabel) (*string, error) Name(ctx context.Context, obj *pg.TaskLabel) (*string, error)
} }
type TeamResolver interface {
ID(ctx context.Context, obj *pg.Team) (uuid.UUID, error)
}
type UserAccountResolver interface { type UserAccountResolver interface {
ID(ctx context.Context, obj *pg.UserAccount) (uuid.UUID, error)
ProfileIcon(ctx context.Context, obj *pg.UserAccount) (*ProfileIcon, error) ProfileIcon(ctx context.Context, obj *pg.UserAccount) (*ProfileIcon, error)
} }
@ -522,6 +540,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Project.CreatedAt(childComplexity), true return e.complexity.Project.CreatedAt(childComplexity), true
case "Project.id":
if e.complexity.Project.ID == nil {
break
}
return e.complexity.Project.ID(childComplexity), true
case "Project.labels": case "Project.labels":
if e.complexity.Project.Labels == nil { if e.complexity.Project.Labels == nil {
break break
@ -550,13 +575,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Project.Owner(childComplexity), true return e.complexity.Project.Owner(childComplexity), true
case "Project.projectID":
if e.complexity.Project.ProjectID == nil {
break
}
return e.complexity.Project.ProjectID(childComplexity), true
case "Project.taskGroups": case "Project.taskGroups":
if e.complexity.Project.TaskGroups == nil { if e.complexity.Project.TaskGroups == nil {
break break
@ -585,6 +603,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ProjectLabel.CreatedDate(childComplexity), true return e.complexity.ProjectLabel.CreatedDate(childComplexity), true
case "ProjectLabel.id":
if e.complexity.ProjectLabel.ID == nil {
break
}
return e.complexity.ProjectLabel.ID(childComplexity), true
case "ProjectLabel.name": case "ProjectLabel.name":
if e.complexity.ProjectLabel.Name == nil { if e.complexity.ProjectLabel.Name == nil {
break break
@ -592,13 +617,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ProjectLabel.Name(childComplexity), true return e.complexity.ProjectLabel.Name(childComplexity), true
case "ProjectLabel.projectLabelID":
if e.complexity.ProjectLabel.ProjectLabelID == nil {
break
}
return e.complexity.ProjectLabel.ProjectLabelID(childComplexity), true
case "ProjectMember.firstName": case "ProjectMember.firstName":
if e.complexity.ProjectMember.FirstName == nil { if e.complexity.ProjectMember.FirstName == nil {
break break
@ -606,6 +624,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ProjectMember.FirstName(childComplexity), true return e.complexity.ProjectMember.FirstName(childComplexity), true
case "ProjectMember.id":
if e.complexity.ProjectMember.ID == nil {
break
}
return e.complexity.ProjectMember.ID(childComplexity), true
case "ProjectMember.lastName": case "ProjectMember.lastName":
if e.complexity.ProjectMember.LastName == nil { if e.complexity.ProjectMember.LastName == nil {
break break
@ -620,13 +645,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ProjectMember.ProfileIcon(childComplexity), true return e.complexity.ProjectMember.ProfileIcon(childComplexity), true
case "ProjectMember.userID":
if e.complexity.ProjectMember.UserID == nil {
break
}
return e.complexity.ProjectMember.UserID(childComplexity), true
case "Query.findProject": case "Query.findProject":
if e.complexity.Query.FindProject == nil { if e.complexity.Query.FindProject == nil {
break break
@ -710,12 +728,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.RefreshToken.ExpiresAt(childComplexity), true return e.complexity.RefreshToken.ExpiresAt(childComplexity), true
case "RefreshToken.tokenId": case "RefreshToken.id":
if e.complexity.RefreshToken.TokenID == nil { if e.complexity.RefreshToken.ID == nil {
break break
} }
return e.complexity.RefreshToken.TokenID(childComplexity), true return e.complexity.RefreshToken.ID(childComplexity), true
case "RefreshToken.userId": case "RefreshToken.userId":
if e.complexity.RefreshToken.UserID == nil { if e.complexity.RefreshToken.UserID == nil {
@ -745,6 +763,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Task.Description(childComplexity), true return e.complexity.Task.Description(childComplexity), true
case "Task.id":
if e.complexity.Task.ID == nil {
break
}
return e.complexity.Task.ID(childComplexity), true
case "Task.labels": case "Task.labels":
if e.complexity.Task.Labels == nil { if e.complexity.Task.Labels == nil {
break break
@ -773,13 +798,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Task.TaskGroup(childComplexity), true return e.complexity.Task.TaskGroup(childComplexity), true
case "Task.taskID":
if e.complexity.Task.TaskID == nil {
break
}
return e.complexity.Task.TaskID(childComplexity), true
case "TaskGroup.createdAt": case "TaskGroup.createdAt":
if e.complexity.TaskGroup.CreatedAt == nil { if e.complexity.TaskGroup.CreatedAt == nil {
break break
@ -787,6 +805,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.TaskGroup.CreatedAt(childComplexity), true return e.complexity.TaskGroup.CreatedAt(childComplexity), true
case "TaskGroup.id":
if e.complexity.TaskGroup.ID == nil {
break
}
return e.complexity.TaskGroup.ID(childComplexity), true
case "TaskGroup.name": case "TaskGroup.name":
if e.complexity.TaskGroup.Name == nil { if e.complexity.TaskGroup.Name == nil {
break break
@ -808,13 +833,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.TaskGroup.ProjectID(childComplexity), true return e.complexity.TaskGroup.ProjectID(childComplexity), true
case "TaskGroup.taskGroupID":
if e.complexity.TaskGroup.TaskGroupID == nil {
break
}
return e.complexity.TaskGroup.TaskGroupID(childComplexity), true
case "TaskGroup.tasks": case "TaskGroup.tasks":
if e.complexity.TaskGroup.Tasks == nil { if e.complexity.TaskGroup.Tasks == nil {
break break
@ -836,6 +854,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.TaskLabel.ColorHex(childComplexity), true return e.complexity.TaskLabel.ColorHex(childComplexity), true
case "TaskLabel.id":
if e.complexity.TaskLabel.ID == nil {
break
}
return e.complexity.TaskLabel.ID(childComplexity), true
case "TaskLabel.name": case "TaskLabel.name":
if e.complexity.TaskLabel.Name == nil { if e.complexity.TaskLabel.Name == nil {
break break
@ -850,13 +875,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.TaskLabel.ProjectLabelID(childComplexity), true return e.complexity.TaskLabel.ProjectLabelID(childComplexity), true
case "TaskLabel.taskLabelID":
if e.complexity.TaskLabel.TaskLabelID == nil {
break
}
return e.complexity.TaskLabel.TaskLabelID(childComplexity), true
case "Team.createdAt": case "Team.createdAt":
if e.complexity.Team.CreatedAt == nil { if e.complexity.Team.CreatedAt == nil {
break break
@ -864,6 +882,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Team.CreatedAt(childComplexity), true return e.complexity.Team.CreatedAt(childComplexity), true
case "Team.id":
if e.complexity.Team.ID == nil {
break
}
return e.complexity.Team.ID(childComplexity), true
case "Team.name": case "Team.name":
if e.complexity.Team.Name == nil { if e.complexity.Team.Name == nil {
break break
@ -871,13 +896,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Team.Name(childComplexity), true return e.complexity.Team.Name(childComplexity), true
case "Team.teamID":
if e.complexity.Team.TeamID == nil {
break
}
return e.complexity.Team.TeamID(childComplexity), true
case "UserAccount.createdAt": case "UserAccount.createdAt":
if e.complexity.UserAccount.CreatedAt == nil { if e.complexity.UserAccount.CreatedAt == nil {
break break
@ -899,6 +917,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.UserAccount.FirstName(childComplexity), true return e.complexity.UserAccount.FirstName(childComplexity), true
case "UserAccount.id":
if e.complexity.UserAccount.ID == nil {
break
}
return e.complexity.UserAccount.ID(childComplexity), true
case "UserAccount.lastName": case "UserAccount.lastName":
if e.complexity.UserAccount.LastName == nil { if e.complexity.UserAccount.LastName == nil {
break break
@ -913,13 +938,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.UserAccount.ProfileIcon(childComplexity), true return e.complexity.UserAccount.ProfileIcon(childComplexity), true
case "UserAccount.userID":
if e.complexity.UserAccount.UserID == nil {
break
}
return e.complexity.UserAccount.UserID(childComplexity), true
case "UserAccount.username": case "UserAccount.username":
if e.complexity.UserAccount.Username == nil { if e.complexity.UserAccount.Username == nil {
break break
@ -995,14 +1013,14 @@ var sources = []*ast.Source{
scalar UUID scalar UUID
type ProjectLabel { type ProjectLabel {
projectLabelID: ID! id: ID!
createdDate: Time! createdDate: Time!
colorHex: String! colorHex: String!
name: String name: String
} }
type TaskLabel { type TaskLabel {
taskLabelID: ID! id: ID!
projectLabelID: UUID! projectLabelID: UUID!
assignedDate: Time! assignedDate: Time!
colorHex: String! colorHex: String!
@ -1016,21 +1034,21 @@ type ProfileIcon {
} }
type ProjectMember { type ProjectMember {
userID: ID! id: ID!
firstName: String! firstName: String!
lastName: String! lastName: String!
profileIcon: ProfileIcon! profileIcon: ProfileIcon!
} }
type RefreshToken { type RefreshToken {
tokenId: ID! id: ID!
userId: UUID! userId: UUID!
expiresAt: Time! expiresAt: Time!
createdAt: Time! createdAt: Time!
} }
type UserAccount { type UserAccount {
userID: ID! id: ID!
email: String! email: String!
createdAt: Time! createdAt: Time!
firstName: String! firstName: String!
@ -1040,13 +1058,13 @@ type UserAccount {
} }
type Team { type Team {
teamID: ID! id: ID!
createdAt: Time! createdAt: Time!
name: String! name: String!
} }
type Project { type Project {
projectID: ID! id: ID!
createdAt: Time! createdAt: Time!
name: String! name: String!
team: Team! team: Team!
@ -1057,7 +1075,7 @@ type Project {
} }
type TaskGroup { type TaskGroup {
taskGroupID: ID! id: ID!
projectID: String! projectID: String!
createdAt: Time! createdAt: Time!
name: String! name: String!
@ -1066,7 +1084,7 @@ type TaskGroup {
} }
type Task { type Task {
taskID: ID! id: ID!
taskGroup: TaskGroup! taskGroup: TaskGroup!
createdAt: Time! createdAt: Time!
name: String! name: String!
@ -2563,7 +2581,7 @@ func (ec *executionContext) _ProfileIcon_bgColor(ctx context.Context, field grap
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _Project_projectID(ctx context.Context, field graphql.CollectedField, obj *pg.Project) (ret graphql.Marshaler) { func (ec *executionContext) _Project_id(ctx context.Context, field graphql.CollectedField, obj *pg.Project) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -2574,13 +2592,13 @@ func (ec *executionContext) _Project_projectID(ctx context.Context, field graphq
Object: "Project", Object: "Project",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.ProjectID, nil return ec.resolvers.Project().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -2835,7 +2853,7 @@ func (ec *executionContext) _Project_labels(ctx context.Context, field graphql.C
return ec.marshalNProjectLabel2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐProjectLabelᚄ(ctx, field.Selections, res) return ec.marshalNProjectLabel2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐProjectLabelᚄ(ctx, field.Selections, res)
} }
func (ec *executionContext) _ProjectLabel_projectLabelID(ctx context.Context, field graphql.CollectedField, obj *pg.ProjectLabel) (ret graphql.Marshaler) { func (ec *executionContext) _ProjectLabel_id(ctx context.Context, field graphql.CollectedField, obj *pg.ProjectLabel) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -2846,13 +2864,13 @@ func (ec *executionContext) _ProjectLabel_projectLabelID(ctx context.Context, fi
Object: "ProjectLabel", Object: "ProjectLabel",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.ProjectLabelID, nil return ec.resolvers.ProjectLabel().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -2968,7 +2986,7 @@ func (ec *executionContext) _ProjectLabel_name(ctx context.Context, field graphq
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _ProjectMember_userID(ctx context.Context, field graphql.CollectedField, obj *ProjectMember) (ret graphql.Marshaler) { func (ec *executionContext) _ProjectMember_id(ctx context.Context, field graphql.CollectedField, obj *ProjectMember) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -2985,7 +3003,7 @@ func (ec *executionContext) _ProjectMember_userID(ctx context.Context, field gra
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.UserID, nil return obj.ID, nil
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -3439,7 +3457,7 @@ func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.C
return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res)
} }
func (ec *executionContext) _RefreshToken_tokenId(ctx context.Context, field graphql.CollectedField, obj *pg.RefreshToken) (ret graphql.Marshaler) { func (ec *executionContext) _RefreshToken_id(ctx context.Context, field graphql.CollectedField, obj *pg.RefreshToken) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -3450,13 +3468,13 @@ func (ec *executionContext) _RefreshToken_tokenId(ctx context.Context, field gra
Object: "RefreshToken", Object: "RefreshToken",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.TokenID, nil return ec.resolvers.RefreshToken().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -3575,7 +3593,7 @@ func (ec *executionContext) _RefreshToken_createdAt(ctx context.Context, field g
return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res)
} }
func (ec *executionContext) _Task_taskID(ctx context.Context, field graphql.CollectedField, obj *pg.Task) (ret graphql.Marshaler) { func (ec *executionContext) _Task_id(ctx context.Context, field graphql.CollectedField, obj *pg.Task) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -3586,13 +3604,13 @@ func (ec *executionContext) _Task_taskID(ctx context.Context, field graphql.Coll
Object: "Task", Object: "Task",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.TaskID, nil return ec.resolvers.Task().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -3844,7 +3862,7 @@ func (ec *executionContext) _Task_labels(ctx context.Context, field graphql.Coll
return ec.marshalNTaskLabel2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskLabelᚄ(ctx, field.Selections, res) return ec.marshalNTaskLabel2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskLabelᚄ(ctx, field.Selections, res)
} }
func (ec *executionContext) _TaskGroup_taskGroupID(ctx context.Context, field graphql.CollectedField, obj *pg.TaskGroup) (ret graphql.Marshaler) { func (ec *executionContext) _TaskGroup_id(ctx context.Context, field graphql.CollectedField, obj *pg.TaskGroup) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -3855,13 +3873,13 @@ func (ec *executionContext) _TaskGroup_taskGroupID(ctx context.Context, field gr
Object: "TaskGroup", Object: "TaskGroup",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.TaskGroupID, nil return ec.resolvers.TaskGroup().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -4048,7 +4066,7 @@ func (ec *executionContext) _TaskGroup_tasks(ctx context.Context, field graphql.
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) _TaskLabel_taskLabelID(ctx context.Context, field graphql.CollectedField, obj *pg.TaskLabel) (ret graphql.Marshaler) { func (ec *executionContext) _TaskLabel_id(ctx context.Context, field graphql.CollectedField, obj *pg.TaskLabel) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -4059,13 +4077,13 @@ func (ec *executionContext) _TaskLabel_taskLabelID(ctx context.Context, field gr
Object: "TaskLabel", Object: "TaskLabel",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.TaskLabelID, nil return ec.resolvers.TaskLabel().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -4215,7 +4233,7 @@ func (ec *executionContext) _TaskLabel_name(ctx context.Context, field graphql.C
return ec.marshalOString2ᚖstring(ctx, field.Selections, res) return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
} }
func (ec *executionContext) _Team_teamID(ctx context.Context, field graphql.CollectedField, obj *pg.Team) (ret graphql.Marshaler) { func (ec *executionContext) _Team_id(ctx context.Context, field graphql.CollectedField, obj *pg.Team) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -4226,13 +4244,13 @@ func (ec *executionContext) _Team_teamID(ctx context.Context, field graphql.Coll
Object: "Team", Object: "Team",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.TeamID, nil return ec.resolvers.Team().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -4317,7 +4335,7 @@ func (ec *executionContext) _Team_name(ctx context.Context, field graphql.Collec
return ec.marshalNString2string(ctx, field.Selections, res) return ec.marshalNString2string(ctx, field.Selections, res)
} }
func (ec *executionContext) _UserAccount_userID(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 {
ec.Error(ctx, ec.Recover(ctx, r)) ec.Error(ctx, ec.Recover(ctx, r))
@ -4328,13 +4346,13 @@ func (ec *executionContext) _UserAccount_userID(ctx context.Context, field graph
Object: "UserAccount", Object: "UserAccount",
Field: field, Field: field,
Args: nil, Args: nil,
IsMethod: false, IsMethod: true,
} }
ctx = graphql.WithFieldContext(ctx, fc) ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children ctx = rctx // use context from middleware stack in children
return obj.UserID, nil return ec.resolvers.UserAccount().ID(rctx, obj)
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -6365,11 +6383,20 @@ func (ec *executionContext) _Project(ctx context.Context, sel ast.SelectionSet,
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("Project") out.Values[i] = graphql.MarshalString("Project")
case "projectID": case "id":
out.Values[i] = ec._Project_projectID(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
atomic.AddUint32(&invalids, 1) defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Project_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "createdAt": case "createdAt":
out.Values[i] = ec._Project_createdAt(ctx, field, obj) out.Values[i] = ec._Project_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -6472,11 +6499,20 @@ func (ec *executionContext) _ProjectLabel(ctx context.Context, sel ast.Selection
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("ProjectLabel") out.Values[i] = graphql.MarshalString("ProjectLabel")
case "projectLabelID": case "id":
out.Values[i] = ec._ProjectLabel_projectLabelID(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
atomic.AddUint32(&invalids, 1) defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._ProjectLabel_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "createdDate": case "createdDate":
out.Values[i] = ec._ProjectLabel_createdDate(ctx, field, obj) out.Values[i] = ec._ProjectLabel_createdDate(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -6529,8 +6565,8 @@ func (ec *executionContext) _ProjectMember(ctx context.Context, sel ast.Selectio
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("ProjectMember") out.Values[i] = graphql.MarshalString("ProjectMember")
case "userID": case "id":
out.Values[i] = ec._ProjectMember_userID(ctx, field, obj) out.Values[i] = ec._ProjectMember_id(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
@ -6699,25 +6735,34 @@ func (ec *executionContext) _RefreshToken(ctx context.Context, sel ast.Selection
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("RefreshToken") out.Values[i] = graphql.MarshalString("RefreshToken")
case "tokenId": case "id":
out.Values[i] = ec._RefreshToken_tokenId(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
invalids++ defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._RefreshToken_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "userId": case "userId":
out.Values[i] = ec._RefreshToken_userId(ctx, field, obj) out.Values[i] = ec._RefreshToken_userId(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ atomic.AddUint32(&invalids, 1)
} }
case "expiresAt": case "expiresAt":
out.Values[i] = ec._RefreshToken_expiresAt(ctx, field, obj) out.Values[i] = ec._RefreshToken_expiresAt(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ atomic.AddUint32(&invalids, 1)
} }
case "createdAt": case "createdAt":
out.Values[i] = ec._RefreshToken_createdAt(ctx, field, obj) out.Values[i] = ec._RefreshToken_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ atomic.AddUint32(&invalids, 1)
} }
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
@ -6741,11 +6786,20 @@ func (ec *executionContext) _Task(ctx context.Context, sel ast.SelectionSet, obj
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("Task") out.Values[i] = graphql.MarshalString("Task")
case "taskID": case "id":
out.Values[i] = ec._Task_taskID(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
atomic.AddUint32(&invalids, 1) defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Task_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "taskGroup": case "taskGroup":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
@ -6836,11 +6890,20 @@ func (ec *executionContext) _TaskGroup(ctx context.Context, sel ast.SelectionSet
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("TaskGroup") out.Values[i] = graphql.MarshalString("TaskGroup")
case "taskGroupID": case "id":
out.Values[i] = ec._TaskGroup_taskGroupID(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
atomic.AddUint32(&invalids, 1) defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TaskGroup_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "projectID": case "projectID":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
@ -6906,11 +6969,20 @@ func (ec *executionContext) _TaskLabel(ctx context.Context, sel ast.SelectionSet
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("TaskLabel") out.Values[i] = graphql.MarshalString("TaskLabel")
case "taskLabelID": case "id":
out.Values[i] = ec._TaskLabel_taskLabelID(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
atomic.AddUint32(&invalids, 1) defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TaskLabel_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "projectLabelID": case "projectLabelID":
out.Values[i] = ec._TaskLabel_projectLabelID(ctx, field, obj) out.Values[i] = ec._TaskLabel_projectLabelID(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -6968,20 +7040,29 @@ func (ec *executionContext) _Team(ctx context.Context, sel ast.SelectionSet, obj
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("Team") out.Values[i] = graphql.MarshalString("Team")
case "teamID": case "id":
out.Values[i] = ec._Team_teamID(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
invalids++ defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Team_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "createdAt": case "createdAt":
out.Values[i] = ec._Team_createdAt(ctx, field, obj) out.Values[i] = ec._Team_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ atomic.AddUint32(&invalids, 1)
} }
case "name": case "name":
out.Values[i] = ec._Team_name(ctx, field, obj) out.Values[i] = ec._Team_name(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ atomic.AddUint32(&invalids, 1)
} }
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
@ -7005,11 +7086,20 @@ func (ec *executionContext) _UserAccount(ctx context.Context, sel ast.SelectionS
switch field.Name { switch field.Name {
case "__typename": case "__typename":
out.Values[i] = graphql.MarshalString("UserAccount") out.Values[i] = graphql.MarshalString("UserAccount")
case "userID": case "id":
out.Values[i] = ec._UserAccount_userID(ctx, field, obj) field := field
if out.Values[i] == graphql.Null { out.Concurrently(i, func() (res graphql.Marshaler) {
atomic.AddUint32(&invalids, 1) defer func() {
} if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._UserAccount_id(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "email": case "email":
out.Values[i] = ec._UserAccount_email(ctx, field, obj) out.Values[i] = ec._UserAccount_email(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {

View File

@ -110,7 +110,7 @@ type ProfileIcon struct {
} }
type ProjectMember struct { type ProjectMember struct {
UserID uuid.UUID `json:"userID"` ID uuid.UUID `json:"id"`
FirstName string `json:"firstName"` FirstName string `json:"firstName"`
LastName string `json:"lastName"` LastName string `json:"lastName"`
ProfileIcon *ProfileIcon `json:"profileIcon"` ProfileIcon *ProfileIcon `json:"profileIcon"`

View File

@ -2,14 +2,14 @@ scalar Time
scalar UUID scalar UUID
type ProjectLabel { type ProjectLabel {
projectLabelID: ID! id: ID!
createdDate: Time! createdDate: Time!
colorHex: String! colorHex: String!
name: String name: String
} }
type TaskLabel { type TaskLabel {
taskLabelID: ID! id: ID!
projectLabelID: UUID! projectLabelID: UUID!
assignedDate: Time! assignedDate: Time!
colorHex: String! colorHex: String!
@ -23,21 +23,21 @@ type ProfileIcon {
} }
type ProjectMember { type ProjectMember {
userID: ID! id: ID!
firstName: String! firstName: String!
lastName: String! lastName: String!
profileIcon: ProfileIcon! profileIcon: ProfileIcon!
} }
type RefreshToken { type RefreshToken {
tokenId: ID! id: ID!
userId: UUID! userId: UUID!
expiresAt: Time! expiresAt: Time!
createdAt: Time! createdAt: Time!
} }
type UserAccount { type UserAccount {
userID: ID! id: ID!
email: String! email: String!
createdAt: Time! createdAt: Time!
firstName: String! firstName: String!
@ -47,13 +47,13 @@ type UserAccount {
} }
type Team { type Team {
teamID: ID! id: ID!
createdAt: Time! createdAt: Time!
name: String! name: String!
} }
type Project { type Project {
projectID: ID! id: ID!
createdAt: Time! createdAt: Time!
name: String! name: String!
team: Team! team: Team!
@ -64,7 +64,7 @@ type Project {
} }
type TaskGroup { type TaskGroup {
taskGroupID: ID! id: ID!
projectID: String! projectID: String!
createdAt: Time! createdAt: Time!
name: String! name: String!
@ -73,7 +73,7 @@ type TaskGroup {
} }
type Task { type Task {
taskID: ID! id: ID!
taskGroup: TaskGroup! taskGroup: TaskGroup!
createdAt: Time! createdAt: Time!
name: String! name: String!

View File

@ -190,6 +190,10 @@ func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bo
return true, err return true, err
} }
func (r *projectResolver) ID(ctx context.Context, obj *pg.Project) (uuid.UUID, error) {
return obj.ProjectID, nil
}
func (r *projectResolver) Team(ctx context.Context, obj *pg.Project) (*pg.Team, error) { func (r *projectResolver) Team(ctx context.Context, obj *pg.Project) (*pg.Team, error) {
team, err := r.Repository.GetTeamByID(ctx, obj.TeamID) team, err := r.Repository.GetTeamByID(ctx, obj.TeamID)
return &team, err return &team, err
@ -226,6 +230,10 @@ func (r *projectResolver) Labels(ctx context.Context, obj *pg.Project) ([]pg.Pro
return labels, err return labels, err
} }
func (r *projectLabelResolver) ID(ctx context.Context, obj *pg.ProjectLabel) (uuid.UUID, error) {
return obj.ProjectLabelID, nil
}
func (r *projectLabelResolver) ColorHex(ctx context.Context, obj *pg.ProjectLabel) (string, error) { func (r *projectLabelResolver) ColorHex(ctx context.Context, obj *pg.ProjectLabel) (string, error) {
labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID) labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID)
if err != nil { if err != nil {
@ -235,7 +243,11 @@ func (r *projectLabelResolver) ColorHex(ctx context.Context, obj *pg.ProjectLabe
} }
func (r *projectLabelResolver) Name(ctx context.Context, obj *pg.ProjectLabel) (*string, error) { func (r *projectLabelResolver) Name(ctx context.Context, obj *pg.ProjectLabel) (*string, error) {
panic(fmt.Errorf("not implemented")) var name *string
if obj.Name.Valid {
name = &obj.Name.String
}
return name, nil
} }
func (r *queryResolver) Users(ctx context.Context) ([]pg.UserAccount, error) { func (r *queryResolver) Users(ctx context.Context) ([]pg.UserAccount, error) {
@ -311,6 +323,14 @@ func (r *queryResolver) Me(ctx context.Context) (*pg.UserAccount, error) {
return &user, err return &user, err
} }
func (r *refreshTokenResolver) ID(ctx context.Context, obj *pg.RefreshToken) (uuid.UUID, error) {
return obj.TokenID, nil
}
func (r *taskResolver) ID(ctx context.Context, obj *pg.Task) (uuid.UUID, error) {
return obj.TaskID, nil
}
func (r *taskResolver) TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error) { func (r *taskResolver) TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error) {
taskGroup, err := r.Repository.GetTaskGroupByID(ctx, obj.TaskGroupID) taskGroup, err := r.Repository.GetTaskGroupByID(ctx, obj.TaskGroupID)
return &taskGroup, err return &taskGroup, err
@ -349,6 +369,10 @@ func (r *taskResolver) Labels(ctx context.Context, obj *pg.Task) ([]pg.TaskLabel
return r.Repository.GetTaskLabelsForTaskID(ctx, obj.TaskID) return r.Repository.GetTaskLabelsForTaskID(ctx, obj.TaskID)
} }
func (r *taskGroupResolver) ID(ctx context.Context, obj *pg.TaskGroup) (uuid.UUID, error) {
return obj.TaskGroupID, nil
}
func (r *taskGroupResolver) ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error) { func (r *taskGroupResolver) ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error) {
return obj.ProjectID.String(), nil return obj.ProjectID.String(), nil
} }
@ -358,6 +382,10 @@ func (r *taskGroupResolver) Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.
return tasks, err return tasks, err
} }
func (r *taskLabelResolver) ID(ctx context.Context, obj *pg.TaskLabel) (uuid.UUID, error) {
return obj.TaskLabelID, nil
}
func (r *taskLabelResolver) ColorHex(ctx context.Context, obj *pg.TaskLabel) (string, error) { func (r *taskLabelResolver) ColorHex(ctx context.Context, obj *pg.TaskLabel) (string, error) {
projectLabel, err := r.Repository.GetProjectLabelByID(ctx, obj.ProjectLabelID) projectLabel, err := r.Repository.GetProjectLabelByID(ctx, obj.ProjectLabelID)
if err != nil { if err != nil {
@ -382,6 +410,14 @@ func (r *taskLabelResolver) Name(ctx context.Context, obj *pg.TaskLabel) (*strin
return &name.String, err return &name.String, err
} }
func (r *teamResolver) ID(ctx context.Context, obj *pg.Team) (uuid.UUID, error) {
return obj.TeamID, nil
}
func (r *userAccountResolver) ID(ctx context.Context, obj *pg.UserAccount) (uuid.UUID, error) {
return obj.UserID, nil
}
func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *pg.UserAccount) (*ProfileIcon, error) { func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *pg.UserAccount) (*ProfileIcon, error) {
initials := string([]rune(obj.FirstName)[0]) + string([]rune(obj.LastName)[0]) initials := string([]rune(obj.FirstName)[0]) + string([]rune(obj.LastName)[0])
profileIcon := &ProfileIcon{nil, &initials, &obj.ProfileBgColor} profileIcon := &ProfileIcon{nil, &initials, &obj.ProfileBgColor}
@ -400,6 +436,9 @@ func (r *Resolver) ProjectLabel() ProjectLabelResolver { return &projectLabelRes
// Query returns QueryResolver implementation. // Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
// RefreshToken returns RefreshTokenResolver implementation.
func (r *Resolver) RefreshToken() RefreshTokenResolver { return &refreshTokenResolver{r} }
// Task returns TaskResolver implementation. // Task returns TaskResolver implementation.
func (r *Resolver) Task() TaskResolver { return &taskResolver{r} } func (r *Resolver) Task() TaskResolver { return &taskResolver{r} }
@ -409,6 +448,9 @@ func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r}
// TaskLabel returns TaskLabelResolver implementation. // TaskLabel returns TaskLabelResolver implementation.
func (r *Resolver) TaskLabel() TaskLabelResolver { return &taskLabelResolver{r} } func (r *Resolver) TaskLabel() TaskLabelResolver { return &taskLabelResolver{r} }
// Team returns TeamResolver implementation.
func (r *Resolver) Team() TeamResolver { return &teamResolver{r} }
// UserAccount returns UserAccountResolver implementation. // UserAccount returns UserAccountResolver implementation.
func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolver{r} } func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolver{r} }
@ -416,20 +458,9 @@ type mutationResolver struct{ *Resolver }
type projectResolver struct{ *Resolver } type projectResolver struct{ *Resolver }
type projectLabelResolver struct{ *Resolver } type projectLabelResolver struct{ *Resolver }
type queryResolver struct{ *Resolver } type queryResolver struct{ *Resolver }
type refreshTokenResolver struct{ *Resolver }
type taskResolver struct{ *Resolver } type taskResolver struct{ *Resolver }
type taskGroupResolver struct{ *Resolver } type taskGroupResolver struct{ *Resolver }
type taskLabelResolver struct{ *Resolver } type taskLabelResolver struct{ *Resolver }
type teamResolver struct{ *Resolver }
type userAccountResolver struct{ *Resolver } type userAccountResolver struct{ *Resolver }
// !!! WARNING !!!
// The code below was going to be deleted when updating resolvers. It has been copied here so you have
// one last chance to move it out of harms way if you want. There are two reasons this happens:
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
// it when you're done.
// - You have helper methods in this file. Move them out to keep these resolver files clean.
func (r *taskLabelResolver) ProjectLabelID(ctx context.Context, obj *pg.TaskLabel) (uuid.UUID, error) {
panic(fmt.Errorf("not implemented"))
}
func (r *userAccountResolver) DisplayName(ctx context.Context, obj *pg.UserAccount) (string, error) {
return obj.FirstName + " " + obj.LastName, nil
}

View File

@ -0,0 +1,7 @@
ALTER TABLE task_assigned
DROP CONSTRAINT task_assigned_task_id_fkey,
ADD CONSTRAINT task_assigned_task_id_fkey
FOREIGN KEY (task_id)
REFERENCES task(task_id)
ON DELETE CASCADE;

69
assets/favicon.svg Normal file
View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64"
height="64"
viewBox="0 0 12.7 12.7"
version="1.1"
id="svg8"
sodipodi:docname="favicon.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1908"
inkscape:window-height="983"
id="namedview10"
showgrid="false"
inkscape:zoom="7.375"
inkscape:cx="5.2026609"
inkscape:cy="37.687216"
inkscape:window-x="6"
inkscape:window-y="6"
inkscape:window-maximized="0"
inkscape:current-layer="g6" />
<g
transform="translate(-.26 -24.137) scale(.1249)"
id="g6"
style="stroke-width:17.47648118;stroke-miterlimit:4;stroke-dasharray:none">
<path
d="M50.886 286.515l-40.4-44.46 44.459-40.401 40.401 44.46z"
fill="none"
stroke="#000"
strokeWidth="11.90597031"
id="path2"
style="stroke-width:7.94385508;stroke-miterlimit:4;stroke-dasharray:none;stroke:#7367f0;stroke-opacity:1" />
<circle
cx="52.917"
cy="244.083"
r="11.025"
fill="#000"
id="circle4"
style="stroke-width:17.47648118;stroke-miterlimit:4;stroke-dasharray:none;fill:#7367f0;fill-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 295 KiB

View File

@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL. work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`. Learn how to configure a non-root public URL by running `npm run build`.
--> -->
<title>React App</title> <title>Citadel</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -5,7 +5,10 @@ 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';
const GlobalTopNavbar: React.FC = () => { type GlobalTopNavbarProps = {
name: string;
};
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ name }) => {
const { loading, data } = useMeQuery(); const { loading, data } = useMeQuery();
const history = useHistory(); const history = useHistory();
const { userID, setUserID } = useContext(UserIDContext); const { userID, setUserID } = useContext(UserIDContext);
@ -41,6 +44,7 @@ const GlobalTopNavbar: React.FC = () => {
return ( return (
<> <>
<TopNavbar <TopNavbar
projectName={name}
bgColor={data ? data.me.profileIcon.bgColor ?? '#7367F0' : '#7367F0'} bgColor={data ? data.me.profileIcon.bgColor ?? '#7367F0' : '#7367F0'}
firstName={data ? data.me.firstName : ''} firstName={data ? data.me.firstName : ''}
lastName={data ? data.me.lastName : ''} lastName={data ? data.me.lastName : ''}

View File

@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
import jwtDecode from 'jwt-decode'; import jwtDecode from 'jwt-decode';
import { createBrowserHistory } from 'history'; import { createBrowserHistory } from 'history';
import { setAccessToken } from 'shared/utils/accessToken'; import { setAccessToken } from 'shared/utils/accessToken';
import GlobalTopNavbar from 'App/TopNavbar';
import styled from 'styled-components'; import styled from 'styled-components';
import NormalizeStyles from './NormalizeStyles'; import NormalizeStyles from './NormalizeStyles';
import BaseStyles from './BaseStyles'; import BaseStyles from './BaseStyles';
@ -10,6 +9,7 @@ import Routes from './Routes';
import { UserIDContext } from './context'; import { UserIDContext } from './context';
import Navbar from './Navbar'; import Navbar from './Navbar';
import { Router } from 'react-router'; import { Router } from 'react-router';
import { PopupProvider } from 'shared/components/PopupMenu';
const history = createBrowserHistory(); const history = createBrowserHistory();
@ -45,21 +45,22 @@ const App = () => {
return ( return (
<> <>
<UserIDContext.Provider value={{ userID, setUserID }}> <UserIDContext.Provider value={{ userID, setUserID }}>
<NormalizeStyles /> <PopupProvider>
<BaseStyles /> <NormalizeStyles />
<Router history={history}> <BaseStyles />
{loading ? ( <Router history={history}>
<div>loading</div> {loading ? (
) : ( <div>loading</div>
<> ) : (
<Navbar /> <>
<MainContent> <Navbar />
<GlobalTopNavbar /> <MainContent>
<Routes history={history} /> <Routes history={history} />
</MainContent> </MainContent>
</> </>
)} )}
</Router> </Router>
</PopupProvider>
</UserIDContext.Provider> </UserIDContext.Provider>
</> </>
); );

View File

@ -1,11 +1,12 @@
import React, { useState, useContext } from 'react'; import React, { useState, useContext } from 'react';
import Modal from 'shared/components/Modal'; import Modal from 'shared/components/Modal';
import TaskDetails from 'shared/components/TaskDetails'; import TaskDetails from 'shared/components/TaskDetails';
import PopupMenu from 'shared/components/PopupMenu'; import PopupMenu, { Popup, usePopup } from 'shared/components/PopupMenu';
import MemberManager from 'shared/components/MemberManager'; import MemberManager from 'shared/components/MemberManager';
import { useRouteMatch, useHistory } from 'react-router'; import { useRouteMatch, useHistory } from 'react-router';
import { useFindTaskQuery, useAssignTaskMutation, useUnassignTaskMutation } from 'shared/generated/graphql'; import { useFindTaskQuery, useAssignTaskMutation, useUnassignTaskMutation } from 'shared/generated/graphql';
import UserIDContext from 'App/context'; import UserIDContext from 'App/context';
import MiniProfile from 'shared/components/MiniProfile';
type DetailsProps = { type DetailsProps = {
taskID: string; taskID: string;
@ -13,7 +14,7 @@ type DetailsProps = {
onTaskNameChange: (task: Task, newName: string) => void; onTaskNameChange: (task: Task, newName: string) => void;
onTaskDescriptionChange: (task: Task, newDescription: string) => void; onTaskDescriptionChange: (task: Task, newDescription: string) => void;
onDeleteTask: (task: Task) => void; onDeleteTask: (task: Task) => void;
onOpenAddLabelPopup: (task: Task, bounds: ElementBounds) => void; onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
availableMembers: Array<TaskUser>; availableMembers: Array<TaskUser>;
refreshCache: () => void; refreshCache: () => void;
}; };
@ -31,8 +32,10 @@ const Details: React.FC<DetailsProps> = ({
refreshCache, refreshCache,
}) => { }) => {
const { userID } = useContext(UserIDContext); const { userID } = useContext(UserIDContext);
const { showPopup } = usePopup();
const history = useHistory(); const history = useHistory();
const match = useRouteMatch(); const match = useRouteMatch();
const [currentMemberTask, setCurrentMemberTask] = useState('');
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState); const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } }); const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
const [assignTask] = useAssignTaskMutation({ const [assignTask] = useAssignTaskMutation({
@ -55,7 +58,7 @@ const Details: React.FC<DetailsProps> = ({
} }
const taskMembers = data.findTask.assigned.map(assigned => { const taskMembers = data.findTask.assigned.map(assigned => {
return { return {
userID: assigned.userID, userID: assigned.id,
displayName: `${assigned.firstName} ${assigned.lastName}`, displayName: `${assigned.firstName} ${assigned.lastName}`,
profileIcon: { profileIcon: {
url: null, url: null,
@ -76,6 +79,8 @@ const Details: React.FC<DetailsProps> = ({
<TaskDetails <TaskDetails
task={{ task={{
...data.findTask, ...data.findTask,
taskID: data.findTask.id,
taskGroup: { taskGroupID: data.findTask.taskGroup.id },
members: taskMembers, members: taskMembers,
description: data.findTask.description ?? '', description: data.findTask.description ?? '',
labels: [], labels: [],
@ -84,42 +89,48 @@ const Details: React.FC<DetailsProps> = ({
onTaskDescriptionChange={onTaskDescriptionChange} onTaskDescriptionChange={onTaskDescriptionChange}
onDeleteTask={onDeleteTask} onDeleteTask={onDeleteTask}
onCloseModal={() => history.push(projectURL)} onCloseModal={() => history.push(projectURL)}
onOpenAddMemberPopup={(task, bounds) => { onMemberProfile={($targetRef, memberID) => {
console.log(task, bounds); showPopup(
setMemberPopupData({ $targetRef,
isOpen: true, <Popup title={null} onClose={() => {}} tab={0}>
taskID: task.taskID, <MiniProfile
top: bounds.position.top + bounds.size.height + 10, profileIcon={taskMembers[0].profileIcon}
left: bounds.position.left, displayName="Jordan Knott"
}); username="@jordanthedev"
bio="None"
onRemoveFromTask={() => {
unassignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
}}
/>
</Popup>,
);
}}
onOpenAddMemberPopup={(task, $targetRef) => {
console.log(`task: ${task.taskID}`);
showPopup(
$targetRef,
<Popup title="Members" tab={0} onClose={() => {}}>
<MemberManager
availableMembers={availableMembers}
activeMembers={taskMembers}
onMemberChange={(member, isActive) => {
console.log(`is active ${member.userID} - ${isActive}`);
if (isActive) {
assignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
} else {
unassignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
}
console.log(member, isActive);
}}
/>
</Popup>,
);
}} }}
onOpenAddLabelPopup={onOpenAddLabelPopup} onOpenAddLabelPopup={onOpenAddLabelPopup}
/> />
); );
}} }}
/> />
{memberPopupData.isOpen && (
<PopupMenu
title="Members"
top={memberPopupData.top}
onClose={() => setMemberPopupData(initialMemberPopupState)}
left={memberPopupData.left}
>
<MemberManager
availableMembers={availableMembers}
activeMembers={taskMembers}
onMemberChange={(member, isActive) => {
console.log(`is active ${member.userID} - ${isActive}`);
if (isActive) {
assignTask({ variables: { taskID: data.findTask.taskID, userID: userID ?? '' } });
} else {
unassignTask({ variables: { taskID: data.findTask.taskID, userID: userID ?? '' } });
}
console.log(member, isActive);
}}
/>
</PopupMenu>
)}
</> </>
); );
}; };

View File

@ -6,7 +6,7 @@ import { Board } from './Styles';
type KanbanBoardProps = { type KanbanBoardProps = {
listsData: BoardState; listsData: BoardState;
onOpenListActionsPopup: (isOpen: boolean, left: number, top: number, taskGroupID: string) => void; onOpenListActionsPopup: ($targetRef: React.RefObject<HTMLElement>, taskGroupID: string) => void;
onCardDrop: (task: Task) => void; onCardDrop: (task: Task) => void;
onListDrop: (taskGroup: TaskGroup) => void; onListDrop: (taskGroup: TaskGroup) => void;
onCardCreate: (taskGroupID: string, name: string) => void; onCardCreate: (taskGroupID: string, name: string) => void;
@ -31,8 +31,8 @@ const KanbanBoard: React.FC<KanbanBoardProps> = ({
onCardClick={task => { onCardClick={task => {
history.push(`${match.url}/c/${task.taskID}`); history.push(`${match.url}/c/${task.taskID}`);
}} }}
onExtraMenuOpen={(taskGroupID, pos, size) => { onExtraMenuOpen={(taskGroupID, $targetRef) => {
onOpenListActionsPopup(true, pos.left, pos.top + size.height + 5, taskGroupID); onOpenListActionsPopup($targetRef, taskGroupID);
}} }}
onQuickEditorOpen={onQuickEditorOpen} onQuickEditorOpen={onQuickEditorOpen}
onCardCreate={onCardCreate} onCardCreate={onCardCreate}

View File

@ -1,6 +1,9 @@
import React, { useState } from 'react'; import React, { useState, useRef } from 'react';
import * as BoardStateUtils from 'shared/utils/boardState'; import * as BoardStateUtils from 'shared/utils/boardState';
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 { usePopup, Popup } from 'shared/components/PopupMenu';
import { useParams, Route, useRouteMatch, useHistory, RouteComponentProps } from 'react-router-dom'; import { useParams, Route, useRouteMatch, useHistory, RouteComponentProps } from 'react-router-dom';
import { import {
useFindProjectQuery, useFindProjectQuery,
@ -13,14 +16,19 @@ import {
useDeleteTaskGroupMutation, useDeleteTaskGroupMutation,
useUpdateTaskDescriptionMutation, useUpdateTaskDescriptionMutation,
useAssignTaskMutation, useAssignTaskMutation,
DeleteTaskDocument,
FindProjectDocument,
} from 'shared/generated/graphql'; } from 'shared/generated/graphql';
import QuickCardEditor from 'shared/components/QuickCardEditor'; import QuickCardEditor from 'shared/components/QuickCardEditor';
import PopupMenu from 'shared/components/PopupMenu';
import ListActions from 'shared/components/ListActions'; import ListActions from 'shared/components/ListActions';
import MemberManager from 'shared/components/MemberManager'; import MemberManager from 'shared/components/MemberManager';
import { LabelsPopup } from 'shared/components/PopupMenu/PopupMenu.stories'; import { LabelsPopup } from 'shared/components/PopupMenu/PopupMenu.stories';
import KanbanBoard from 'Projects/Project/KanbanBoard'; import KanbanBoard from 'Projects/Project/KanbanBoard';
import { mixin } from 'shared/utils/styles';
import LabelManager from 'shared/components/PopupMenu/LabelManager';
import LabelEditor from 'shared/components/PopupMenu/LabelEditor';
import produce from 'immer';
import Details from './Details'; import Details from './Details';
type TaskRouteProps = { type TaskRouteProps = {
@ -45,6 +53,67 @@ const Title = styled.span`
color: #fff; color: #fff;
`; `;
type LabelManagerEditorProps = {
labels: Array<Label>;
};
const LabelManagerEditor: React.FC<LabelManagerEditorProps> = ({ labels: initialLabels }) => {
const [labels, setLabels] = useState<Array<Label>>(initialLabels);
const [currentLabel, setCurrentLabel] = useState('');
const { setTab } = usePopup();
return (
<>
<Popup title="Labels" tab={0} onClose={() => {}}>
<LabelManager
labels={labels}
onLabelCreate={() => {
setTab(2);
}}
onLabelEdit={labelId => {
setCurrentLabel(labelId);
setTab(1);
}}
onLabelToggle={labelId => {
setLabels(
produce(labels, draftState => {
const idx = labels.findIndex(label => label.labelId === labelId);
if (idx !== -1) {
draftState[idx] = { ...draftState[idx], active: !labels[idx].active };
}
}),
);
}}
/>
</Popup>
<Popup onClose={() => {}} title="Edit label" tab={1}>
<LabelEditor
label={labels.find(label => label.labelId === currentLabel) ?? null}
onLabelEdit={(_labelId, name, color) => {
setLabels(
produce(labels, draftState => {
const idx = labels.findIndex(label => label.labelId === currentLabel);
if (idx !== -1) {
draftState[idx] = { ...draftState[idx], name, color };
}
}),
);
setTab(0);
}}
/>
</Popup>
<Popup onClose={() => {}} title="Create new label" tab={2}>
<LabelEditor
label={null}
onLabelEdit={(_labelId, name, color) => {
setLabels([...labels, { labelId: name, name, color, active: false }]);
setTab(0);
}}
/>
</Popup>
</>
);
};
interface ProjectParams { interface ProjectParams {
projectId: string; projectId: string;
} }
@ -55,6 +124,34 @@ const initialQuickCardEditorState: QuickCardEditorState = { isOpen: false, top:
const initialLabelsPopupState = { taskID: '', isOpen: false, top: 0, left: 0 }; const initialLabelsPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
const initialTaskDetailsState = { isOpen: false, taskID: '' }; const initialTaskDetailsState = { isOpen: false, taskID: '' };
const ProjectActions = styled.div`
display: flex;
align-items: center;
justify-content: flex-end;
height: 40px;
padding: 0 12px;
`;
const ProjectAction = styled.div`
cursor: pointer;
display: flex;
align-items: center;
font-size: 15px;
color: #c2c6dc;
&:not(:last-child) {
margin-right: 16px;
}
&:hover {
color: ${mixin.lighten('#c2c6dc', 0.25)};
}
`;
const ProjectActionText = styled.span`
padding-left: 4px;
`;
const Project = () => { const Project = () => {
const { projectId } = useParams<ProjectParams>(); const { projectId } = useParams<ProjectParams>();
const match = useRouteMatch(); const match = useRouteMatch();
@ -70,17 +167,16 @@ const Project = () => {
const [deleteTaskGroup] = useDeleteTaskGroupMutation({ const [deleteTaskGroup] = useDeleteTaskGroupMutation({
onCompleted: deletedTaskGroupData => { onCompleted: deletedTaskGroupData => {
setListsData( setListsData(BoardStateUtils.deleteTaskGroup(listsData, deletedTaskGroupData.deleteTaskGroup.taskGroup.id));
BoardStateUtils.deleteTaskGroup(listsData, deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID),
);
}, },
}); });
const [createTaskGroup] = useCreateTaskGroupMutation({ const [createTaskGroup] = useCreateTaskGroupMutation({
onCompleted: newTaskGroupData => { onCompleted: newTaskGroupData => {
const newTaskGroup = { const newTaskGroup = {
...newTaskGroupData.createTaskGroup, taskGroupID: newTaskGroupData.createTaskGroup.id,
tasks: [], tasks: [],
...newTaskGroupData.createTaskGroup,
}; };
setListsData(BoardStateUtils.addTaskGroup(listsData, newTaskGroup)); setListsData(BoardStateUtils.addTaskGroup(listsData, newTaskGroup));
}, },
@ -90,10 +186,43 @@ const Project = () => {
onCompleted: newTaskData => { onCompleted: newTaskData => {
const newTask = { const newTask = {
...newTaskData.createTask, ...newTaskData.createTask,
taskID: newTaskData.createTask.id,
taskGroup: { taskGroupID: newTaskData.createTask.taskGroup.id },
labels: [], labels: [],
}; };
setListsData(BoardStateUtils.addTask(listsData, newTask)); setListsData(BoardStateUtils.addTask(listsData, newTask));
}, },
update: (client, newTaskData) => {
const cacheData: any = client.readQuery({
query: FindProjectDocument,
variables: {
projectId: projectId,
},
});
console.log(cacheData);
console.log(newTaskData);
const newTaskGroups = produce(cacheData.findProject.taskGroups, (draftState: any) => {
const targetIndex = draftState.findIndex(
(taskGroup: any) => taskGroup.id === newTaskData.data.createTask.taskGroup.id,
);
draftState[targetIndex] = {
...draftState[targetIndex],
tasks: [...draftState[targetIndex].tasks, { ...newTaskData.data.createTask }],
};
});
console.log(newTaskGroups);
const newData = {
...cacheData.findProject,
taskGroups: newTaskGroups,
};
client.writeQuery({
query: FindProjectDocument,
variables: {
projectId: projectId,
},
data: { findProject: newData },
});
},
}); });
const [deleteTask] = useDeleteTaskMutation({ const [deleteTask] = useDeleteTaskMutation({
@ -105,50 +234,14 @@ const Project = () => {
const [updateTaskName] = useUpdateTaskNameMutation({ const [updateTaskName] = useUpdateTaskNameMutation({
onCompleted: newTaskData => { onCompleted: newTaskData => {
setListsData( setListsData(
BoardStateUtils.updateTaskName(listsData, newTaskData.updateTaskName.taskID, newTaskData.updateTaskName.name), BoardStateUtils.updateTaskName(listsData, newTaskData.updateTaskName.id, newTaskData.updateTaskName.name),
); );
}, },
}); });
const { loading, data, refetch } = useFindProjectQuery({ const { loading, data, refetch } = useFindProjectQuery({
variables: { projectId }, variables: { projectId },
onCompleted: newData => {
console.log('beep!');
const newListsData: BoardState = { tasks: {}, columns: {} };
newData.findProject.taskGroups.forEach(taskGroup => {
newListsData.columns[taskGroup.taskGroupID] = {
taskGroupID: taskGroup.taskGroupID,
name: taskGroup.name,
position: taskGroup.position,
tasks: [],
};
taskGroup.tasks.forEach(task => {
const taskMembers = task.assigned.map(assigned => {
return {
userID: assigned.userID,
displayName: `${assigned.firstName} ${assigned.lastName}`,
profileIcon: {
url: null,
initials: assigned.profileIcon.initials ?? '',
bgColor: assigned.profileIcon.bgColor ?? '#7367F0',
},
};
});
newListsData.tasks[task.taskID] = {
taskID: task.taskID,
taskGroup: {
taskGroupID: taskGroup.taskGroupID,
},
name: task.name,
labels: [],
position: task.position,
description: task.description ?? undefined,
members: taskMembers,
};
});
});
setListsData(newListsData);
},
}); });
console.log(`loading ${loading} - ${data}`);
const onCardCreate = (taskGroupID: string, name: string) => { const onCardCreate = (taskGroupID: string, name: string) => {
const taskGroupTasks = Object.values(listsData.tasks).filter( const taskGroupTasks = Object.values(listsData.tasks).filter(
@ -163,15 +256,6 @@ const Project = () => {
createTask({ variables: { taskGroupID, name, position } }); createTask({ variables: { taskGroupID, name, position } });
}; };
const onQuickEditorOpen = (e: ContextMenuEvent) => {
const currentTask = Object.values(listsData.tasks).find(task => task.taskID === e.taskID);
setQuickCardEditor({
top: e.top,
left: e.left,
isOpen: true,
task: currentTask,
});
};
const onCardDrop = (droppedTask: Task) => { const onCardDrop = (droppedTask: Task) => {
updateTaskLocation({ updateTaskLocation({
variables: { variables: {
@ -202,10 +286,50 @@ const Project = () => {
const [assignTask] = useAssignTaskMutation(); const [assignTask] = useAssignTaskMutation();
const { showPopup } = usePopup();
const $labelsRef = useRef<HTMLDivElement>(null);
if (loading) { if (loading) {
return <Title>Error Loading</Title>; return (
<>
<GlobalTopNavbar name="Project" />
<Title>Error Loading</Title>
</>
);
} }
if (data) { if (data) {
const currentListsData: BoardState = { tasks: {}, columns: {} };
data.findProject.taskGroups.forEach(taskGroup => {
currentListsData.columns[taskGroup.id] = {
taskGroupID: taskGroup.id,
name: taskGroup.name,
position: taskGroup.position,
tasks: [],
};
taskGroup.tasks.forEach(task => {
const taskMembers = task.assigned.map(assigned => {
return {
userID: assigned.id,
displayName: `${assigned.firstName} ${assigned.lastName}`,
profileIcon: {
url: null,
initials: assigned.profileIcon.initials ?? '',
bgColor: assigned.profileIcon.bgColor ?? '#7367F0',
},
};
});
currentListsData.tasks[task.id] = {
taskID: task.id,
taskGroup: {
taskGroupID: taskGroup.id,
},
name: task.name,
labels: [],
position: task.position,
description: task.description ?? undefined,
members: taskMembers,
};
});
});
const availableMembers = data.findProject.members.map(member => { const availableMembers = data.findProject.members.map(member => {
return { return {
displayName: `${member.firstName} ${member.lastName}`, displayName: `${member.firstName} ${member.lastName}`,
@ -214,38 +338,75 @@ const Project = () => {
initials: member.profileIcon.initials ?? null, initials: member.profileIcon.initials ?? null,
bgColor: member.profileIcon.bgColor ?? null, bgColor: member.profileIcon.bgColor ?? null,
}, },
userID: member.userID, userID: member.id,
}; };
}); });
const onQuickEditorOpen = (e: ContextMenuEvent) => {
const currentTask = Object.values(currentListsData.tasks).find(task => task.taskID === e.taskID);
console.log(`currentTask: ${currentTask?.taskID}`);
setQuickCardEditor({
top: e.top,
left: e.left,
isOpen: true,
task: currentTask,
});
};
return ( return (
<> <>
<GlobalTopNavbar name={data.findProject.name} />
<ProjectActions>
<ProjectAction
ref={$labelsRef}
onClick={() => {
showPopup(
$labelsRef,
<LabelManagerEditor
labels={data.findProject.labels.map(label => {
return {
labelId: label.id,
name: label.name ?? '',
color: label.colorHex,
active: false,
};
})}
/>,
);
}}
>
<Tags size={13} color="#c2c6dc" />
<ProjectActionText>Labels</ProjectActionText>
</ProjectAction>
<ProjectAction>
<ToggleOn size={13} color="#c2c6dc" />
<ProjectActionText>Fields</ProjectActionText>
</ProjectAction>
<ProjectAction>
<Bolt size={13} color="#c2c6dc" />
<ProjectActionText>Rules</ProjectActionText>
</ProjectAction>
</ProjectActions>
<KanbanBoard <KanbanBoard
listsData={listsData} listsData={currentListsData}
onCardDrop={onCardDrop} onCardDrop={onCardDrop}
onListDrop={onListDrop} onListDrop={onListDrop}
onCardCreate={onCardCreate} onCardCreate={onCardCreate}
onCreateList={onCreateList} onCreateList={onCreateList}
onQuickEditorOpen={onQuickEditorOpen} onQuickEditorOpen={onQuickEditorOpen}
onOpenListActionsPopup={(isOpen, left, top, taskGroupID) => { onOpenListActionsPopup={($targetRef, taskGroupID) => {
setPopupData({ isOpen, top, left, taskGroupID }); showPopup(
$targetRef,
<Popup title="List actions" tab={0} onClose={() => {}}>
<ListActions
taskGroupID={taskGroupID}
onArchiveTaskGroup={tgID => {
deleteTaskGroup({ variables: { taskGroupID: tgID } });
setPopupData(initialPopupState);
}}
/>
</Popup>,
);
}} }}
/> />
{popupData.isOpen && (
<PopupMenu
title="List Actions"
top={popupData.top}
onClose={() => setPopupData(initialPopupState)}
left={popupData.left}
>
<ListActions
taskGroupID={popupData.taskGroupID}
onArchiveTaskGroup={taskGroupID => {
deleteTaskGroup({ variables: { taskGroupID } });
setPopupData(initialPopupState);
}}
/>
</PopupMenu>
)}
{quickCardEditor.isOpen && ( {quickCardEditor.isOpen && (
<QuickCardEditor <QuickCardEditor
isOpen isOpen
@ -257,7 +418,35 @@ const Project = () => {
updateTaskName({ variables: { taskID: cardId, name: cardName } }); updateTaskName({ variables: { taskID: cardId, name: cardName } });
}} }}
onOpenPopup={() => console.log()} onOpenPopup={() => console.log()}
onArchiveCard={(_listId: string, cardId: string) => deleteTask({ variables: { taskID: cardId } })} onArchiveCard={(_listId: string, cardId: string) =>
deleteTask({
variables: { taskID: cardId },
update: client => {
const cacheData: any = client.readQuery({
query: FindProjectDocument,
variables: {
projectId: projectId,
},
});
const newData = {
...cacheData.findProject,
taskGroups: cacheData.findProject.taskGroups.map((taskGroup: any) => {
return {
...taskGroup,
tasks: taskGroup.tasks.filter((t: any) => t.id !== cardId),
};
}),
};
client.writeQuery({
query: FindProjectDocument,
variables: {
projectId: projectId,
},
data: { findProject: newData },
});
},
})
}
labels={[]} labels={[]}
top={quickCardEditor.top} top={quickCardEditor.top}
left={quickCardEditor.left} left={quickCardEditor.left}
@ -269,7 +458,7 @@ const Project = () => {
<Details <Details
refreshCache={() => { refreshCache={() => {
console.log('beep 2!'); console.log('beep 2!');
refetch(); // refetch();
}} }}
availableMembers={availableMembers} availableMembers={availableMembers}
projectURL={match.url} projectURL={match.url}
@ -284,7 +473,7 @@ const Project = () => {
setTaskDetails(initialTaskDetailsState); setTaskDetails(initialTaskDetailsState);
deleteTask({ variables: { taskID: deletedTask.taskID } }); deleteTask({ variables: { taskID: deletedTask.taskID } });
}} }}
onOpenAddLabelPopup={(task, bounds) => {}} onOpenAddLabelPopup={(task, $targetRef) => {}}
/> />
)} )}
/> />

View File

@ -1,5 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import styled from 'styled-components/macro'; import styled from 'styled-components/macro';
import GlobalTopNavbar from 'App/TopNavbar';
import { useGetProjectsQuery } from 'shared/generated/graphql'; import { useGetProjectsQuery } from 'shared/generated/graphql';
import ProjectGridItem from 'shared/components/ProjectGridItem'; import ProjectGridItem from 'shared/components/ProjectGridItem';
@ -40,13 +41,18 @@ const Projects = () => {
if (data) { if (data) {
const { projects } = data; const { projects } = data;
return ( return (
<ProjectGrid> <>
{projects.map(project => ( <GlobalTopNavbar name="Projects" />
<ProjectLink key={project.projectID} to={`/projects/${project.projectID}`}> <ProjectGrid>
<ProjectGridItem project={{ ...project, teamTitle: project.team.name, taskGroups: [] }} /> {projects.map(project => (
</ProjectLink> <ProjectLink key={project.id} to={`/projects/${project.id}`}>
))} <ProjectGridItem
</ProjectGrid> project={{ ...project, projectID: project.id, teamTitle: project.team.name, taskGroups: [] }}
/>
</ProjectLink>
))}
</ProjectGrid>
</>
); );
} }
return <div>Error!</div>; return <div>Error!</div>;

View File

@ -52,7 +52,7 @@ type Task = {
name: string; name: string;
position: number; position: number;
labels: Label[]; labels: Label[];
description?: string; description?: string | null;
members?: Array<TaskUser>; members?: Array<TaskUser>;
}; };

View File

@ -16,7 +16,7 @@ export const CardComposerWrapper = styled.div<{ isOpen: boolean }>`
`; `;
export const ListCard = styled.div` export const ListCard = styled.div`
background-color: #fff; background-color: ${props => mixin.lighten('#262c49', 0.05)};
border-radius: 3px; border-radius: 3px;
${mixin.boxShadowCard} ${mixin.boxShadowCard}
cursor: pointer; cursor: pointer;
@ -55,9 +55,10 @@ export const ListCardEditor = styled(TextareaAutosize)`
padding: 0; padding: 0;
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
&:focus {
border: none; color: #c2c6dc;
outline: none; l &:focus {
background-color: ${props => mixin.lighten('#262c49', 0.05)};
} }
`; `;

View File

@ -4,8 +4,8 @@ export const Container = styled.div<{ left: number; top: number }>`
position: absolute; position: absolute;
left: ${props => props.left}px; left: ${props => props.left}px;
top: ${props => props.top}px; top: ${props => props.top}px;
padding-top: 10px;
position: absolute; position: absolute;
padding-top: 10px;
height: auto; height: auto;
width: auto; width: auto;
transform: translate(-100%); transform: translate(-100%);
@ -18,12 +18,12 @@ export const Wrapper = styled.div`
padding-top: 8px; padding-top: 8px;
border-radius: 5px; border-radius: 5px;
box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.1);
position: relative; position: relative;
margin: 0; margin: 0;
color: #c2c6dc; color: #c2c6dc;
background: #262c49; background: #262c49;
border: 1px solid rgba(0, 0, 0, 0.1);
border-color: #414561; border-color: #414561;
`; `;

View File

@ -21,7 +21,7 @@ export const AddCardContainer = styled.div`
export const AddCardButton = styled.a` export const AddCardButton = styled.a`
border-radius: 3px; border-radius: 3px;
color: #5e6c84; color: #c2c6dc;
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
@ -32,9 +32,9 @@ export const AddCardButton = styled.a`
text-decoration: none; text-decoration: none;
user-select: none; user-select: none;
&:hover { &:hover {
background-color: rgba(9, 30, 66, 0.08); color: #c2c6dc;
color: #172b4d;
text-decoration: none; text-decoration: none;
background: rgb(115, 103, 240);
} }
`; `;
export const Wrapper = styled.div` export const Wrapper = styled.div`
@ -125,4 +125,5 @@ export const ListExtraMenuButtonWrapper = styled.div`
top: 4px; top: 4px;
z-index: 1; z-index: 1;
padding: 6px; padding: 6px;
padding-bottom: 0;
`; `;

View File

@ -26,7 +26,7 @@ type Props = {
wrapperProps?: any; wrapperProps?: any;
headerProps?: any; headerProps?: any;
index?: number; index?: number;
onExtraMenuOpen: (taskGroupID: string, pos: ElementPosition, size: ElementSize) => void; onExtraMenuOpen: (taskGroupID: string, $targetRef: React.RefObject<HTMLElement>) => void;
}; };
const List = React.forwardRef( const List = React.forwardRef(
@ -78,20 +78,7 @@ const List = React.forwardRef(
const handleExtraMenuOpen = () => { const handleExtraMenuOpen = () => {
if ($extraActionsRef && $extraActionsRef.current) { if ($extraActionsRef && $extraActionsRef.current) {
const pos = $extraActionsRef.current.getBoundingClientRect(); onExtraMenuOpen(id, $extraActionsRef);
onExtraMenuOpen(
id,
{
top: pos.top,
left: pos.left,
right: pos.right,
bottom: pos.bottom,
},
{
width: pos.width,
height: pos.height,
},
);
} }
}; };
useOnEscapeKeyDown(isEditingTitle, onEscape); useOnEscapeKeyDown(isEditingTitle, onEscape);
@ -116,7 +103,7 @@ const List = React.forwardRef(
{children && children} {children && children}
<AddCardContainer hidden={isComposerOpen}> <AddCardContainer hidden={isComposerOpen}>
<AddCardButton onClick={() => onOpenComposer(id)}> <AddCardButton onClick={() => onOpenComposer(id)}>
<Plus size={12} color="#42526e" /> <Plus size={12} color="#c2c6dc" />
<AddCardButtonText>Add another card</AddCardButtonText> <AddCardButtonText>Add another card</AddCardButtonText>
</AddCardButton> </AddCardButton>
</AddCardContainer> </AddCardContainer>

View File

@ -14,19 +14,19 @@ export const ListActionItem = styled.span`
cursor: pointer; cursor: pointer;
display: block; display: block;
font-size: 14px; font-size: 14px;
color: #172b4d; color: #c2c6dc;
font-weight: 400; font-weight: 400;
padding: 6px 12px; padding: 6px 12px;
position: relative; position: relative;
margin: 0 -12px; margin: 0 -12px;
text-decoration: none; text-decoration: none;
&:hover { &:hover {
background-color: rgba(9, 30, 66, 0.04); background: rgb(115, 103, 240);
} }
`; `;
export const ListSeparator = styled.hr` export const ListSeparator = styled.hr`
background-color: rgba(9, 30, 66, 0.13); background-color: #414561;
border: 0; border: 0;
height: 1px; height: 1px;
margin: 8px 0; margin: 8px 0;

View File

@ -29,7 +29,7 @@ type Props = {
onCardCreate: (taskGroupID: string, name: string) => void; onCardCreate: (taskGroupID: string, name: string) => void;
onQuickEditorOpen: (e: ContextMenuEvent) => void; onQuickEditorOpen: (e: ContextMenuEvent) => void;
onCreateList: (listName: string) => void; onCreateList: (listName: string) => void;
onExtraMenuOpen: (taskGroupID: string, pos: ElementPosition, size: ElementSize) => void; onExtraMenuOpen: (taskGroupID: string, $targetRef: React.RefObject<HTMLElement>) => void;
}; };
const Lists: React.FC<Props> = ({ const Lists: React.FC<Props> = ({

View File

@ -1,5 +1,6 @@
import styled from 'styled-components'; import styled from 'styled-components';
import TextareaAutosize from 'react-autosize-textarea/lib'; import TextareaAutosize from 'react-autosize-textarea/lib';
import { mixin } from 'shared/utils/styles';
export const MemberManagerWrapper = styled.div``; export const MemberManagerWrapper = styled.div``;
@ -11,17 +12,27 @@ export const MemberManagerSearchWrapper = styled.div`
export const MemberManagerSearch = styled(TextareaAutosize)` export const MemberManagerSearch = styled(TextareaAutosize)`
margin: 4px 0 12px; margin: 4px 0 12px;
width: 100%; width: 100%;
background-color: #ebecf0; border: 1px solid rgba(0, 0, 0, 0.1);
border: none; border-radius: 3px;
box-shadow: inset 0 0 0 2px #dfe1e6;
line-height: 20px; line-height: 20px;
padding: 8px 12px; padding: 8px 12px;
font-size: 14px; font-size: 14px;
color: #172b4d; font-family: 'Droid Sans';
font-weight: 400;
background: #262c49;
outline: none;
color: #c2c6dc;
border-color: #414561;
&:focus {
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
background: ${mixin.darken('#262c49', 0.15)};
}
`; `;
export const BoardMembersLabel = styled.h4` export const BoardMembersLabel = styled.h4`
color: #5e6c84; color: #c2c6dc;
font-size: 12px; font-size: 12px;
font-weight: 500; font-weight: 500;
letter-spacing: 0.04em; letter-spacing: 0.04em;
@ -52,7 +63,7 @@ export const BoardMemberListItemContent = styled.div`
white-space: nowrap; white-space: nowrap;
padding: 4px; padding: 4px;
margin-bottom: 2px; margin-bottom: 2px;
color: #172b4d; color: #c2c6dc;
`; `;
export const ProfileIcon = styled.div` export const ProfileIcon = styled.div`
@ -62,7 +73,7 @@ export const ProfileIcon = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #fff; color: #c2c6dc;
font-weight: 700; font-weight: 700;
background: rgb(115, 103, 240); background: rgb(115, 103, 240);
cursor: pointer; cursor: pointer;

View File

@ -43,7 +43,7 @@ const MemberManager: React.FC<MemberManagerProps> = ({
) )
.map(member => { .map(member => {
return ( return (
<BoardMembersListItem> <BoardMembersListItem key={member.userID}>
<BoardMemberListItemContent <BoardMemberListItemContent
onClick={() => { onClick={() => {
const isActive = activeMembers.findIndex(m => m.userID === member.userID) !== -1; const isActive = activeMembers.findIndex(m => m.userID === member.userID) !== -1;

View File

@ -11,7 +11,11 @@ export const ProfileIcon = styled.div<{ bgColor: string }>`
margin: 2px; margin: 2px;
background-color: ${props => props.bgColor}; background-color: ${props => props.bgColor};
border-radius: 25em; border-radius: 25em;
display: block; font-size: 16px;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
height: 50px; height: 50px;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
@ -20,6 +24,7 @@ export const ProfileIcon = styled.div<{ bgColor: string }>`
`; `;
export const ProfileInfo = styled.div` export const ProfileInfo = styled.div`
color: #c2c6dc;
margin: 0 0 0 64px; margin: 0 0 0 64px;
word-wrap: break-word; word-wrap: break-word;
`; `;
@ -29,11 +34,11 @@ export const InfoTitle = styled.h3`
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
line-height: 20px; line-height: 20px;
color: #172b4d; color: #c2c6dc;
`; `;
export const InfoUsername = styled.p` export const InfoUsername = styled.p`
color: #5e6c84; color: #c2c6dc;
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
`; `;
@ -41,10 +46,29 @@ export const InfoUsername = styled.p`
export const InfoBio = styled.p` export const InfoBio = styled.p`
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
color: #5e6c84; color: #c2c6dc;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
margin: 0; margin: 0;
padding: 0; padding: 0;
`; `;
export const MiniProfileActions = styled.ul`
list-style-type: none;
`;
export const MiniProfileActionWrapper = styled.li``;
export const MiniProfileActionItem = styled.span`
color: #c2c6dc;
cursor: pointer;
display: block;
font-weight: 400;
padding: 6px 12px;
position: relative;
text-decoration: none;
&:hover {
background: rgb(115, 103, 240);
}
`;

View File

@ -1,14 +1,25 @@
import React from 'react'; import React from 'react';
import { Profile, ProfileIcon, ProfileInfo, InfoTitle, InfoUsername, InfoBio } from './Styles'; import {
Profile,
ProfileIcon,
ProfileInfo,
InfoTitle,
InfoUsername,
InfoBio,
MiniProfileActions,
MiniProfileActionWrapper,
MiniProfileActionItem,
} from './Styles';
type MiniProfileProps = { type MiniProfileProps = {
displayName: string; displayName: string;
username: string; username: string;
bio: string; bio: string;
profileIcon: ProfileIcon; profileIcon: ProfileIcon;
onRemoveFromTask: () => void;
}; };
const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, profileIcon }) => { const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, profileIcon, onRemoveFromTask }) => {
return ( return (
<> <>
<Profile> <Profile>
@ -19,6 +30,17 @@ const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, p
<InfoBio>{bio}</InfoBio> <InfoBio>{bio}</InfoBio>
</ProfileInfo> </ProfileInfo>
</Profile> </Profile>
<MiniProfileActions>
<MiniProfileActionWrapper>
<MiniProfileActionItem
onClick={() => {
onRemoveFromTask();
}}
>
Remove from card
</MiniProfileActionItem>
</MiniProfileActionWrapper>
</MiniProfileActions>
</> </>
); );
}; };

View File

@ -2,7 +2,7 @@ import styled from 'styled-components';
import { mixin } from 'shared/utils/styles'; import { mixin } from 'shared/utils/styles';
export const ScrollOverlay = styled.div` export const ScrollOverlay = styled.div`
z-index: 1000000; z-index: 3000;
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;

View File

@ -4,25 +4,51 @@ import { Checkmark } from 'shared/icons';
import { SaveButton, DeleteButton, LabelBox, EditLabelForm, FieldLabel, FieldName } from './Styles'; import { SaveButton, DeleteButton, LabelBox, EditLabelForm, FieldLabel, FieldName } from './Styles';
type Props = { type Props = {
label: Label; label: Label | null;
onLabelEdit: (labelId: string, labelName: string, color: string) => void; onLabelEdit: (labelId: string | null, labelName: string, color: string) => void;
}; };
const LabelManager = ({ label, onLabelEdit }: Props) => { const LabelManager = ({ label, onLabelEdit }: Props) => {
const [currentLabel, setCurrentLabel] = useState(''); console.log(label);
const [currentLabel, setCurrentLabel] = useState(label ? label.name : '');
const [currentColor, setCurrentColor] = useState<string | null>(label ? label.color : null);
return ( return (
<EditLabelForm> <EditLabelForm>
<FieldLabel>Name</FieldLabel> <FieldLabel>Name</FieldLabel>
<FieldName id="labelName" type="text" name="name" value={currentLabel} /> <FieldName
id="labelName"
type="text"
name="name"
onChange={e => {
setCurrentLabel(e.currentTarget.value);
}}
value={currentLabel}
/>
<FieldLabel>Select a color</FieldLabel> <FieldLabel>Select a color</FieldLabel>
<div> <div>
{Object.values(LabelColors).map(labelColor => ( {Object.values(LabelColors).map(labelColor => (
<LabelBox color={labelColor}> <LabelBox
<Checkmark color="#fff" size={12} /> color={labelColor}
onClick={() => {
setCurrentColor(labelColor);
}}
>
{labelColor === currentColor && <Checkmark color="#fff" size={12} />}
</LabelBox> </LabelBox>
))} ))}
</div> </div>
<div> <div>
<SaveButton type="submit" value="Save" /> <SaveButton
onClick={e => {
e.preventDefault();
console.log(currentColor);
if (currentColor) {
onLabelEdit(label ? label.labelId : null, currentLabel, currentColor);
}
}}
type="submit"
value="Save"
/>
<DeleteButton type="submit" value="Delete" /> <DeleteButton type="submit" value="Delete" />
</div> </div>
</EditLabelForm> </EditLabelForm>

View File

@ -1,46 +1,78 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Pencil, Checkmark } from 'shared/icons'; import { Pencil, Checkmark } from 'shared/icons';
import { LabelSearch, ActiveIcon, Labels, Label, CardLabel, Section, SectionTitle, LabelIcon } from './Styles'; import {
LabelSearch,
ActiveIcon,
Labels,
Label,
CardLabel,
Section,
SectionTitle,
LabelIcon,
CreateLabelButton,
} from './Styles';
type Props = { type Props = {
labels?: Label[]; labels?: Label[];
onLabelToggle: (labelId: string) => void; onLabelToggle: (labelId: string) => void;
onLabelEdit: (labelId: string, labelName: string, color: string) => void; onLabelEdit: (labelId: string) => void;
onLabelCreate: () => void;
}; };
const LabelManager: React.FC<Props> = ({ labels, onLabelToggle, onLabelEdit }) => { const LabelManager: React.FC<Props> = ({ labels, onLabelToggle, onLabelEdit, onLabelCreate }) => {
const [currentLabel, setCurrentLabel] = useState(''); const [currentLabel, setCurrentLabel] = useState('');
const [currentSearch, setCurrentSearch] = useState('');
return ( return (
<> <>
<LabelSearch type="text" /> <LabelSearch
type="text"
placeholder="search labels..."
onChange={e => {
setCurrentSearch(e.currentTarget.value);
}}
value={currentSearch}
/>
<Section> <Section>
<SectionTitle>Labels</SectionTitle> <SectionTitle>Labels</SectionTitle>
<Labels> <Labels>
{labels && {labels &&
labels.map(label => ( labels
<Label> .filter(label => currentSearch === '' || label.name.toLowerCase().startsWith(currentSearch.toLowerCase()))
<LabelIcon> .map(label => (
<Pencil /> <Label key={label.labelId}>
</LabelIcon> <LabelIcon
<CardLabel onClick={() => {
key={label.labelId} onLabelEdit(label.labelId);
color={label.color} }}
active={currentLabel === label.labelId} >
onMouseEnter={() => { <Pencil color="#c2c6dc" />
setCurrentLabel(label.labelId); </LabelIcon>
}} <CardLabel
onClick={() => onLabelToggle(label.labelId)} key={label.labelId}
> color={label.color}
{label.name} active={currentLabel === label.labelId}
{label.active && ( onMouseEnter={() => {
<ActiveIcon> setCurrentLabel(label.labelId);
<Checkmark color="#fff" /> }}
</ActiveIcon> onClick={() => onLabelToggle(label.labelId)}
)} >
</CardLabel> {label.name}
</Label> {label.active && (
))} <ActiveIcon>
<Checkmark color="#fff" />
</ActiveIcon>
)}
</CardLabel>
</Label>
))}
</Labels> </Labels>
<CreateLabelButton
onClick={() => {
onLabelCreate();
}}
>
Create a new label
</CreateLabelButton>
</Section> </Section>
</> </>
); );

View File

@ -1,4 +1,4 @@
import React, { useState, useRef } from 'react'; import React, { useState, useRef, createRef } from 'react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import LabelColors from 'shared/constants/labelColors'; import LabelColors from 'shared/constants/labelColors';
import LabelManager from 'shared/components/PopupMenu/LabelManager'; import LabelManager from 'shared/components/PopupMenu/LabelManager';
@ -7,10 +7,12 @@ import ListActions from 'shared/components/ListActions';
import MemberManager from 'shared/components/MemberManager'; import MemberManager from 'shared/components/MemberManager';
import DueDateManager from 'shared/components/DueDateManager'; import DueDateManager from 'shared/components/DueDateManager';
import MiniProfile from 'shared/components/MiniProfile'; import MiniProfile from 'shared/components/MiniProfile';
import styled from 'styled-components';
import PopupMenu from '.'; import PopupMenu, { PopupProvider, usePopup, Popup } from '.';
import NormalizeStyles from 'App/NormalizeStyles'; import NormalizeStyles from 'App/NormalizeStyles';
import BaseStyles from 'App/BaseStyles'; import BaseStyles from 'App/BaseStyles';
import produce from 'immer';
export default { export default {
component: PopupMenu, component: PopupMenu,
@ -37,19 +39,93 @@ const labelData = [
}, },
]; ];
const OpenLabelBtn = styled.span``;
type TabProps = {
tab: number;
};
const LabelManagerEditor = () => {
const [labels, setLabels] = useState(labelData);
const [currentLabel, setCurrentLabel] = useState('');
const { setTab } = usePopup();
return (
<>
<Popup title="Labels" tab={0} onClose={action('on close')}>
<LabelManager
labels={labels}
onLabelCreate={() => {
setTab(2);
}}
onLabelEdit={labelId => {
setCurrentLabel(labelId);
setTab(1);
}}
onLabelToggle={labelId => {
setLabels(
produce(labels, draftState => {
const idx = labels.findIndex(label => label.labelId === labelId);
if (idx !== -1) {
draftState[idx] = { ...draftState[idx], active: !labels[idx].active };
}
}),
);
}}
/>
</Popup>
<Popup onClose={action('on close')} title="Edit label" tab={1}>
<LabelEditor
label={labels.find(label => label.labelId === currentLabel) ?? null}
onLabelEdit={(_labelId, name, color) => {
setLabels(
produce(labels, draftState => {
const idx = labels.findIndex(label => label.labelId === currentLabel);
if (idx !== -1) {
draftState[idx] = { ...draftState[idx], name, color };
}
}),
);
setTab(0);
}}
/>
</Popup>
<Popup onClose={action('on close')} title="Create new label" tab={2}>
<LabelEditor
label={null}
onLabelEdit={(_labelId, name, color) => {
setLabels([...labels, { labelId: name, name, color, active: false }]);
setTab(0);
}}
/>
</Popup>
</>
);
};
const OpenLabelsButton = () => {
const $buttonRef = createRef<HTMLButtonElement>();
const [currentLabel, setCurrentLabel] = useState('');
const [labels, setLabels] = useState(labelData);
const { showPopup, setTab } = usePopup();
console.log(labels);
return (
<OpenLabelBtn
ref={$buttonRef}
onClick={() => {
showPopup($buttonRef, <LabelManagerEditor />);
}}
>
Open
</OpenLabelBtn>
);
};
export const LabelsPopup = () => { export const LabelsPopup = () => {
const [isPopupOpen, setPopupOpen] = useState(false); const [isPopupOpen, setPopupOpen] = useState(false);
return ( return (
<> <PopupProvider>
{isPopupOpen && ( <OpenLabelsButton />
<PopupMenu title="Label" top={10} onClose={() => setPopupOpen(false)} left={10}> </PopupProvider>
<LabelManager labels={labelData} onLabelToggle={action('label toggle')} onLabelEdit={action('label edit')} />
</PopupMenu>
)}
<button type="submit" onClick={() => setPopupOpen(true)}>
Open
</button>
</>
); );
}; };
@ -58,7 +134,13 @@ export const LabelsLabelEditor = () => {
return ( return (
<> <>
{isPopupOpen && ( {isPopupOpen && (
<PopupMenu title="Change Label" top={10} onClose={() => setPopupOpen(false)} left={10}> <PopupMenu
onPrevious={action('on previous')}
title="Change Label"
top={10}
onClose={() => setPopupOpen(false)}
left={10}
>
<LabelEditor label={labelData[0]} onLabelEdit={action('label edit')} /> <LabelEditor label={labelData[0]} onLabelEdit={action('label edit')} />
</PopupMenu> </PopupMenu>
)} )}
@ -201,12 +283,19 @@ export const MiniProfilePopup = () => {
<NormalizeStyles /> <NormalizeStyles />
<BaseStyles /> <BaseStyles />
{popupData.isOpen && ( {popupData.isOpen && (
<PopupMenu title="Due Date" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}> <PopupMenu
noHeader
title="Due Date"
top={popupData.top}
onClose={() => setPopupData(initalState)}
left={popupData.left}
>
<MiniProfile <MiniProfile
displayName="Jordan Knott" displayName="Jordan Knott"
profileIcon={{ url: null, bgColor: '#000', initials: 'JK' }} profileIcon={{ url: null, bgColor: '#000', initials: 'JK' }}
username="@jordanthedev" username="@jordanthedev"
bio="Stuff and things" bio="Stuff and things"
onRemoveFromTask={action('mini profile')}
/> />
</PopupMenu> </PopupMenu>
)} )}
@ -236,3 +325,4 @@ export const MiniProfilePopup = () => {
</> </>
); );
}; };

View File

@ -1,20 +1,34 @@
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<{ top: number; left: number; ref: any }>` export const Container = styled.div<{ invert: boolean; top: number; left: number; ref: any }>`
left: ${props => props.left}px; left: ${props => props.left}px;
top: ${props => props.top}px; top: ${props => props.top}px;
background: #fff;
border-radius: 3px;
box-shadow: 0 8px 16px -4px rgba(9, 30, 66, 0.25), 0 0 0 1px rgba(9, 30, 66, 0.08);
display: block; display: block;
position: absolute; position: absolute;
width: 304px; width: 316px;
z-index: 100000000000; padding-top: 10px;
&:focus { height: auto;
outline: none; z-index: 40000;
border: none; ${props =>
} props.invert &&
css`
transform: translate(-100%);
`}
`;
export const Wrapper = styled.div`
padding: 5px;
padding-top: 8px;
border-radius: 5px;
box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.1);
position: relative;
margin: 0;
color: #c2c6dc;
background: #262c49;
border: 1px solid rgba(0, 0, 0, 0.1);
border-color: #414561;
`; `;
export const Header = styled.div` export const Header = styled.div`
@ -26,10 +40,10 @@ export const Header = styled.div`
export const HeaderTitle = styled.span` export const HeaderTitle = styled.span`
box-sizing: border-box; box-sizing: border-box;
color: #5e6c84; color: #c2c6dc;
display: block; display: block;
line-height: 40px; line-height: 40px;
border-bottom: 1px solid rgba(9, 30, 66, 0.13); border-bottom: 1px solid #414561;
margin: 0 12px; margin: 0 12px;
overflow: hidden; overflow: hidden;
padding: 0 32px; padding: 0 32px;
@ -46,23 +60,30 @@ export const Content = styled.div`
padding: 0 12px 12px; padding: 0 12px 12px;
`; `;
export const LabelSearch = styled.input` export const LabelSearch = styled.input`
box-sizing: border-box;
display: block;
transition-property: background-color, border-color, box-shadow;
transition-duration: 85ms;
transition-timing-function: ease;
margin: 4px 0 12px; margin: 4px 0 12px;
width: 100%; width: 100%;
background-color: #fafbfc; border: 1px solid rgba(0, 0, 0, 0.1);
border: none;
box-shadow: inset 0 0 0 2px #dfe1e6;
color: #172b4d;
box-sizing: border-box;
border-radius: 3px; border-radius: 3px;
display: block;
line-height: 20px; line-height: 20px;
padding: 8px 12px; padding: 8px 12px;
font-size: 14px; font-size: 14px;
font-family: 'Droid Sans'; font-family: 'Droid Sans';
font-weight: 400; font-weight: 400;
transition-property: background-color, border-color, box-shadow;
transition-duration: 85ms; background: #262c49;
transition-timing-function: ease; outline: none;
color: #c2c6dc;
border-color: #414561;
&:focus {
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
background: ${mixin.darken('#262c49', 0.15)};
}
`; `;
export const Section = styled.div` export const Section = styled.div`
@ -70,7 +91,7 @@ export const Section = styled.div`
`; `;
export const SectionTitle = styled.h4` export const SectionTitle = styled.h4`
color: #5e6c84; color: #c2c6dc;
font-size: 12px; font-size: 12px;
font-weight: 500; font-weight: 500;
letter-spacing: 0.04em; letter-spacing: 0.04em;
@ -95,7 +116,7 @@ export const CardLabel = styled.span<{ active: boolean; color: string }>`
props.active && props.active &&
css` css`
margin-left: 4px; margin-left: 4px;
box-shadow: -8px 0 ${mixin.darken(props.color, 0.15)}; box-shadow: -8px 0 ${mixin.darken(props.color, 0.12)};
border-radius: 3px; border-radius: 3px;
`} `}
@ -113,6 +134,7 @@ export const CardLabel = styled.span<{ active: boolean; color: string }>`
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
min-height: 31px;
`; `;
export const CloseButton = styled.div` export const CloseButton = styled.div`
@ -126,8 +148,6 @@ export const CloseButton = styled.div`
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 40; z-index: 40;
height: 20px;
width: 20px;
cursor: pointer; cursor: pointer;
`; `;
@ -142,14 +162,14 @@ export const LabelIcon = styled.div`
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 20px; height: 100%;
font-size: 16px; font-size: 16px;
line-height: 20px; line-height: 20px;
width: 20px; width: auto;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background: rgba(9, 30, 66, 0.08); background: rgb(115, 103, 240);
} }
`; `;
@ -186,19 +206,27 @@ export const FieldLabel = styled.label`
export const FieldName = styled.input` export const FieldName = styled.input`
margin: 4px 0 12px; margin: 4px 0 12px;
width: 100%; width: 100%;
background-color: #fafbfc;
border: none;
box-shadow: inset 0 0 0 2px #dfe1e6;
color: #172b4d;
box-sizing: border-box; box-sizing: border-box;
border-radius: 3px; border-radius: 3px;
display: block; display: block;
line-height: 20px; line-height: 20px;
margin-bottom: 12px; margin-bottom: 12px;
padding: 8px 12px; padding: 8px 12px;
background: #262c49;
border-width: 1px;
border-style: solid;
border-color: transparent;
border-image: initial;
font-size: 12px; font-size: 12px;
font-weight: 400; font-weight: 400;
color: #c2c6dc;
&:focus {
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
background: ${mixin.darken('#262c49', 0.15)};
}
`; `;
export const LabelBox = styled.span<{ color: string }>` export const LabelBox = styled.span<{ color: string }>`
@ -208,6 +236,7 @@ export const LabelBox = styled.span<{ color: string }>`
padding: 0; padding: 0;
width: 48px; width: 48px;
cursor: pointer;
background-color: ${props => props.color}; background-color: ${props => props.color};
border-radius: 4px; border-radius: 4px;
color: #fff; color: #fff;
@ -217,6 +246,7 @@ export const LabelBox = styled.span<{ color: string }>`
`; `;
export const SaveButton = styled.input` export const SaveButton = styled.input`
cursor: pointer;
background-color: #5aac44; background-color: #5aac44;
box-shadow: none; box-shadow: none;
border: none; border: none;
@ -239,8 +269,7 @@ export const DeleteButton = styled.input`
border: none; border: none;
color: #fff; color: #fff;
cursor: pointer; cursor: pointer;
display: inline-block; type="submit"font-weight: 400;
font-weight: 400;
line-height: 20px; line-height: 20px;
margin: 8px 4px 0 0; margin: 8px 4px 0 0;
padding: 6px 12px; padding: 6px 12px;
@ -248,3 +277,53 @@ export const DeleteButton = styled.input`
border-radius: 3px; border-radius: 3px;
float: right; float: right;
`; `;
export const CreateLabelButton = styled.button`
outline: none;
border: none;
width: 100%;
border-radius: 3px;
line-height: 20px;
margin-bottom: 8px;
padding: 6px 12px;
background-color: none;
text-align: center;
color: #c2c6dc;
margin: 8px 4px 0 0;
font-size: 14px;
cursor: pointer;
&:hover {
background: rgb(115, 103, 240);
}
`;
export const PreviousButton = styled.div`
padding: 10px 12px 10px 8px;
position: absolute;
top: 0;
left: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
z-index: 40;
cursor: pointer;
`;
export const ContainerDiamond = styled.div<{ invert: boolean }>`
top: 10px;
${props => (props.invert ? 'right: 10px; ' : 'left: 15px;')}
position: absolute;
width: 10px;
height: 10px;
display: block;
transform: rotate(45deg) translate(-7px);
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-left: 1px solid rgba(0, 0, 0, 0.1);
z-index: 10;
background: #262c49;
border-color: #414561;
`;

View File

@ -1,30 +1,219 @@
import React, { useRef } from 'react'; import React, { useRef, createContext, RefObject, useState, useContext } from 'react';
import { Cross } from 'shared/icons'; import { Cross, AngleLeft } from 'shared/icons';
import useOnOutsideClick from 'shared/hooks/onOutsideClick'; import useOnOutsideClick from 'shared/hooks/onOutsideClick';
import { Container, Header, HeaderTitle, Content, CloseButton } from './Styles'; import { createPortal } from 'react-dom';
import produce from 'immer';
import {
Container,
ContainerDiamond,
Header,
HeaderTitle,
Content,
CloseButton,
PreviousButton,
Wrapper,
} from './Styles';
type Props = { type PopupContextState = {
title: string; show: (target: RefObject<HTMLElement>, content: JSX.Element) => void;
setTab: (newTab: number) => void;
getCurrentTab: () => number;
};
type PopupProps = {
title: string | null;
onClose: () => void;
tab: number;
};
type PopupContainerProps = {
top: number; top: number;
left: number; left: number;
invert: boolean;
onClose: () => void; onClose: () => void;
}; };
const PopupMenu: React.FC<Props> = ({ title, top, left, onClose, children }) => { const PopupContainer: React.FC<PopupContainerProps> = ({ top, left, onClose, children, invert }) => {
const $containerRef = useRef();
useOnOutsideClick($containerRef, true, onClose, null);
return (
<Container left={left} top={top} ref={$containerRef} invert={invert}>
{children}
</Container>
);
};
const PopupContext = createContext<PopupContextState>({
show: () => {},
setTab: () => {},
getCurrentTab: () => 0,
});
export const usePopup = () => {
const ctx = useContext<PopupContextState>(PopupContext);
return { showPopup: ctx.show, setTab: ctx.setTab, getCurrentTab: ctx.getCurrentTab };
};
type PopupState = {
isOpen: boolean;
left: number;
top: number;
invert: boolean;
currentTab: number;
previousTab: number;
content: JSX.Element | null;
};
const { Provider, Consumer } = PopupContext;
const canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
const defaultState = {
isOpen: false,
left: 0,
top: 0,
invert: false,
currentTab: 0,
previousTab: 0,
content: null,
};
export const PopupProvider: React.FC = ({ children }) => {
const [currentState, setState] = useState<PopupState>(defaultState);
const show = (target: RefObject<HTMLElement>, content: JSX.Element) => {
console.log(target);
if (target && target.current) {
const bounds = target.current.getBoundingClientRect();
if (bounds.left + 304 + 30 > window.innerWidth) {
console.log('open!');
setState({
isOpen: true,
left: bounds.left + bounds.width,
top: bounds.top + bounds.height,
invert: true,
currentTab: 0,
previousTab: 0,
content,
});
} else {
console.log('open NOT INVERT!');
setState({
isOpen: true,
left: bounds.left,
top: bounds.top + bounds.height,
invert: false,
currentTab: 0,
previousTab: 0,
content,
});
}
}
};
const portalTarget = canUseDOM ? document.body : null; // appease flow
const setTab = (newTab: number) => {
setState((prevState: PopupState) => {
return {
...prevState,
previousTab: currentState.currentTab,
currentTab: newTab,
};
});
};
const getCurrentTab = () => {
return currentState.currentTab;
};
return (
<Provider value={{ show, setTab, getCurrentTab }}>
{portalTarget &&
currentState.isOpen &&
createPortal(
<PopupContainer
invert={currentState.invert}
top={currentState.top}
left={currentState.left}
onClose={() => setState(defaultState)}
>
{currentState.content}
<ContainerDiamond invert={currentState.invert} />
</PopupContainer>,
portalTarget,
)}
{children}
</Provider>
);
};
type Props = {
title: string | null;
top: number;
left: number;
onClose: () => void;
onPrevious?: () => void | null;
noHeader?: boolean | null;
};
const PopupMenu: React.FC<Props> = ({ 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 left={left} top={top} ref={$containerRef}> <Container invert={false} left={left} top={top} ref={$containerRef}>
<Header> <Wrapper>
<HeaderTitle>{title}</HeaderTitle> {onPrevious && (
<CloseButton onClick={() => onClose()}> <PreviousButton onClick={onPrevious}>
<Cross /> <AngleLeft color="#c2c6dc" />
</CloseButton> </PreviousButton>
</Header> )}
<Content>{children}</Content> {noHeader ? (
<CloseButton onClick={() => onClose()}>
<Cross color="#c2c6dc" />
</CloseButton>
) : (
<Header>
<HeaderTitle>{title}</HeaderTitle>
<CloseButton onClick={() => onClose()}>
<Cross color="#c2c6dc" />
</CloseButton>
</Header>
)}
<Content>{children}</Content>
</Wrapper>
</Container> </Container>
); );
}; };
export const Popup: React.FC<PopupProps> = ({ title, onClose, tab, children }) => {
const { getCurrentTab, setTab } = usePopup();
if (getCurrentTab() !== tab) {
return null;
}
return (
<>
<Wrapper>
{tab > 0 && (
<PreviousButton
onClick={() => {
setTab(0);
}}
>
<AngleLeft color="#c2c6dc" />
</PreviousButton>
)}
{title && (
<Header>
<HeaderTitle>{title}</HeaderTitle>
</Header>
)}
<CloseButton onClick={() => onClose()}>
<Cross color="#c2c6dc" />
</CloseButton>
<Content>{children}</Content>
</Wrapper>
</>
);
};
export default PopupMenu; export default PopupMenu;

View File

@ -61,7 +61,7 @@ export const Default = () => {
onSaveName={action('on save name')} onSaveName={action('on save name')}
onOpenComposer={action('on open composer')} onOpenComposer={action('on open composer')}
tasks={[]} tasks={[]}
onExtraMenuOpen={(taskGroupID, pos, size) => console.log(taskGroupID, pos, size)} onExtraMenuOpen={(taskGroupID, $targetRef) => console.log(taskGroupID, $targetRef)}
> >
<ListCards> <ListCards>
<Card <Card

View File

@ -2,7 +2,7 @@ import styled, { keyframes } from 'styled-components';
import TextareaAutosize from 'react-autosize-textarea'; import TextareaAutosize from 'react-autosize-textarea';
export const Wrapper = styled.div<{ open: boolean }>` export const Wrapper = styled.div<{ open: boolean }>`
background: rgba(0, 0, 0, 0.6); background: rgba(0, 0, 0, 0.4);
bottom: 0; bottom: 0;
color: #fff; color: #fff;
left: 0; left: 0;

View File

@ -286,4 +286,7 @@ export const UnassignedLabel = styled.div`
color: rgb(137, 147, 164); color: rgb(137, 147, 164);
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
height: 32px;
`; `;

View File

@ -47,6 +47,7 @@ export const Default = () => {
onTaskDescriptionChange={(_task, desc) => setDescription(desc)} onTaskDescriptionChange={(_task, desc) => setDescription(desc)}
onDeleteTask={action('delete task')} onDeleteTask={action('delete task')}
onCloseModal={action('close modal')} onCloseModal={action('close modal')}
onMemberProfile={action('profile')}
onOpenAddMemberPopup={action('open add member popup')} onOpenAddMemberPopup={action('open add member popup')}
onOpenAddLabelPopup={action('open add label popup')} onOpenAddLabelPopup={action('open add label popup')}
/> />

View File

@ -93,13 +93,27 @@ const DetailsEditor: React.FC<DetailsEditorProps> = ({
); );
}; };
type TaskAssigneeProps = {
member: TaskUser;
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
};
const TaskAssignee: React.FC<TaskAssigneeProps> = ({ member, onMemberProfile }) => {
const $memberRef = useRef<HTMLDivElement>(null);
return (
<TaskDetailAssignee ref={$memberRef} onClick={() => onMemberProfile($memberRef, member.userID)} key={member.userID}>
<ProfileIcon>{member.profileIcon.initials ?? ''}</ProfileIcon>
</TaskDetailAssignee>
);
};
type TaskDetailsProps = { type TaskDetailsProps = {
task: Task; task: Task;
onTaskNameChange: (task: Task, newName: string) => void; onTaskNameChange: (task: Task, newName: string) => void;
onTaskDescriptionChange: (task: Task, newDescription: string) => void; onTaskDescriptionChange: (task: Task, newDescription: string) => void;
onDeleteTask: (task: Task) => void; onDeleteTask: (task: Task) => void;
onOpenAddMemberPopup: (task: Task, bounds: ElementBounds) => void; onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
onOpenAddLabelPopup: (task: Task, bounds: ElementBounds) => void; onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
onCloseModal: () => void; onCloseModal: () => void;
}; };
@ -111,6 +125,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
onCloseModal, onCloseModal,
onOpenAddMemberPopup, onOpenAddMemberPopup,
onOpenAddLabelPopup, onOpenAddLabelPopup,
onMemberProfile,
}) => { }) => {
const [editorOpen, setEditorOpen] = useState(false); const [editorOpen, setEditorOpen] = useState(false);
const [description, setDescription] = useState(task.description ?? ''); const [description, setDescription] = useState(task.description ?? '');
@ -130,23 +145,14 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
const $unassignedRef = useRef<HTMLDivElement>(null); const $unassignedRef = useRef<HTMLDivElement>(null);
const $addMemberRef = useRef<HTMLDivElement>(null); const $addMemberRef = useRef<HTMLDivElement>(null);
const onUnassignedClick = () => { const onUnassignedClick = () => {
const bounds = convertDivElementRefToBounds($unassignedRef); onOpenAddMemberPopup(task, $unassignedRef);
if (bounds) {
onOpenAddMemberPopup(task, bounds);
}
}; };
const onAddMember = () => { const onAddMember = () => {
const bounds = convertDivElementRefToBounds($addMemberRef); onOpenAddMemberPopup(task, $addMemberRef);
if (bounds) {
onOpenAddMemberPopup(task, bounds);
}
}; };
const $addLabelRef = useRef<HTMLDivElement>(null); const $addLabelRef = useRef<HTMLDivElement>(null);
const onAddLabel = () => { const onAddLabel = () => {
const bounds = convertDivElementRefToBounds($addLabelRef); onOpenAddLabelPopup(task, $addLabelRef);
if (bounds) {
onOpenAddLabelPopup(task, bounds);
}
}; };
console.log(task); console.log(task);
return ( return (
@ -204,14 +210,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
) : ( ) : (
<> <>
{task.members && {task.members &&
task.members.map(member => { task.members.map(member => <TaskAssignee member={member} onMemberProfile={onMemberProfile} />)}
console.log(member);
return (
<TaskDetailAssignee key={member.userID}>
<ProfileIcon>{member.profileIcon.initials ?? ''}</ProfileIcon>
</TaskDetailAssignee>
);
})}
<TaskDetailsAddMember ref={$addMemberRef} onClick={onAddMember}> <TaskDetailsAddMember ref={$addMemberRef} onClick={onAddMember}>
<TaskDetailsAddMemberIcon> <TaskDetailsAddMemberIcon>
<Plus size={16} color="#c2c6dc" /> <Plus size={16} color="#c2c6dc" />

View File

@ -1,4 +1,5 @@
import styled from 'styled-components'; import styled, { css } from 'styled-components';
import { mixin } from 'shared/utils/styles';
export const NavbarWrapper = styled.div` export const NavbarWrapper = styled.div`
width: 100%; width: 100%;
@ -76,8 +77,10 @@ export const ProfileIcon = styled.div<{ bgColor: string }>`
`; `;
export const ProjectMeta = styled.div` export const ProjectMeta = styled.div`
align-items: center;
display: flex; display: flex;
padding-top: 9px;
margin-left: -14px;
align-items: center;
max-width: 100%; max-width: 100%;
min-height: 51px; min-height: 51px;
`; `;
@ -91,11 +94,11 @@ export const ProjectTabs = styled.div`
max-width: 100%; max-width: 100%;
`; `;
export const ProjectTab = styled.span` export const ProjectTab = styled.span<{ active?: boolean }>`
font-size: 80%; font-size: 80%;
color: #c2c6dc; color: #c2c6dc;
font-size: 15px; font-size: 15px;
cursor: default; cursor: pointer;
display: flex; display: flex;
line-height: normal; line-height: normal;
min-width: 1px; min-width: 1px;
@ -103,16 +106,71 @@ export const ProjectTab = styled.span`
transition-property: box-shadow, color; transition-property: box-shadow, color;
white-space: nowrap; white-space: nowrap;
flex: 0 1 auto; flex: 0 1 auto;
padding-bottom: 12px; padding-bottom: 12px;
box-shadow: inset 0 -2px #d85dd8; &:not(:last-child) {
color: #d85dd8; margin-right: 20px;
}
${props =>
props.active
? css`
box-shadow: inset 0 -2px #d85dd8;
color: #d85dd8;
`
: css`
&:hover {
box-shadow: inset 0 -2px #cbd4db;
color: ${mixin.lighten('#c2c6dc', 0.25)};
}
`}
`; `;
export const ProjectName = styled.h1` export const ProjectName = styled.h1`
color: #c2c6dc; color: #c2c6dc;
margin-top: 9px;
font-weight: 600; font-weight: 600;
font-size: 20px; font-size: 20px;
padding: 6px 10px 6px 8px;
`;
export const ProjectSwitcher = styled.button`
font-size: 20px;
outline: none;
border: none;
width: 100px;
border-radius: 3px;
line-height: 20px;
padding: 6px 4px;
background-color: none;
text-align: center;
color: #c2c6dc;
cursor: pointer;
&:hover {
background: rgb(115, 103, 240);
}
`;
export const Separator = styled.div`
color: #c2c6dc;
font-size: 16px;
padding-left: 4px;
padding-right: 4px;
`;
export const ProjectSettingsButton = styled.button`
outline: none;
border: none;
border-radius: 3px;
line-height: 20px;
width: 28px;
height: 28px;
background-color: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover {
background: rgb(115, 103, 240);
}
`; `;

View File

@ -38,6 +38,7 @@ export const Default = () => {
<NormalizeStyles /> <NormalizeStyles />
<BaseStyles /> <BaseStyles />
<TopNavbar <TopNavbar
projectName="Projects"
bgColor="#7367F0" bgColor="#7367F0"
firstName="Jordan" firstName="Jordan"
lastName="Knott" lastName="Knott"

View File

@ -1,10 +1,12 @@
import React, { useRef } from 'react'; import React, { useRef } from 'react';
import { Bell } from 'shared/icons'; import { Star, Bell, Cog, AngleDown } from 'shared/icons';
import { import {
NotificationContainer, NotificationContainer,
GlobalActions, GlobalActions,
ProjectActions, ProjectActions,
ProjectSwitcher,
Separator,
ProjectMeta, ProjectMeta,
ProjectName, ProjectName,
ProjectTabs, ProjectTabs,
@ -13,6 +15,7 @@ import {
NavbarHeader, NavbarHeader,
Breadcrumbs, Breadcrumbs,
BreadcrumpSeparator, BreadcrumpSeparator,
ProjectSettingsButton,
ProfileIcon, ProfileIcon,
ProfileContainer, ProfileContainer,
ProfileNameWrapper, ProfileNameWrapper,
@ -21,6 +24,7 @@ import {
} from './Styles'; } from './Styles';
type NavBarProps = { type NavBarProps = {
projectName: string;
onProfileClick: (bottom: number, right: number) => void; onProfileClick: (bottom: number, right: number) => void;
onNotificationClick: () => void; onNotificationClick: () => void;
bgColor: string; bgColor: string;
@ -29,6 +33,7 @@ type NavBarProps = {
initials: string; initials: string;
}; };
const NavBar: React.FC<NavBarProps> = ({ const NavBar: React.FC<NavBarProps> = ({
projectName,
onProfileClick, onProfileClick,
onNotificationClick, onNotificationClick,
firstName, firstName,
@ -47,10 +52,19 @@ const NavBar: React.FC<NavBarProps> = ({
<NavbarHeader> <NavbarHeader>
<ProjectActions> <ProjectActions>
<ProjectMeta> <ProjectMeta>
<ProjectName>Production Team</ProjectName> <ProjectSwitcher>Projects</ProjectSwitcher>
<Separator>»</Separator>
<ProjectName>{projectName}</ProjectName>
<ProjectSettingsButton>
<AngleDown color="#c2c6dc" />
</ProjectSettingsButton>
<Star filled color="#c2c6dc" />
</ProjectMeta> </ProjectMeta>
<ProjectTabs> <ProjectTabs>
<ProjectTab>Board</ProjectTab> <ProjectTab active>Board</ProjectTab>
<ProjectTab>Calender</ProjectTab>
<ProjectTab>Timeline</ProjectTab>
<ProjectTab>Wiki</ProjectTab>
</ProjectTabs> </ProjectTabs>
</ProjectActions> </ProjectActions>
<GlobalActions> <GlobalActions>

View File

@ -17,7 +17,7 @@ export type Scalars = {
export type ProjectLabel = { export type ProjectLabel = {
__typename?: 'ProjectLabel'; __typename?: 'ProjectLabel';
projectLabelID: Scalars['ID']; id: Scalars['ID'];
createdDate: Scalars['Time']; createdDate: Scalars['Time'];
colorHex: Scalars['String']; colorHex: Scalars['String'];
name?: Maybe<Scalars['String']>; name?: Maybe<Scalars['String']>;
@ -25,7 +25,7 @@ export type ProjectLabel = {
export type TaskLabel = { export type TaskLabel = {
__typename?: 'TaskLabel'; __typename?: 'TaskLabel';
taskLabelID: Scalars['ID']; id: Scalars['ID'];
projectLabelID: Scalars['UUID']; projectLabelID: Scalars['UUID'];
assignedDate: Scalars['Time']; assignedDate: Scalars['Time'];
colorHex: Scalars['String']; colorHex: Scalars['String'];
@ -41,7 +41,7 @@ export type ProfileIcon = {
export type ProjectMember = { export type ProjectMember = {
__typename?: 'ProjectMember'; __typename?: 'ProjectMember';
userID: Scalars['ID']; id: Scalars['ID'];
firstName: Scalars['String']; firstName: Scalars['String'];
lastName: Scalars['String']; lastName: Scalars['String'];
profileIcon: ProfileIcon; profileIcon: ProfileIcon;
@ -49,7 +49,7 @@ export type ProjectMember = {
export type RefreshToken = { export type RefreshToken = {
__typename?: 'RefreshToken'; __typename?: 'RefreshToken';
tokenId: Scalars['ID']; id: Scalars['ID'];
userId: Scalars['UUID']; userId: Scalars['UUID'];
expiresAt: Scalars['Time']; expiresAt: Scalars['Time'];
createdAt: Scalars['Time']; createdAt: Scalars['Time'];
@ -57,7 +57,7 @@ export type RefreshToken = {
export type UserAccount = { export type UserAccount = {
__typename?: 'UserAccount'; __typename?: 'UserAccount';
userID: Scalars['ID']; id: Scalars['ID'];
email: Scalars['String']; email: Scalars['String'];
createdAt: Scalars['Time']; createdAt: Scalars['Time'];
firstName: Scalars['String']; firstName: Scalars['String'];
@ -68,14 +68,14 @@ export type UserAccount = {
export type Team = { export type Team = {
__typename?: 'Team'; __typename?: 'Team';
teamID: Scalars['ID']; id: Scalars['ID'];
createdAt: Scalars['Time']; createdAt: Scalars['Time'];
name: Scalars['String']; name: Scalars['String'];
}; };
export type Project = { export type Project = {
__typename?: 'Project'; __typename?: 'Project';
projectID: Scalars['ID']; id: Scalars['ID'];
createdAt: Scalars['Time']; createdAt: Scalars['Time'];
name: Scalars['String']; name: Scalars['String'];
team: Team; team: Team;
@ -87,7 +87,7 @@ export type Project = {
export type TaskGroup = { export type TaskGroup = {
__typename?: 'TaskGroup'; __typename?: 'TaskGroup';
taskGroupID: Scalars['ID']; id: Scalars['ID'];
projectID: Scalars['String']; projectID: Scalars['String'];
createdAt: Scalars['Time']; createdAt: Scalars['Time'];
name: Scalars['String']; name: Scalars['String'];
@ -97,7 +97,7 @@ export type TaskGroup = {
export type Task = { export type Task = {
__typename?: 'Task'; __typename?: 'Task';
taskID: Scalars['ID']; id: Scalars['ID'];
taskGroup: TaskGroup; taskGroup: TaskGroup;
createdAt: Scalars['Time']; createdAt: Scalars['Time'];
name: Scalars['String']; name: Scalars['String'];
@ -382,14 +382,29 @@ export type AssignTaskMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { assignTask: ( & { assignTask: (
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID'> & Pick<Task, 'id'>
& { assigned: Array<( & { assigned: Array<(
{ __typename?: 'ProjectMember' } { __typename?: 'ProjectMember' }
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'> & Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
)> } )> }
) } ) }
); );
export type CreateProjectLabelMutationVariables = {
projectID: Scalars['UUID'];
labelColorID: Scalars['UUID'];
name: Scalars['String'];
};
export type CreateProjectLabelMutation = (
{ __typename?: 'Mutation' }
& { createProjectLabel: (
{ __typename?: 'ProjectLabel' }
& Pick<ProjectLabel, 'id' | 'createdDate' | 'colorHex' | 'name'>
) }
);
export type CreateTaskMutationVariables = { export type CreateTaskMutationVariables = {
taskGroupID: Scalars['String']; taskGroupID: Scalars['String'];
name: Scalars['String']; name: Scalars['String'];
@ -401,11 +416,18 @@ export type CreateTaskMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { createTask: ( & { createTask: (
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID' | 'name' | 'position'> & Pick<Task, 'id' | 'name' | 'position' | 'description'>
& { taskGroup: ( & { taskGroup: (
{ __typename?: 'TaskGroup' } { __typename?: 'TaskGroup' }
& Pick<TaskGroup, 'taskGroupID'> & Pick<TaskGroup, 'id'>
) } ), assigned: Array<(
{ __typename?: 'ProjectMember' }
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
& { profileIcon: (
{ __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
) }
)> }
) } ) }
); );
@ -420,7 +442,7 @@ export type CreateTaskGroupMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { createTaskGroup: ( & { createTaskGroup: (
{ __typename?: 'TaskGroup' } { __typename?: 'TaskGroup' }
& Pick<TaskGroup, 'taskGroupID' | 'name' | 'position'> & Pick<TaskGroup, 'id' | 'name' | 'position'>
) } ) }
); );
@ -449,10 +471,10 @@ export type DeleteTaskGroupMutation = (
& Pick<DeleteTaskGroupPayload, 'ok' | 'affectedRows'> & Pick<DeleteTaskGroupPayload, 'ok' | 'affectedRows'>
& { taskGroup: ( & { taskGroup: (
{ __typename?: 'TaskGroup' } { __typename?: 'TaskGroup' }
& Pick<TaskGroup, 'taskGroupID'> & Pick<TaskGroup, 'id'>
& { tasks: Array<( & { tasks: Array<(
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID' | 'name'> & Pick<Task, 'id' | 'name'>
)> } )> }
) } ) }
) } ) }
@ -470,20 +492,23 @@ export type FindProjectQuery = (
& Pick<Project, 'name'> & Pick<Project, 'name'>
& { members: Array<( & { members: Array<(
{ __typename?: 'ProjectMember' } { __typename?: 'ProjectMember' }
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'> & Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
& { profileIcon: ( & { profileIcon: (
{ __typename?: 'ProfileIcon' } { __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'> & Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
) } ) }
)>, labels: Array<(
{ __typename?: 'ProjectLabel' }
& Pick<ProjectLabel, 'id' | 'createdDate' | 'colorHex' | 'name'>
)>, taskGroups: Array<( )>, taskGroups: Array<(
{ __typename?: 'TaskGroup' } { __typename?: 'TaskGroup' }
& Pick<TaskGroup, 'taskGroupID' | 'name' | 'position'> & Pick<TaskGroup, 'id' | 'name' | 'position'>
& { tasks: Array<( & { tasks: Array<(
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID' | 'name' | 'position' | 'description'> & Pick<Task, 'id' | 'name' | 'position' | 'description'>
& { assigned: Array<( & { assigned: Array<(
{ __typename?: 'ProjectMember' } { __typename?: 'ProjectMember' }
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'> & Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
& { profileIcon: ( & { profileIcon: (
{ __typename?: 'ProfileIcon' } { __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'> & Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
@ -503,13 +528,13 @@ export type FindTaskQuery = (
{ __typename?: 'Query' } { __typename?: 'Query' }
& { findTask: ( & { findTask: (
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID' | 'name' | 'description' | 'position'> & Pick<Task, 'id' | 'name' | 'description' | 'position'>
& { taskGroup: ( & { taskGroup: (
{ __typename?: 'TaskGroup' } { __typename?: 'TaskGroup' }
& Pick<TaskGroup, 'taskGroupID'> & Pick<TaskGroup, 'id'>
), assigned: Array<( ), assigned: Array<(
{ __typename?: 'ProjectMember' } { __typename?: 'ProjectMember' }
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'> & Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
& { profileIcon: ( & { profileIcon: (
{ __typename?: 'ProfileIcon' } { __typename?: 'ProfileIcon' }
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'> & Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
@ -525,10 +550,10 @@ export type GetProjectsQuery = (
{ __typename?: 'Query' } { __typename?: 'Query' }
& { projects: Array<( & { projects: Array<(
{ __typename?: 'Project' } { __typename?: 'Project' }
& Pick<Project, 'projectID' | 'name'> & Pick<Project, 'id' | 'name'>
& { team: ( & { team: (
{ __typename?: 'Team' } { __typename?: 'Team' }
& Pick<Team, 'teamID' | 'name'> & Pick<Team, 'id' | 'name'>
) } ) }
)> } )> }
); );
@ -558,10 +583,10 @@ export type UnassignTaskMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { unassignTask: ( & { unassignTask: (
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID'> & Pick<Task, 'id'>
& { assigned: Array<( & { assigned: Array<(
{ __typename?: 'ProjectMember' } { __typename?: 'ProjectMember' }
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'> & Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
)> } )> }
) } ) }
); );
@ -576,7 +601,7 @@ export type UpdateTaskDescriptionMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { updateTaskDescription: ( & { updateTaskDescription: (
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID'> & Pick<Task, 'id'>
) } ) }
); );
@ -590,7 +615,7 @@ export type UpdateTaskGroupLocationMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { updateTaskGroupLocation: ( & { updateTaskGroupLocation: (
{ __typename?: 'TaskGroup' } { __typename?: 'TaskGroup' }
& Pick<TaskGroup, 'taskGroupID' | 'position'> & Pick<TaskGroup, 'id' | 'position'>
) } ) }
); );
@ -605,7 +630,7 @@ export type UpdateTaskLocationMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { updateTaskLocation: ( & { updateTaskLocation: (
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID' | 'createdAt' | 'name' | 'position'> & Pick<Task, 'id' | 'createdAt' | 'name' | 'position'>
) } ) }
); );
@ -619,7 +644,7 @@ export type UpdateTaskNameMutation = (
{ __typename?: 'Mutation' } { __typename?: 'Mutation' }
& { updateTaskName: ( & { updateTaskName: (
{ __typename?: 'Task' } { __typename?: 'Task' }
& Pick<Task, 'taskID' | 'name' | 'position'> & Pick<Task, 'id' | 'name' | 'position'>
) } ) }
); );
@ -627,12 +652,12 @@ export type UpdateTaskNameMutation = (
export const AssignTaskDocument = gql` export const AssignTaskDocument = gql`
mutation assignTask($taskID: UUID!, $userID: UUID!) { mutation assignTask($taskID: UUID!, $userID: UUID!) {
assignTask(input: {taskID: $taskID, userID: $userID}) { assignTask(input: {taskID: $taskID, userID: $userID}) {
id
assigned { assigned {
userID id
firstName firstName
lastName lastName
} }
taskID
} }
} }
`; `;
@ -662,15 +687,63 @@ 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 CreateProjectLabelDocument = gql`
mutation createProjectLabel($projectID: UUID!, $labelColorID: UUID!, $name: String!) {
createProjectLabel(input: {projectID: $projectID, labelColorID: $labelColorID, name: $name}) {
id
createdDate
colorHex
name
}
}
`;
export type CreateProjectLabelMutationFn = ApolloReactCommon.MutationFunction<CreateProjectLabelMutation, CreateProjectLabelMutationVariables>;
/**
* __useCreateProjectLabelMutation__
*
* To run a mutation, you first call `useCreateProjectLabelMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useCreateProjectLabelMutation` 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 [createProjectLabelMutation, { data, loading, error }] = useCreateProjectLabelMutation({
* variables: {
* projectID: // value for 'projectID'
* labelColorID: // value for 'labelColorID'
* name: // value for 'name'
* },
* });
*/
export function useCreateProjectLabelMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateProjectLabelMutation, CreateProjectLabelMutationVariables>) {
return ApolloReactHooks.useMutation<CreateProjectLabelMutation, CreateProjectLabelMutationVariables>(CreateProjectLabelDocument, baseOptions);
}
export type CreateProjectLabelMutationHookResult = ReturnType<typeof useCreateProjectLabelMutation>;
export type CreateProjectLabelMutationResult = ApolloReactCommon.MutationResult<CreateProjectLabelMutation>;
export type CreateProjectLabelMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateProjectLabelMutation, CreateProjectLabelMutationVariables>;
export const CreateTaskDocument = gql` export const CreateTaskDocument = gql`
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) { mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) { createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) {
taskID id
taskGroup {
taskGroupID
}
name name
position position
description
taskGroup {
id
}
assigned {
id
firstName
lastName
profileIcon {
url
initials
bgColor
}
}
} }
} }
`; `;
@ -704,7 +777,7 @@ export type CreateTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<Cr
export const CreateTaskGroupDocument = gql` export const CreateTaskGroupDocument = gql`
mutation createTaskGroup($projectID: String!, $name: String!, $position: Float!) { mutation createTaskGroup($projectID: String!, $name: String!, $position: Float!) {
createTaskGroup(input: {projectID: $projectID, name: $name, position: $position}) { createTaskGroup(input: {projectID: $projectID, name: $name, position: $position}) {
taskGroupID id
name name
position position
} }
@ -775,9 +848,9 @@ export const DeleteTaskGroupDocument = gql`
ok ok
affectedRows affectedRows
taskGroup { taskGroup {
taskGroupID id
tasks { tasks {
taskID id
name name
} }
} }
@ -814,7 +887,7 @@ export const FindProjectDocument = gql`
findProject(input: {projectId: $projectId}) { findProject(input: {projectId: $projectId}) {
name name
members { members {
userID id
firstName firstName
lastName lastName
profileIcon { profileIcon {
@ -823,17 +896,23 @@ export const FindProjectDocument = gql`
bgColor bgColor
} }
} }
labels {
id
createdDate
colorHex
name
}
taskGroups { taskGroups {
taskGroupID id
name name
position position
tasks { tasks {
taskID id
name name
position position
description description
assigned { assigned {
userID id
firstName firstName
lastName lastName
profileIcon { profileIcon {
@ -876,15 +955,15 @@ export type FindProjectQueryResult = ApolloReactCommon.QueryResult<FindProjectQu
export const FindTaskDocument = gql` export const FindTaskDocument = gql`
query findTask($taskID: UUID!) { query findTask($taskID: UUID!) {
findTask(input: {taskID: $taskID}) { findTask(input: {taskID: $taskID}) {
taskID id
name name
description description
position position
taskGroup { taskGroup {
taskGroupID id
} }
assigned { assigned {
userID id
firstName firstName
lastName lastName
profileIcon { profileIcon {
@ -925,10 +1004,10 @@ export type FindTaskQueryResult = ApolloReactCommon.QueryResult<FindTaskQuery, F
export const GetProjectsDocument = gql` export const GetProjectsDocument = gql`
query getProjects { query getProjects {
projects { projects {
projectID id
name name
team { team {
teamID id
name name
} }
} }
@ -1000,11 +1079,11 @@ export const UnassignTaskDocument = gql`
mutation unassignTask($taskID: UUID!, $userID: UUID!) { mutation unassignTask($taskID: UUID!, $userID: UUID!) {
unassignTask(input: {taskID: $taskID, userID: $userID}) { unassignTask(input: {taskID: $taskID, userID: $userID}) {
assigned { assigned {
userID id
firstName firstName
lastName lastName
} }
taskID id
} }
} }
`; `;
@ -1037,7 +1116,7 @@ export type UnassignTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<
export const UpdateTaskDescriptionDocument = gql` export const UpdateTaskDescriptionDocument = gql`
mutation updateTaskDescription($taskID: UUID!, $description: String!) { mutation updateTaskDescription($taskID: UUID!, $description: String!) {
updateTaskDescription(input: {taskID: $taskID, description: $description}) { updateTaskDescription(input: {taskID: $taskID, description: $description}) {
taskID id
} }
} }
`; `;
@ -1070,7 +1149,7 @@ export type UpdateTaskDescriptionMutationOptions = ApolloReactCommon.BaseMutatio
export const UpdateTaskGroupLocationDocument = gql` export const UpdateTaskGroupLocationDocument = gql`
mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) { mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) {
updateTaskGroupLocation(input: {taskGroupID: $taskGroupID, position: $position}) { updateTaskGroupLocation(input: {taskGroupID: $taskGroupID, position: $position}) {
taskGroupID id
position position
} }
} }
@ -1104,7 +1183,7 @@ export type UpdateTaskGroupLocationMutationOptions = ApolloReactCommon.BaseMutat
export const UpdateTaskLocationDocument = gql` export const UpdateTaskLocationDocument = gql`
mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) { mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) {
updateTaskLocation(input: {taskID: $taskID, taskGroupID: $taskGroupID, position: $position}) { updateTaskLocation(input: {taskID: $taskID, taskGroupID: $taskGroupID, position: $position}) {
taskID id
createdAt createdAt
name name
position position
@ -1141,7 +1220,7 @@ export type UpdateTaskLocationMutationOptions = ApolloReactCommon.BaseMutationOp
export const UpdateTaskNameDocument = gql` export const UpdateTaskNameDocument = gql`
mutation updateTaskName($taskID: String!, $name: String!) { mutation updateTaskName($taskID: String!, $name: String!) {
updateTaskName(input: {taskID: $taskID, name: $name}) { updateTaskName(input: {taskID: $taskID, name: $name}) {
taskID id
name name
position position
} }

View File

@ -1,10 +1,10 @@
mutation assignTask($taskID: UUID!, $userID: UUID!) { mutation assignTask($taskID: UUID!, $userID: UUID!) {
assignTask(input: {taskID: $taskID, userID: $userID}) { assignTask(input: {taskID: $taskID, userID: $userID}) {
id
assigned { assigned {
userID id
firstName firstName
lastName lastName
} }
taskID
} }
} }

View File

@ -0,0 +1,8 @@
mutation createProjectLabel($projectID: UUID!, $labelColorID: UUID!, $name: String!) {
createProjectLabel(input:{projectID:$projectID, labelColorID: $labelColorID, name: $name}) {
id
createdDate
colorHex
name
}
}

View File

@ -1,10 +1,21 @@
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) { mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) { createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) {
taskID id
taskGroup {
taskGroupID
}
name name
position position
description
taskGroup {
id
}
assigned {
id
firstName
lastName
profileIcon {
url
initials
bgColor
}
}
} }
} }

View File

@ -2,7 +2,7 @@ mutation createTaskGroup( $projectID: String!, $name: String!, $position: Float!
createTaskGroup( createTaskGroup(
input: { projectID: $projectID, name: $name, position: $position } input: { projectID: $projectID, name: $name, position: $position }
) { ) {
taskGroupID id
name name
position position
} }

View File

@ -3,9 +3,9 @@ mutation deleteTaskGroup($taskGroupID: UUID!) {
ok ok
affectedRows affectedRows
taskGroup { taskGroup {
taskGroupID id
tasks { tasks {
taskID id
name name
} }
} }

View File

@ -2,7 +2,7 @@ query findProject($projectId: String!) {
findProject(input: { projectId: $projectId }) { findProject(input: { projectId: $projectId }) {
name name
members { members {
userID id
firstName firstName
lastName lastName
profileIcon { profileIcon {
@ -11,17 +11,23 @@ query findProject($projectId: String!) {
bgColor bgColor
} }
} }
labels {
id
createdDate
colorHex
name
}
taskGroups { taskGroups {
taskGroupID id
name name
position position
tasks { tasks {
taskID id
name name
position position
description description
assigned { assigned {
userID id
firstName firstName
lastName lastName
profileIcon { profileIcon {

View File

@ -1,14 +1,14 @@
query findTask($taskID: UUID!) { query findTask($taskID: UUID!) {
findTask(input: {taskID: $taskID}) { findTask(input: {taskID: $taskID}) {
taskID id
name name
description description
position position
taskGroup { taskGroup {
taskGroupID id
} }
assigned { assigned {
userID id
firstName firstName
lastName lastName
profileIcon { profileIcon {

View File

@ -1,9 +1,9 @@
query getProjects { query getProjects {
projects { projects {
projectID id
name name
team { team {
teamID id
name name
} }
} }

View File

@ -1,10 +1,10 @@
mutation unassignTask($taskID: UUID!, $userID: UUID!) { mutation unassignTask($taskID: UUID!, $userID: UUID!) {
unassignTask(input: {taskID: $taskID, userID: $userID}) { unassignTask(input: {taskID: $taskID, userID: $userID}) {
assigned { assigned {
userID id
firstName firstName
lastName lastName
} }
taskID id
} }
} }

View File

@ -1,5 +1,5 @@
mutation updateTaskDescription($taskID: UUID!, $description: String!) { mutation updateTaskDescription($taskID: UUID!, $description: String!) {
updateTaskDescription(input: {taskID: $taskID, description: $description}) { updateTaskDescription(input: {taskID: $taskID, description: $description}) {
taskID id
} }
} }

View File

@ -1,6 +1,6 @@
mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) { mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) {
updateTaskGroupLocation(input:{taskGroupID:$taskGroupID, position: $position}) { updateTaskGroupLocation(input:{taskGroupID:$taskGroupID, position: $position}) {
taskGroupID id
position position
} }
} }

View File

@ -1,6 +1,6 @@
mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) { mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) {
updateTaskLocation(input: { taskID: $taskID, taskGroupID: $taskGroupID, position: $position }) { updateTaskLocation(input: { taskID: $taskID, taskGroupID: $taskGroupID, position: $position }) {
taskID id
createdAt createdAt
name name
position position

View File

@ -1,6 +1,6 @@
mutation updateTaskName($taskID: String!, $name: String!) { mutation updateTaskName($taskID: String!, $name: String!) {
updateTaskName(input: { taskID: $taskID, name: $name }) { updateTaskName(input: { taskID: $taskID, name: $name }) {
taskID id
name name
position position
} }

View File

@ -0,0 +1,27 @@
import React from 'react';
type Props = {
width: number | string;
height: number | string;
color: string;
};
const AngleDown = ({ width, height, color }: Props) => {
return (
<svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path
fill={color}
d="M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z"
/>
</svg>
);
};
AngleDown.defaultProps = {
width: 24,
height: 16,
color: '#000',
};
export default AngleDown;

View File

@ -0,0 +1,24 @@
import React from 'react';
type Props = {
size: number | string;
color: string;
};
const AngleLeft = ({ size, color }: Props) => {
return (
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512">
<path
fill={color}
d="M31.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L127.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L201.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34z"
/>
</svg>
);
};
AngleLeft.defaultProps = {
size: 16,
color: '#000',
};
export default AngleLeft;

View File

@ -7,8 +7,8 @@ type Props = {
const Bell = ({ size, color }: Props) => { const Bell = ({ size, color }: Props) => {
return ( return (
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16"> <svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 448 512">
<path d="M16.023 12.5c0-4.5-4-3.5-4-7 0-0.29-0.028-0.538-0.079-0.749-0.263-1.766-1.44-3.183-2.965-3.615 0.014-0.062 0.021-0.125 0.021-0.191 0-0.52-0.45-0.945-1-0.945s-1 0.425-1 0.945c0 0.065 0.007 0.129 0.021 0.191-1.71 0.484-2.983 2.208-3.020 4.273-0.001 0.030-0.001 0.060-0.001 0.091 0 3.5-4 2.5-4 7 0 1.191 2.665 2.187 6.234 2.439 0.336 0.631 1.001 1.061 1.766 1.061s1.43-0.43 1.766-1.061c3.568-0.251 6.234-1.248 6.234-2.439 0-0.004-0-0.007-0-0.011l0.024 0.011zM12.91 13.345c-0.847 0.226-1.846 0.389-2.918 0.479-0.089-1.022-0.947-1.824-1.992-1.824s-1.903 0.802-1.992 1.824c-1.072-0.090-2.071-0.253-2.918-0.479-1.166-0.311-1.724-0.659-1.928-0.845 0.204-0.186 0.762-0.534 1.928-0.845 1.356-0.362 3.1-0.561 4.91-0.561s3.554 0.199 4.91 0.561c1.166 0.311 1.724 0.659 1.928 0.845-0.204 0.186-0.762 0.534-1.928 0.845z" /> <path d="M439.39 362.29c-19.32-20.76-55.47-51.99-55.47-154.29 0-77.7-54.48-139.9-127.94-155.16V32c0-17.67-14.32-32-31.98-32s-31.98 14.33-31.98 32v20.84C118.56 68.1 64.08 130.3 64.08 208c0 102.3-36.15 133.53-55.47 154.29-6 6.45-8.66 14.16-8.61 21.71.11 16.4 12.98 32 32.1 32h383.8c19.12 0 32-15.6 32.1-32 .05-7.55-2.61-15.27-8.61-21.71zM67.53 368c21.22-27.97 44.42-74.33 44.53-159.42 0-.2-.06-.38-.06-.58 0-61.86 50.14-112 112-112s112 50.14 112 112c0 .2-.06.38-.06.58.11 85.1 23.31 131.46 44.53 159.42H67.53zM224 512c35.32 0 63.97-28.65 63.97-64H160.03c0 35.35 28.65 64 63.97 64z" />
</svg> </svg>
); );
}; };

View File

@ -0,0 +1,24 @@
import React from 'react';
type Props = {
size: number | string;
color: string;
};
const Bolt = ({ size, color }: Props) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" width={size} height={size}>
<path
fill={color}
d="M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z"
/>
</svg>
);
};
Bolt.defaultProps = {
size: 16,
color: '#000',
};
export default Bolt;

View File

@ -0,0 +1,24 @@
import React from 'react';
type Props = {
size: number | string;
color: string;
};
const Cog = ({ size, color }: Props) => {
return (
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path
fill={color}
d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"
/>
</svg>
);
};
Cog.defaultProps = {
size: 16,
color: '#000',
};
export default Cog;

View File

@ -7,8 +7,8 @@ type Props = {
const Cross = ({ size, color }: Props) => { const Cross = ({ size, color }: Props) => {
return ( return (
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16"> <svg fill={color} width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512">
<path d="M15.854 12.854c-0-0-0-0-0-0l-4.854-4.854 4.854-4.854c0-0 0-0 0-0 0.052-0.052 0.090-0.113 0.114-0.178 0.066-0.178 0.028-0.386-0.114-0.529l-2.293-2.293c-0.143-0.143-0.351-0.181-0.529-0.114-0.065 0.024-0.126 0.062-0.178 0.114 0 0-0 0-0 0l-4.854 4.854-4.854-4.854c-0-0-0-0-0-0-0.052-0.052-0.113-0.090-0.178-0.114-0.178-0.066-0.386-0.029-0.529 0.114l-2.293 2.293c-0.143 0.143-0.181 0.351-0.114 0.529 0.024 0.065 0.062 0.126 0.114 0.178 0 0 0 0 0 0l4.854 4.854-4.854 4.854c-0 0-0 0-0 0-0.052 0.052-0.090 0.113-0.114 0.178-0.066 0.178-0.029 0.386 0.114 0.529l2.293 2.293c0.143 0.143 0.351 0.181 0.529 0.114 0.065-0.024 0.126-0.062 0.178-0.114 0-0 0-0 0-0l4.854-4.854 4.854 4.854c0 0 0 0 0 0 0.052 0.052 0.113 0.090 0.178 0.114 0.178 0.066 0.386 0.029 0.529-0.114l2.293-2.293c0.143-0.143 0.181-0.351 0.114-0.529-0.024-0.065-0.062-0.126-0.114-0.178z" /> <path d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" />
</svg> </svg>
); );
}; };

View File

@ -0,0 +1,36 @@
import React from 'react';
type Props = {
width: number | string;
height: number | string;
color: string;
filled: boolean;
};
const Star = ({ width, height, color, filled }: Props) => {
return (
<svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
{filled ? (
<path
fill={color}
d="M259.3 17.8L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0z"
/>
) : (
<path
fill={color}
d="M528.1 171.5L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6zM388.6 312.3l23.7 138.4L288 385.4l-124.3 65.3 23.7-138.4-100.6-98 139-20.2 62.2-126 62.2 126 139 20.2-100.6 98z"
/>
)}
</svg>
);
};
Star.defaultProps = {
width: 24,
height: 16,
color: '#000',
filled: false,
};
export default Star;

View File

@ -0,0 +1,24 @@
import React from 'react';
type Props = {
size: number | string;
color: string;
};
const Tags = ({ size, color }: Props) => {
return (
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
<path
fill={color}
d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"
/>
</svg>
);
};
Tags.defaultProps = {
size: 16,
color: '#000',
};
export default Tags;

View File

@ -0,0 +1,24 @@
import React from 'react';
type Props = {
size: number | string;
color: string;
};
const ToggleOn = ({ size, color }: Props) => {
return (
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
<path
fill={color}
d="M384 64H192C86 64 0 150 0 256s86 192 192 192h192c106 0 192-86 192-192S490 64 384 64zm0 320c-70.8 0-128-57.3-128-128 0-70.8 57.3-128 128-128 70.8 0 128 57.3 128 128 0 70.8-57.3 128-128 128z"
/>
</svg>
);
};
ToggleOn.defaultProps = {
size: 16,
color: '#000',
};
export default ToggleOn;

View File

@ -1,6 +1,10 @@
import Cross from './Cross'; import Cross from './Cross';
import Cog from './Cog';
import Bolt from './Bolt';
import Plus from './Plus'; import Plus from './Plus';
import Bell from './Bell'; import Bell from './Bell';
import AngleLeft from './AngleLeft';
import AngleDown from './AngleDown';
import Bin from './Bin'; import Bin from './Bin';
import Pencil from './Pencil'; import Pencil from './Pencil';
import Checkmark from './Checkmark'; import Checkmark from './Checkmark';
@ -13,5 +17,31 @@ import Stack from './Stack';
import Question from './Question'; import Question from './Question';
import Exit from './Exit'; import Exit from './Exit';
import Ellipsis from './Ellipsis'; import Ellipsis from './Ellipsis';
import ToggleOn from './ToggleOn';
import Tags from './Tags';
import Star from './Star';
export { Cross, Plus, Bell, Ellipsis, Bin, Exit, Pencil, Stack, Question, Home, Citadel, Checkmark, User, Users, Lock }; export {
Star,
AngleDown,
Cross,
Cog,
Bolt,
Plus,
Bell,
AngleLeft,
Tags,
Ellipsis,
Bin,
Exit,
Pencil,
Stack,
Question,
Home,
Citadel,
Checkmark,
User,
Users,
Lock,
ToggleOn,
};

View File

@ -2,7 +2,7 @@ import produce from 'immer';
export const addTask = (currentState: BoardState, newTask: Task) => { export const addTask = (currentState: BoardState, newTask: Task) => {
return produce(currentState, (draftState: BoardState) => { return produce(currentState, (draftState: BoardState) => {
currentState.tasks[newTask.taskID] = newTask; draftState.tasks[newTask.taskID] = newTask;
}); });
}; };