feature: add more to project pane
This commit is contained in:
parent
7e78ee36b4
commit
fba4de631f
@ -41,9 +41,11 @@ type ResolverRoot interface {
|
||||
Project() ProjectResolver
|
||||
ProjectLabel() ProjectLabelResolver
|
||||
Query() QueryResolver
|
||||
RefreshToken() RefreshTokenResolver
|
||||
Task() TaskResolver
|
||||
TaskGroup() TaskGroupResolver
|
||||
TaskLabel() TaskLabelResolver
|
||||
Team() TeamResolver
|
||||
UserAccount() UserAccountResolver
|
||||
}
|
||||
|
||||
@ -90,11 +92,11 @@ type ComplexityRoot struct {
|
||||
|
||||
Project struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Labels func(childComplexity int) int
|
||||
Members func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Owner func(childComplexity int) int
|
||||
ProjectID func(childComplexity int) int
|
||||
TaskGroups func(childComplexity int) int
|
||||
Team func(childComplexity int) int
|
||||
}
|
||||
@ -102,15 +104,15 @@ type ComplexityRoot struct {
|
||||
ProjectLabel struct {
|
||||
ColorHex func(childComplexity int) int
|
||||
CreatedDate func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
ProjectLabelID func(childComplexity int) int
|
||||
}
|
||||
|
||||
ProjectMember struct {
|
||||
FirstName func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
LastName func(childComplexity int) int
|
||||
ProfileIcon func(childComplexity int) int
|
||||
UserID func(childComplexity int) int
|
||||
}
|
||||
|
||||
Query struct {
|
||||
@ -126,7 +128,7 @@ type ComplexityRoot struct {
|
||||
RefreshToken struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
ExpiresAt func(childComplexity int) int
|
||||
TokenID func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
UserID func(childComplexity int) int
|
||||
}
|
||||
|
||||
@ -134,43 +136,43 @@ type ComplexityRoot struct {
|
||||
Assigned func(childComplexity int) int
|
||||
CreatedAt func(childComplexity int) int
|
||||
Description func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Labels func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Position func(childComplexity int) int
|
||||
TaskGroup func(childComplexity int) int
|
||||
TaskID func(childComplexity int) int
|
||||
}
|
||||
|
||||
TaskGroup struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
Position func(childComplexity int) int
|
||||
ProjectID func(childComplexity int) int
|
||||
TaskGroupID func(childComplexity int) int
|
||||
Tasks func(childComplexity int) int
|
||||
}
|
||||
|
||||
TaskLabel struct {
|
||||
AssignedDate func(childComplexity int) int
|
||||
ColorHex func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
ProjectLabelID func(childComplexity int) int
|
||||
TaskLabelID func(childComplexity int) int
|
||||
}
|
||||
|
||||
Team struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
TeamID func(childComplexity int) int
|
||||
}
|
||||
|
||||
UserAccount struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
Email func(childComplexity int) int
|
||||
FirstName func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
LastName func(childComplexity int) int
|
||||
ProfileIcon func(childComplexity int) int
|
||||
UserID func(childComplexity int) int
|
||||
Username func(childComplexity int) int
|
||||
}
|
||||
}
|
||||
@ -196,6 +198,8 @@ type MutationResolver interface {
|
||||
LogoutUser(ctx context.Context, input LogoutUser) (bool, error)
|
||||
}
|
||||
type ProjectResolver interface {
|
||||
ID(ctx context.Context, obj *pg.Project) (uuid.UUID, error)
|
||||
|
||||
Team(ctx context.Context, obj *pg.Project) (*pg.Team, error)
|
||||
Owner(ctx context.Context, obj *pg.Project) (*ProjectMember, 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)
|
||||
}
|
||||
type ProjectLabelResolver interface {
|
||||
ID(ctx context.Context, obj *pg.ProjectLabel) (uuid.UUID, error)
|
||||
|
||||
ColorHex(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)
|
||||
Me(ctx context.Context) (*pg.UserAccount, error)
|
||||
}
|
||||
type RefreshTokenResolver interface {
|
||||
ID(ctx context.Context, obj *pg.RefreshToken) (uuid.UUID, error)
|
||||
}
|
||||
type TaskResolver interface {
|
||||
ID(ctx context.Context, obj *pg.Task) (uuid.UUID, error)
|
||||
TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, 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)
|
||||
}
|
||||
type TaskGroupResolver interface {
|
||||
ID(ctx context.Context, obj *pg.TaskGroup) (uuid.UUID, error)
|
||||
ProjectID(ctx context.Context, obj *pg.TaskGroup) (string, error)
|
||||
|
||||
Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.Task, error)
|
||||
}
|
||||
type TaskLabelResolver interface {
|
||||
ID(ctx context.Context, obj *pg.TaskLabel) (uuid.UUID, error)
|
||||
|
||||
ColorHex(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 {
|
||||
ID(ctx context.Context, obj *pg.UserAccount) (uuid.UUID, 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
|
||||
|
||||
case "Project.id":
|
||||
if e.complexity.Project.ID == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Project.ID(childComplexity), true
|
||||
|
||||
case "Project.labels":
|
||||
if e.complexity.Project.Labels == nil {
|
||||
break
|
||||
@ -550,13 +575,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.Project.TaskGroups == nil {
|
||||
break
|
||||
@ -585,6 +603,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.ProjectLabel.Name == nil {
|
||||
break
|
||||
@ -592,13 +617,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.ProjectMember.FirstName == nil {
|
||||
break
|
||||
@ -606,6 +624,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.ProjectMember.LastName == nil {
|
||||
break
|
||||
@ -620,13 +645,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.Query.FindProject == nil {
|
||||
break
|
||||
@ -710,12 +728,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.RefreshToken.ExpiresAt(childComplexity), true
|
||||
|
||||
case "RefreshToken.tokenId":
|
||||
if e.complexity.RefreshToken.TokenID == nil {
|
||||
case "RefreshToken.id":
|
||||
if e.complexity.RefreshToken.ID == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.RefreshToken.TokenID(childComplexity), true
|
||||
return e.complexity.RefreshToken.ID(childComplexity), true
|
||||
|
||||
case "RefreshToken.userId":
|
||||
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
|
||||
|
||||
case "Task.id":
|
||||
if e.complexity.Task.ID == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Task.ID(childComplexity), true
|
||||
|
||||
case "Task.labels":
|
||||
if e.complexity.Task.Labels == nil {
|
||||
break
|
||||
@ -773,13 +798,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.TaskGroup.CreatedAt == nil {
|
||||
break
|
||||
@ -787,6 +805,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.TaskGroup.Name == nil {
|
||||
break
|
||||
@ -808,13 +833,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.TaskGroup.Tasks == nil {
|
||||
break
|
||||
@ -836,6 +854,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.TaskLabel.Name == nil {
|
||||
break
|
||||
@ -850,13 +875,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.Team.CreatedAt == nil {
|
||||
break
|
||||
@ -864,6 +882,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.Team.Name == nil {
|
||||
break
|
||||
@ -871,13 +896,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.UserAccount.CreatedAt == nil {
|
||||
break
|
||||
@ -899,6 +917,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.UserAccount.LastName == nil {
|
||||
break
|
||||
@ -913,13 +938,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.UserAccount.Username == nil {
|
||||
break
|
||||
@ -995,14 +1013,14 @@ var sources = []*ast.Source{
|
||||
scalar UUID
|
||||
|
||||
type ProjectLabel {
|
||||
projectLabelID: ID!
|
||||
id: ID!
|
||||
createdDate: Time!
|
||||
colorHex: String!
|
||||
name: String
|
||||
}
|
||||
|
||||
type TaskLabel {
|
||||
taskLabelID: ID!
|
||||
id: ID!
|
||||
projectLabelID: UUID!
|
||||
assignedDate: Time!
|
||||
colorHex: String!
|
||||
@ -1016,21 +1034,21 @@ type ProfileIcon {
|
||||
}
|
||||
|
||||
type ProjectMember {
|
||||
userID: ID!
|
||||
id: ID!
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
profileIcon: ProfileIcon!
|
||||
}
|
||||
|
||||
type RefreshToken {
|
||||
tokenId: ID!
|
||||
id: ID!
|
||||
userId: UUID!
|
||||
expiresAt: Time!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type UserAccount {
|
||||
userID: ID!
|
||||
id: ID!
|
||||
email: String!
|
||||
createdAt: Time!
|
||||
firstName: String!
|
||||
@ -1040,13 +1058,13 @@ type UserAccount {
|
||||
}
|
||||
|
||||
type Team {
|
||||
teamID: ID!
|
||||
id: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Project {
|
||||
projectID: ID!
|
||||
id: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
team: Team!
|
||||
@ -1057,7 +1075,7 @@ type Project {
|
||||
}
|
||||
|
||||
type TaskGroup {
|
||||
taskGroupID: ID!
|
||||
id: ID!
|
||||
projectID: String!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
@ -1066,7 +1084,7 @@ type TaskGroup {
|
||||
}
|
||||
|
||||
type Task {
|
||||
taskID: ID!
|
||||
id: ID!
|
||||
taskGroup: TaskGroup!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
@ -2563,7 +2581,7 @@ func (ec *executionContext) _ProfileIcon_bgColor(ctx context.Context, field grap
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -2574,13 +2592,13 @@ func (ec *executionContext) _Project_projectID(ctx context.Context, field graphq
|
||||
Object: "Project",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ProjectID, nil
|
||||
return ec.resolvers.Project().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -2846,13 +2864,13 @@ func (ec *executionContext) _ProjectLabel_projectLabelID(ctx context.Context, fi
|
||||
Object: "ProjectLabel",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ProjectLabelID, nil
|
||||
return ec.resolvers.ProjectLabel().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
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)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.UserID, nil
|
||||
return obj.ID, nil
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -3450,13 +3468,13 @@ func (ec *executionContext) _RefreshToken_tokenId(ctx context.Context, field gra
|
||||
Object: "RefreshToken",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.TokenID, nil
|
||||
return ec.resolvers.RefreshToken().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -3586,13 +3604,13 @@ func (ec *executionContext) _Task_taskID(ctx context.Context, field graphql.Coll
|
||||
Object: "Task",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.TaskID, nil
|
||||
return ec.resolvers.Task().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -3855,13 +3873,13 @@ func (ec *executionContext) _TaskGroup_taskGroupID(ctx context.Context, field gr
|
||||
Object: "TaskGroup",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.TaskGroupID, nil
|
||||
return ec.resolvers.TaskGroup().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -4059,13 +4077,13 @@ func (ec *executionContext) _TaskLabel_taskLabelID(ctx context.Context, field gr
|
||||
Object: "TaskLabel",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.TaskLabelID, nil
|
||||
return ec.resolvers.TaskLabel().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -4226,13 +4244,13 @@ func (ec *executionContext) _Team_teamID(ctx context.Context, field graphql.Coll
|
||||
Object: "Team",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.TeamID, nil
|
||||
return ec.resolvers.Team().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
@ -4328,13 +4346,13 @@ func (ec *executionContext) _UserAccount_userID(ctx context.Context, field graph
|
||||
Object: "UserAccount",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsMethod: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.UserID, nil
|
||||
return ec.resolvers.UserAccount().ID(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
@ -6365,11 +6383,20 @@ func (ec *executionContext) _Project(ctx context.Context, sel ast.SelectionSet,
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Project")
|
||||
case "projectID":
|
||||
out.Values[i] = ec._Project_projectID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Project_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "createdAt":
|
||||
out.Values[i] = ec._Project_createdAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
@ -6472,11 +6499,20 @@ func (ec *executionContext) _ProjectLabel(ctx context.Context, sel ast.Selection
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("ProjectLabel")
|
||||
case "projectLabelID":
|
||||
out.Values[i] = ec._ProjectLabel_projectLabelID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._ProjectLabel_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "createdDate":
|
||||
out.Values[i] = ec._ProjectLabel_createdDate(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
@ -6529,8 +6565,8 @@ func (ec *executionContext) _ProjectMember(ctx context.Context, sel ast.Selectio
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("ProjectMember")
|
||||
case "userID":
|
||||
out.Values[i] = ec._ProjectMember_userID(ctx, field, obj)
|
||||
case "id":
|
||||
out.Values[i] = ec._ProjectMember_id(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
@ -6699,25 +6735,34 @@ func (ec *executionContext) _RefreshToken(ctx context.Context, sel ast.Selection
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("RefreshToken")
|
||||
case "tokenId":
|
||||
out.Values[i] = ec._RefreshToken_tokenId(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._RefreshToken_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "userId":
|
||||
out.Values[i] = ec._RefreshToken_userId(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "expiresAt":
|
||||
out.Values[i] = ec._RefreshToken_expiresAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "createdAt":
|
||||
out.Values[i] = ec._RefreshToken_createdAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
default:
|
||||
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 {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Task")
|
||||
case "taskID":
|
||||
out.Values[i] = ec._Task_taskID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Task_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "taskGroup":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
@ -6836,11 +6890,20 @@ func (ec *executionContext) _TaskGroup(ctx context.Context, sel ast.SelectionSet
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("TaskGroup")
|
||||
case "taskGroupID":
|
||||
out.Values[i] = ec._TaskGroup_taskGroupID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._TaskGroup_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "projectID":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
@ -6906,11 +6969,20 @@ func (ec *executionContext) _TaskLabel(ctx context.Context, sel ast.SelectionSet
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("TaskLabel")
|
||||
case "taskLabelID":
|
||||
out.Values[i] = ec._TaskLabel_taskLabelID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._TaskLabel_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "projectLabelID":
|
||||
out.Values[i] = ec._TaskLabel_projectLabelID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
@ -6968,20 +7040,29 @@ func (ec *executionContext) _Team(ctx context.Context, sel ast.SelectionSet, obj
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("Team")
|
||||
case "teamID":
|
||||
out.Values[i] = ec._Team_teamID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Team_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "createdAt":
|
||||
out.Values[i] = ec._Team_createdAt(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "name":
|
||||
out.Values[i] = ec._Team_name(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
@ -7005,11 +7086,20 @@ func (ec *executionContext) _UserAccount(ctx context.Context, sel ast.SelectionS
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("UserAccount")
|
||||
case "userID":
|
||||
out.Values[i] = ec._UserAccount_userID(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
case "id":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._UserAccount_id(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "email":
|
||||
out.Values[i] = ec._UserAccount_email(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
|
@ -110,7 +110,7 @@ type ProfileIcon struct {
|
||||
}
|
||||
|
||||
type ProjectMember struct {
|
||||
UserID uuid.UUID `json:"userID"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
FirstName string `json:"firstName"`
|
||||
LastName string `json:"lastName"`
|
||||
ProfileIcon *ProfileIcon `json:"profileIcon"`
|
||||
|
@ -2,14 +2,14 @@ scalar Time
|
||||
scalar UUID
|
||||
|
||||
type ProjectLabel {
|
||||
projectLabelID: ID!
|
||||
id: ID!
|
||||
createdDate: Time!
|
||||
colorHex: String!
|
||||
name: String
|
||||
}
|
||||
|
||||
type TaskLabel {
|
||||
taskLabelID: ID!
|
||||
id: ID!
|
||||
projectLabelID: UUID!
|
||||
assignedDate: Time!
|
||||
colorHex: String!
|
||||
@ -23,21 +23,21 @@ type ProfileIcon {
|
||||
}
|
||||
|
||||
type ProjectMember {
|
||||
userID: ID!
|
||||
id: ID!
|
||||
firstName: String!
|
||||
lastName: String!
|
||||
profileIcon: ProfileIcon!
|
||||
}
|
||||
|
||||
type RefreshToken {
|
||||
tokenId: ID!
|
||||
id: ID!
|
||||
userId: UUID!
|
||||
expiresAt: Time!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type UserAccount {
|
||||
userID: ID!
|
||||
id: ID!
|
||||
email: String!
|
||||
createdAt: Time!
|
||||
firstName: String!
|
||||
@ -47,13 +47,13 @@ type UserAccount {
|
||||
}
|
||||
|
||||
type Team {
|
||||
teamID: ID!
|
||||
id: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type Project {
|
||||
projectID: ID!
|
||||
id: ID!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
team: Team!
|
||||
@ -64,7 +64,7 @@ type Project {
|
||||
}
|
||||
|
||||
type TaskGroup {
|
||||
taskGroupID: ID!
|
||||
id: ID!
|
||||
projectID: String!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
@ -73,7 +73,7 @@ type TaskGroup {
|
||||
}
|
||||
|
||||
type Task {
|
||||
taskID: ID!
|
||||
id: ID!
|
||||
taskGroup: TaskGroup!
|
||||
createdAt: Time!
|
||||
name: String!
|
||||
|
@ -190,6 +190,10 @@ func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bo
|
||||
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) {
|
||||
team, err := r.Repository.GetTeamByID(ctx, obj.TeamID)
|
||||
return &team, err
|
||||
@ -226,6 +230,10 @@ func (r *projectResolver) Labels(ctx context.Context, obj *pg.Project) ([]pg.Pro
|
||||
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) {
|
||||
labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID)
|
||||
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) {
|
||||
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) {
|
||||
@ -311,6 +323,14 @@ func (r *queryResolver) Me(ctx context.Context) (*pg.UserAccount, error) {
|
||||
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) {
|
||||
taskGroup, err := r.Repository.GetTaskGroupByID(ctx, obj.TaskGroupID)
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
return obj.ProjectID.String(), nil
|
||||
}
|
||||
@ -358,6 +382,10 @@ func (r *taskGroupResolver) Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.
|
||||
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) {
|
||||
projectLabel, err := r.Repository.GetProjectLabelByID(ctx, obj.ProjectLabelID)
|
||||
if err != nil {
|
||||
@ -382,6 +410,14 @@ func (r *taskLabelResolver) Name(ctx context.Context, obj *pg.TaskLabel) (*strin
|
||||
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) {
|
||||
initials := string([]rune(obj.FirstName)[0]) + string([]rune(obj.LastName)[0])
|
||||
profileIcon := &ProfileIcon{nil, &initials, &obj.ProfileBgColor}
|
||||
@ -400,6 +436,9 @@ func (r *Resolver) ProjectLabel() ProjectLabelResolver { return &projectLabelRes
|
||||
// Query returns QueryResolver implementation.
|
||||
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
||||
|
||||
// RefreshToken returns RefreshTokenResolver implementation.
|
||||
func (r *Resolver) RefreshToken() RefreshTokenResolver { return &refreshTokenResolver{r} }
|
||||
|
||||
// Task returns TaskResolver implementation.
|
||||
func (r *Resolver) Task() TaskResolver { return &taskResolver{r} }
|
||||
|
||||
@ -409,6 +448,9 @@ func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r}
|
||||
// TaskLabel returns TaskLabelResolver implementation.
|
||||
func (r *Resolver) TaskLabel() TaskLabelResolver { return &taskLabelResolver{r} }
|
||||
|
||||
// Team returns TeamResolver implementation.
|
||||
func (r *Resolver) Team() TeamResolver { return &teamResolver{r} }
|
||||
|
||||
// UserAccount returns UserAccountResolver implementation.
|
||||
func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolver{r} }
|
||||
|
||||
@ -416,20 +458,9 @@ type mutationResolver struct{ *Resolver }
|
||||
type projectResolver struct{ *Resolver }
|
||||
type projectLabelResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type refreshTokenResolver struct{ *Resolver }
|
||||
type taskResolver struct{ *Resolver }
|
||||
type taskGroupResolver struct{ *Resolver }
|
||||
type taskLabelResolver struct{ *Resolver }
|
||||
type teamResolver 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
|
||||
}
|
||||
|
7
api/migrations/0017_add-profile-bg-delete-cascade.up.sql
Normal file
7
api/migrations/0017_add-profile-bg-delete-cascade.up.sql
Normal 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
69
assets/favicon.svg
Normal 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 |
@ -24,7 +24,7 @@
|
||||
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`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<title>Citadel</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
@ -5,7 +5,10 @@ import { useHistory } from 'react-router';
|
||||
import UserIDContext from 'App/context';
|
||||
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 history = useHistory();
|
||||
const { userID, setUserID } = useContext(UserIDContext);
|
||||
@ -41,6 +44,7 @@ const GlobalTopNavbar: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<TopNavbar
|
||||
projectName={name}
|
||||
bgColor={data ? data.me.profileIcon.bgColor ?? '#7367F0' : '#7367F0'}
|
||||
firstName={data ? data.me.firstName : ''}
|
||||
lastName={data ? data.me.lastName : ''}
|
||||
|
@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
|
||||
import jwtDecode from 'jwt-decode';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { setAccessToken } from 'shared/utils/accessToken';
|
||||
import GlobalTopNavbar from 'App/TopNavbar';
|
||||
import styled from 'styled-components';
|
||||
import NormalizeStyles from './NormalizeStyles';
|
||||
import BaseStyles from './BaseStyles';
|
||||
@ -10,6 +9,7 @@ import Routes from './Routes';
|
||||
import { UserIDContext } from './context';
|
||||
import Navbar from './Navbar';
|
||||
import { Router } from 'react-router';
|
||||
import { PopupProvider } from 'shared/components/PopupMenu';
|
||||
|
||||
const history = createBrowserHistory();
|
||||
|
||||
@ -45,6 +45,7 @@ const App = () => {
|
||||
return (
|
||||
<>
|
||||
<UserIDContext.Provider value={{ userID, setUserID }}>
|
||||
<PopupProvider>
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<Router history={history}>
|
||||
@ -54,12 +55,12 @@ const App = () => {
|
||||
<>
|
||||
<Navbar />
|
||||
<MainContent>
|
||||
<GlobalTopNavbar />
|
||||
<Routes history={history} />
|
||||
</MainContent>
|
||||
</>
|
||||
)}
|
||||
</Router>
|
||||
</PopupProvider>
|
||||
</UserIDContext.Provider>
|
||||
</>
|
||||
);
|
||||
|
@ -1,11 +1,12 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
import Modal from 'shared/components/Modal';
|
||||
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 { useRouteMatch, useHistory } from 'react-router';
|
||||
import { useFindTaskQuery, useAssignTaskMutation, useUnassignTaskMutation } from 'shared/generated/graphql';
|
||||
import UserIDContext from 'App/context';
|
||||
import MiniProfile from 'shared/components/MiniProfile';
|
||||
|
||||
type DetailsProps = {
|
||||
taskID: string;
|
||||
@ -13,7 +14,7 @@ type DetailsProps = {
|
||||
onTaskNameChange: (task: Task, newName: string) => void;
|
||||
onTaskDescriptionChange: (task: Task, newDescription: string) => void;
|
||||
onDeleteTask: (task: Task) => void;
|
||||
onOpenAddLabelPopup: (task: Task, bounds: ElementBounds) => void;
|
||||
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
availableMembers: Array<TaskUser>;
|
||||
refreshCache: () => void;
|
||||
};
|
||||
@ -31,8 +32,10 @@ const Details: React.FC<DetailsProps> = ({
|
||||
refreshCache,
|
||||
}) => {
|
||||
const { userID } = useContext(UserIDContext);
|
||||
const { showPopup } = usePopup();
|
||||
const history = useHistory();
|
||||
const match = useRouteMatch();
|
||||
const [currentMemberTask, setCurrentMemberTask] = useState('');
|
||||
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
||||
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
|
||||
const [assignTask] = useAssignTaskMutation({
|
||||
@ -55,7 +58,7 @@ const Details: React.FC<DetailsProps> = ({
|
||||
}
|
||||
const taskMembers = data.findTask.assigned.map(assigned => {
|
||||
return {
|
||||
userID: assigned.userID,
|
||||
userID: assigned.id,
|
||||
displayName: `${assigned.firstName} ${assigned.lastName}`,
|
||||
profileIcon: {
|
||||
url: null,
|
||||
@ -76,6 +79,8 @@ const Details: React.FC<DetailsProps> = ({
|
||||
<TaskDetails
|
||||
task={{
|
||||
...data.findTask,
|
||||
taskID: data.findTask.id,
|
||||
taskGroup: { taskGroupID: data.findTask.taskGroup.id },
|
||||
members: taskMembers,
|
||||
description: data.findTask.description ?? '',
|
||||
labels: [],
|
||||
@ -84,42 +89,48 @@ const Details: React.FC<DetailsProps> = ({
|
||||
onTaskDescriptionChange={onTaskDescriptionChange}
|
||||
onDeleteTask={onDeleteTask}
|
||||
onCloseModal={() => history.push(projectURL)}
|
||||
onOpenAddMemberPopup={(task, bounds) => {
|
||||
console.log(task, bounds);
|
||||
setMemberPopupData({
|
||||
isOpen: true,
|
||||
taskID: task.taskID,
|
||||
top: bounds.position.top + bounds.size.height + 10,
|
||||
left: bounds.position.left,
|
||||
});
|
||||
onMemberProfile={($targetRef, memberID) => {
|
||||
showPopup(
|
||||
$targetRef,
|
||||
<Popup title={null} onClose={() => {}} tab={0}>
|
||||
<MiniProfile
|
||||
profileIcon={taskMembers[0].profileIcon}
|
||||
displayName="Jordan Knott"
|
||||
username="@jordanthedev"
|
||||
bio="None"
|
||||
onRemoveFromTask={() => {
|
||||
unassignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
|
||||
}}
|
||||
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
||||
/>
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{memberPopupData.isOpen && (
|
||||
<PopupMenu
|
||||
title="Members"
|
||||
top={memberPopupData.top}
|
||||
onClose={() => setMemberPopupData(initialMemberPopupState)}
|
||||
left={memberPopupData.left}
|
||||
>
|
||||
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.taskID, userID: userID ?? '' } });
|
||||
assignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
|
||||
} else {
|
||||
unassignTask({ variables: { taskID: data.findTask.taskID, userID: userID ?? '' } });
|
||||
unassignTask({ variables: { taskID: data.findTask.id, userID: userID ?? '' } });
|
||||
}
|
||||
console.log(member, isActive);
|
||||
}}
|
||||
/>
|
||||
</PopupMenu>
|
||||
)}
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ import { Board } from './Styles';
|
||||
|
||||
type KanbanBoardProps = {
|
||||
listsData: BoardState;
|
||||
onOpenListActionsPopup: (isOpen: boolean, left: number, top: number, taskGroupID: string) => void;
|
||||
onOpenListActionsPopup: ($targetRef: React.RefObject<HTMLElement>, taskGroupID: string) => void;
|
||||
onCardDrop: (task: Task) => void;
|
||||
onListDrop: (taskGroup: TaskGroup) => void;
|
||||
onCardCreate: (taskGroupID: string, name: string) => void;
|
||||
@ -31,8 +31,8 @@ const KanbanBoard: React.FC<KanbanBoardProps> = ({
|
||||
onCardClick={task => {
|
||||
history.push(`${match.url}/c/${task.taskID}`);
|
||||
}}
|
||||
onExtraMenuOpen={(taskGroupID, pos, size) => {
|
||||
onOpenListActionsPopup(true, pos.left, pos.top + size.height + 5, taskGroupID);
|
||||
onExtraMenuOpen={(taskGroupID, $targetRef) => {
|
||||
onOpenListActionsPopup($targetRef, taskGroupID);
|
||||
}}
|
||||
onQuickEditorOpen={onQuickEditorOpen}
|
||||
onCardCreate={onCardCreate}
|
||||
|
@ -1,6 +1,9 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import * as BoardStateUtils from 'shared/utils/boardState';
|
||||
import GlobalTopNavbar from 'App/TopNavbar';
|
||||
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 {
|
||||
useFindProjectQuery,
|
||||
@ -13,14 +16,19 @@ import {
|
||||
useDeleteTaskGroupMutation,
|
||||
useUpdateTaskDescriptionMutation,
|
||||
useAssignTaskMutation,
|
||||
DeleteTaskDocument,
|
||||
FindProjectDocument,
|
||||
} from 'shared/generated/graphql';
|
||||
|
||||
import QuickCardEditor from 'shared/components/QuickCardEditor';
|
||||
import PopupMenu from 'shared/components/PopupMenu';
|
||||
import ListActions from 'shared/components/ListActions';
|
||||
import MemberManager from 'shared/components/MemberManager';
|
||||
import { LabelsPopup } from 'shared/components/PopupMenu/PopupMenu.stories';
|
||||
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';
|
||||
|
||||
type TaskRouteProps = {
|
||||
@ -45,6 +53,67 @@ const Title = styled.span`
|
||||
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 {
|
||||
projectId: string;
|
||||
}
|
||||
@ -55,6 +124,34 @@ const initialQuickCardEditorState: QuickCardEditorState = { isOpen: false, top:
|
||||
const initialLabelsPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
|
||||
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 { projectId } = useParams<ProjectParams>();
|
||||
const match = useRouteMatch();
|
||||
@ -70,17 +167,16 @@ const Project = () => {
|
||||
|
||||
const [deleteTaskGroup] = useDeleteTaskGroupMutation({
|
||||
onCompleted: deletedTaskGroupData => {
|
||||
setListsData(
|
||||
BoardStateUtils.deleteTaskGroup(listsData, deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID),
|
||||
);
|
||||
setListsData(BoardStateUtils.deleteTaskGroup(listsData, deletedTaskGroupData.deleteTaskGroup.taskGroup.id));
|
||||
},
|
||||
});
|
||||
|
||||
const [createTaskGroup] = useCreateTaskGroupMutation({
|
||||
onCompleted: newTaskGroupData => {
|
||||
const newTaskGroup = {
|
||||
...newTaskGroupData.createTaskGroup,
|
||||
taskGroupID: newTaskGroupData.createTaskGroup.id,
|
||||
tasks: [],
|
||||
...newTaskGroupData.createTaskGroup,
|
||||
};
|
||||
setListsData(BoardStateUtils.addTaskGroup(listsData, newTaskGroup));
|
||||
},
|
||||
@ -90,10 +186,43 @@ const Project = () => {
|
||||
onCompleted: newTaskData => {
|
||||
const newTask = {
|
||||
...newTaskData.createTask,
|
||||
taskID: newTaskData.createTask.id,
|
||||
taskGroup: { taskGroupID: newTaskData.createTask.taskGroup.id },
|
||||
labels: [],
|
||||
};
|
||||
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({
|
||||
@ -105,50 +234,14 @@ const Project = () => {
|
||||
const [updateTaskName] = useUpdateTaskNameMutation({
|
||||
onCompleted: newTaskData => {
|
||||
setListsData(
|
||||
BoardStateUtils.updateTaskName(listsData, newTaskData.updateTaskName.taskID, newTaskData.updateTaskName.name),
|
||||
BoardStateUtils.updateTaskName(listsData, newTaskData.updateTaskName.id, newTaskData.updateTaskName.name),
|
||||
);
|
||||
},
|
||||
});
|
||||
const { loading, data, refetch } = useFindProjectQuery({
|
||||
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 taskGroupTasks = Object.values(listsData.tasks).filter(
|
||||
@ -163,15 +256,6 @@ const Project = () => {
|
||||
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) => {
|
||||
updateTaskLocation({
|
||||
variables: {
|
||||
@ -202,10 +286,50 @@ const Project = () => {
|
||||
|
||||
const [assignTask] = useAssignTaskMutation();
|
||||
|
||||
const { showPopup } = usePopup();
|
||||
const $labelsRef = useRef<HTMLDivElement>(null);
|
||||
if (loading) {
|
||||
return <Title>Error Loading</Title>;
|
||||
return (
|
||||
<>
|
||||
<GlobalTopNavbar name="Project" />
|
||||
<Title>Error Loading</Title>
|
||||
</>
|
||||
);
|
||||
}
|
||||
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 => {
|
||||
return {
|
||||
displayName: `${member.firstName} ${member.lastName}`,
|
||||
@ -214,38 +338,75 @@ const Project = () => {
|
||||
initials: member.profileIcon.initials ?? 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 (
|
||||
<>
|
||||
<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
|
||||
listsData={listsData}
|
||||
listsData={currentListsData}
|
||||
onCardDrop={onCardDrop}
|
||||
onListDrop={onListDrop}
|
||||
onCardCreate={onCardCreate}
|
||||
onCreateList={onCreateList}
|
||||
onQuickEditorOpen={onQuickEditorOpen}
|
||||
onOpenListActionsPopup={(isOpen, left, top, taskGroupID) => {
|
||||
setPopupData({ isOpen, top, left, taskGroupID });
|
||||
}}
|
||||
/>
|
||||
{popupData.isOpen && (
|
||||
<PopupMenu
|
||||
title="List Actions"
|
||||
top={popupData.top}
|
||||
onClose={() => setPopupData(initialPopupState)}
|
||||
left={popupData.left}
|
||||
>
|
||||
onOpenListActionsPopup={($targetRef, taskGroupID) => {
|
||||
showPopup(
|
||||
$targetRef,
|
||||
<Popup title="List actions" tab={0} onClose={() => {}}>
|
||||
<ListActions
|
||||
taskGroupID={popupData.taskGroupID}
|
||||
onArchiveTaskGroup={taskGroupID => {
|
||||
deleteTaskGroup({ variables: { taskGroupID } });
|
||||
taskGroupID={taskGroupID}
|
||||
onArchiveTaskGroup={tgID => {
|
||||
deleteTaskGroup({ variables: { taskGroupID: tgID } });
|
||||
setPopupData(initialPopupState);
|
||||
}}
|
||||
/>
|
||||
</PopupMenu>
|
||||
)}
|
||||
</Popup>,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{quickCardEditor.isOpen && (
|
||||
<QuickCardEditor
|
||||
isOpen
|
||||
@ -257,7 +418,35 @@ const Project = () => {
|
||||
updateTaskName({ variables: { taskID: cardId, name: cardName } });
|
||||
}}
|
||||
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={[]}
|
||||
top={quickCardEditor.top}
|
||||
left={quickCardEditor.left}
|
||||
@ -269,7 +458,7 @@ const Project = () => {
|
||||
<Details
|
||||
refreshCache={() => {
|
||||
console.log('beep 2!');
|
||||
refetch();
|
||||
// refetch();
|
||||
}}
|
||||
availableMembers={availableMembers}
|
||||
projectURL={match.url}
|
||||
@ -284,7 +473,7 @@ const Project = () => {
|
||||
setTaskDetails(initialTaskDetailsState);
|
||||
deleteTask({ variables: { taskID: deletedTask.taskID } });
|
||||
}}
|
||||
onOpenAddLabelPopup={(task, bounds) => {}}
|
||||
onOpenAddLabelPopup={(task, $targetRef) => {}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components/macro';
|
||||
import GlobalTopNavbar from 'App/TopNavbar';
|
||||
import { useGetProjectsQuery } from 'shared/generated/graphql';
|
||||
|
||||
import ProjectGridItem from 'shared/components/ProjectGridItem';
|
||||
@ -40,13 +41,18 @@ const Projects = () => {
|
||||
if (data) {
|
||||
const { projects } = data;
|
||||
return (
|
||||
<>
|
||||
<GlobalTopNavbar name="Projects" />
|
||||
<ProjectGrid>
|
||||
{projects.map(project => (
|
||||
<ProjectLink key={project.projectID} to={`/projects/${project.projectID}`}>
|
||||
<ProjectGridItem project={{ ...project, teamTitle: project.team.name, taskGroups: [] }} />
|
||||
<ProjectLink key={project.id} to={`/projects/${project.id}`}>
|
||||
<ProjectGridItem
|
||||
project={{ ...project, projectID: project.id, teamTitle: project.team.name, taskGroups: [] }}
|
||||
/>
|
||||
</ProjectLink>
|
||||
))}
|
||||
</ProjectGrid>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <div>Error!</div>;
|
||||
|
2
web/src/citadel.d.ts
vendored
2
web/src/citadel.d.ts
vendored
@ -52,7 +52,7 @@ type Task = {
|
||||
name: string;
|
||||
position: number;
|
||||
labels: Label[];
|
||||
description?: string;
|
||||
description?: string | null;
|
||||
members?: Array<TaskUser>;
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,7 @@ export const CardComposerWrapper = styled.div<{ isOpen: boolean }>`
|
||||
`;
|
||||
|
||||
export const ListCard = styled.div`
|
||||
background-color: #fff;
|
||||
background-color: ${props => mixin.lighten('#262c49', 0.05)};
|
||||
border-radius: 3px;
|
||||
${mixin.boxShadowCard}
|
||||
cursor: pointer;
|
||||
@ -55,9 +55,10 @@ export const ListCardEditor = styled(TextareaAutosize)`
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
&:focus {
|
||||
border: none;
|
||||
outline: none;
|
||||
|
||||
color: #c2c6dc;
|
||||
l &:focus {
|
||||
background-color: ${props => mixin.lighten('#262c49', 0.05)};
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -4,8 +4,8 @@ export const Container = styled.div<{ left: number; top: number }>`
|
||||
position: absolute;
|
||||
left: ${props => props.left}px;
|
||||
top: ${props => props.top}px;
|
||||
padding-top: 10px;
|
||||
position: absolute;
|
||||
padding-top: 10px;
|
||||
height: auto;
|
||||
width: auto;
|
||||
transform: translate(-100%);
|
||||
@ -18,12 +18,12 @@ export const Wrapper = styled.div`
|
||||
padding-top: 8px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid 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;
|
||||
`;
|
||||
|
||||
|
@ -21,7 +21,7 @@ export const AddCardContainer = styled.div`
|
||||
|
||||
export const AddCardButton = styled.a`
|
||||
border-radius: 3px;
|
||||
color: #5e6c84;
|
||||
color: #c2c6dc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
@ -32,9 +32,9 @@ export const AddCardButton = styled.a`
|
||||
text-decoration: none;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background-color: rgba(9, 30, 66, 0.08);
|
||||
color: #172b4d;
|
||||
color: #c2c6dc;
|
||||
text-decoration: none;
|
||||
background: rgb(115, 103, 240);
|
||||
}
|
||||
`;
|
||||
export const Wrapper = styled.div`
|
||||
@ -125,4 +125,5 @@ export const ListExtraMenuButtonWrapper = styled.div`
|
||||
top: 4px;
|
||||
z-index: 1;
|
||||
padding: 6px;
|
||||
padding-bottom: 0;
|
||||
`;
|
||||
|
@ -26,7 +26,7 @@ type Props = {
|
||||
wrapperProps?: any;
|
||||
headerProps?: any;
|
||||
index?: number;
|
||||
onExtraMenuOpen: (taskGroupID: string, pos: ElementPosition, size: ElementSize) => void;
|
||||
onExtraMenuOpen: (taskGroupID: string, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
const List = React.forwardRef(
|
||||
@ -78,20 +78,7 @@ const List = React.forwardRef(
|
||||
|
||||
const handleExtraMenuOpen = () => {
|
||||
if ($extraActionsRef && $extraActionsRef.current) {
|
||||
const pos = $extraActionsRef.current.getBoundingClientRect();
|
||||
onExtraMenuOpen(
|
||||
id,
|
||||
{
|
||||
top: pos.top,
|
||||
left: pos.left,
|
||||
right: pos.right,
|
||||
bottom: pos.bottom,
|
||||
},
|
||||
{
|
||||
width: pos.width,
|
||||
height: pos.height,
|
||||
},
|
||||
);
|
||||
onExtraMenuOpen(id, $extraActionsRef);
|
||||
}
|
||||
};
|
||||
useOnEscapeKeyDown(isEditingTitle, onEscape);
|
||||
@ -116,7 +103,7 @@ const List = React.forwardRef(
|
||||
{children && children}
|
||||
<AddCardContainer hidden={isComposerOpen}>
|
||||
<AddCardButton onClick={() => onOpenComposer(id)}>
|
||||
<Plus size={12} color="#42526e" />
|
||||
<Plus size={12} color="#c2c6dc" />
|
||||
<AddCardButtonText>Add another card</AddCardButtonText>
|
||||
</AddCardButton>
|
||||
</AddCardContainer>
|
||||
|
@ -14,19 +14,19 @@ export const ListActionItem = styled.span`
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #172b4d;
|
||||
color: #c2c6dc;
|
||||
font-weight: 400;
|
||||
padding: 6px 12px;
|
||||
position: relative;
|
||||
margin: 0 -12px;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
background-color: rgba(9, 30, 66, 0.04);
|
||||
background: rgb(115, 103, 240);
|
||||
}
|
||||
`;
|
||||
|
||||
export const ListSeparator = styled.hr`
|
||||
background-color: rgba(9, 30, 66, 0.13);
|
||||
background-color: #414561;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
margin: 8px 0;
|
||||
|
@ -29,7 +29,7 @@ type Props = {
|
||||
onCardCreate: (taskGroupID: string, name: string) => void;
|
||||
onQuickEditorOpen: (e: ContextMenuEvent) => 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> = ({
|
||||
|
@ -1,5 +1,6 @@
|
||||
import styled from 'styled-components';
|
||||
import TextareaAutosize from 'react-autosize-textarea/lib';
|
||||
import { mixin } from 'shared/utils/styles';
|
||||
|
||||
export const MemberManagerWrapper = styled.div``;
|
||||
|
||||
@ -11,17 +12,27 @@ export const MemberManagerSearchWrapper = styled.div`
|
||||
export const MemberManagerSearch = styled(TextareaAutosize)`
|
||||
margin: 4px 0 12px;
|
||||
width: 100%;
|
||||
background-color: #ebecf0;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 2px #dfe1e6;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
line-height: 20px;
|
||||
padding: 8px 12px;
|
||||
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`
|
||||
color: #5e6c84;
|
||||
color: #c2c6dc;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.04em;
|
||||
@ -52,7 +63,7 @@ export const BoardMemberListItemContent = styled.div`
|
||||
white-space: nowrap;
|
||||
padding: 4px;
|
||||
margin-bottom: 2px;
|
||||
color: #172b4d;
|
||||
color: #c2c6dc;
|
||||
`;
|
||||
|
||||
export const ProfileIcon = styled.div`
|
||||
@ -62,7 +73,7 @@ export const ProfileIcon = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
color: #c2c6dc;
|
||||
font-weight: 700;
|
||||
background: rgb(115, 103, 240);
|
||||
cursor: pointer;
|
||||
|
@ -43,7 +43,7 @@ const MemberManager: React.FC<MemberManagerProps> = ({
|
||||
)
|
||||
.map(member => {
|
||||
return (
|
||||
<BoardMembersListItem>
|
||||
<BoardMembersListItem key={member.userID}>
|
||||
<BoardMemberListItemContent
|
||||
onClick={() => {
|
||||
const isActive = activeMembers.findIndex(m => m.userID === member.userID) !== -1;
|
||||
|
@ -11,7 +11,11 @@ export const ProfileIcon = styled.div<{ bgColor: string }>`
|
||||
margin: 2px;
|
||||
background-color: ${props => props.bgColor};
|
||||
border-radius: 25em;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 50px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
@ -20,6 +24,7 @@ export const ProfileIcon = styled.div<{ bgColor: string }>`
|
||||
`;
|
||||
|
||||
export const ProfileInfo = styled.div`
|
||||
color: #c2c6dc;
|
||||
margin: 0 0 0 64px;
|
||||
word-wrap: break-word;
|
||||
`;
|
||||
@ -29,11 +34,11 @@ export const InfoTitle = styled.h3`
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
color: #172b4d;
|
||||
color: #c2c6dc;
|
||||
`;
|
||||
|
||||
export const InfoUsername = styled.p`
|
||||
color: #5e6c84;
|
||||
color: #c2c6dc;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
`;
|
||||
@ -41,10 +46,29 @@ export const InfoUsername = styled.p`
|
||||
export const InfoBio = styled.p`
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #5e6c84;
|
||||
color: #c2c6dc;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin: 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);
|
||||
}
|
||||
`;
|
||||
|
@ -1,14 +1,25 @@
|
||||
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 = {
|
||||
displayName: string;
|
||||
username: string;
|
||||
bio: string;
|
||||
profileIcon: ProfileIcon;
|
||||
onRemoveFromTask: () => void;
|
||||
};
|
||||
const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, profileIcon }) => {
|
||||
const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, profileIcon, onRemoveFromTask }) => {
|
||||
return (
|
||||
<>
|
||||
<Profile>
|
||||
@ -19,6 +30,17 @@ const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, p
|
||||
<InfoBio>{bio}</InfoBio>
|
||||
</ProfileInfo>
|
||||
</Profile>
|
||||
<MiniProfileActions>
|
||||
<MiniProfileActionWrapper>
|
||||
<MiniProfileActionItem
|
||||
onClick={() => {
|
||||
onRemoveFromTask();
|
||||
}}
|
||||
>
|
||||
Remove from card
|
||||
</MiniProfileActionItem>
|
||||
</MiniProfileActionWrapper>
|
||||
</MiniProfileActions>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
import { mixin } from 'shared/utils/styles';
|
||||
|
||||
export const ScrollOverlay = styled.div`
|
||||
z-index: 1000000;
|
||||
z-index: 3000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@ -4,25 +4,51 @@ import { Checkmark } from 'shared/icons';
|
||||
import { SaveButton, DeleteButton, LabelBox, EditLabelForm, FieldLabel, FieldName } from './Styles';
|
||||
|
||||
type Props = {
|
||||
label: Label;
|
||||
onLabelEdit: (labelId: string, labelName: string, color: string) => void;
|
||||
label: Label | null;
|
||||
onLabelEdit: (labelId: string | null, labelName: string, color: string) => void;
|
||||
};
|
||||
|
||||
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 (
|
||||
<EditLabelForm>
|
||||
<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>
|
||||
<div>
|
||||
{Object.values(LabelColors).map(labelColor => (
|
||||
<LabelBox color={labelColor}>
|
||||
<Checkmark color="#fff" size={12} />
|
||||
<LabelBox
|
||||
color={labelColor}
|
||||
onClick={() => {
|
||||
setCurrentColor(labelColor);
|
||||
}}
|
||||
>
|
||||
{labelColor === currentColor && <Checkmark color="#fff" size={12} />}
|
||||
</LabelBox>
|
||||
))}
|
||||
</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" />
|
||||
</div>
|
||||
</EditLabelForm>
|
||||
|
@ -1,26 +1,51 @@
|
||||
import React, { useState } from 'react';
|
||||
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 = {
|
||||
labels?: Label[];
|
||||
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 [currentSearch, setCurrentSearch] = useState('');
|
||||
return (
|
||||
<>
|
||||
<LabelSearch type="text" />
|
||||
<LabelSearch
|
||||
type="text"
|
||||
placeholder="search labels..."
|
||||
onChange={e => {
|
||||
setCurrentSearch(e.currentTarget.value);
|
||||
}}
|
||||
value={currentSearch}
|
||||
/>
|
||||
<Section>
|
||||
<SectionTitle>Labels</SectionTitle>
|
||||
<Labels>
|
||||
{labels &&
|
||||
labels.map(label => (
|
||||
<Label>
|
||||
<LabelIcon>
|
||||
<Pencil />
|
||||
labels
|
||||
.filter(label => currentSearch === '' || label.name.toLowerCase().startsWith(currentSearch.toLowerCase()))
|
||||
.map(label => (
|
||||
<Label key={label.labelId}>
|
||||
<LabelIcon
|
||||
onClick={() => {
|
||||
onLabelEdit(label.labelId);
|
||||
}}
|
||||
>
|
||||
<Pencil color="#c2c6dc" />
|
||||
</LabelIcon>
|
||||
<CardLabel
|
||||
key={label.labelId}
|
||||
@ -41,6 +66,13 @@ const LabelManager: React.FC<Props> = ({ labels, onLabelToggle, onLabelEdit }) =
|
||||
</Label>
|
||||
))}
|
||||
</Labels>
|
||||
<CreateLabelButton
|
||||
onClick={() => {
|
||||
onLabelCreate();
|
||||
}}
|
||||
>
|
||||
Create a new label
|
||||
</CreateLabelButton>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import React, { useState, useRef, createRef } from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import LabelColors from 'shared/constants/labelColors';
|
||||
import LabelManager from 'shared/components/PopupMenu/LabelManager';
|
||||
@ -7,10 +7,12 @@ import ListActions from 'shared/components/ListActions';
|
||||
import MemberManager from 'shared/components/MemberManager';
|
||||
import DueDateManager from 'shared/components/DueDateManager';
|
||||
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 BaseStyles from 'App/BaseStyles';
|
||||
import produce from 'immer';
|
||||
|
||||
export default {
|
||||
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 = () => {
|
||||
const [isPopupOpen, setPopupOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
{isPopupOpen && (
|
||||
<PopupMenu title="Label" top={10} onClose={() => setPopupOpen(false)} left={10}>
|
||||
<LabelManager labels={labelData} onLabelToggle={action('label toggle')} onLabelEdit={action('label edit')} />
|
||||
</PopupMenu>
|
||||
)}
|
||||
<button type="submit" onClick={() => setPopupOpen(true)}>
|
||||
Open
|
||||
</button>
|
||||
</>
|
||||
<PopupProvider>
|
||||
<OpenLabelsButton />
|
||||
</PopupProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -58,7 +134,13 @@ export const LabelsLabelEditor = () => {
|
||||
return (
|
||||
<>
|
||||
{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')} />
|
||||
</PopupMenu>
|
||||
)}
|
||||
@ -201,12 +283,19 @@ export const MiniProfilePopup = () => {
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
{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
|
||||
displayName="Jordan Knott"
|
||||
profileIcon={{ url: null, bgColor: '#000', initials: 'JK' }}
|
||||
username="@jordanthedev"
|
||||
bio="Stuff and things"
|
||||
onRemoveFromTask={action('mini profile')}
|
||||
/>
|
||||
</PopupMenu>
|
||||
)}
|
||||
@ -236,3 +325,4 @@ export const MiniProfilePopup = () => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,20 +1,34 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
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;
|
||||
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;
|
||||
position: absolute;
|
||||
width: 304px;
|
||||
z-index: 100000000000;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
width: 316px;
|
||||
padding-top: 10px;
|
||||
height: auto;
|
||||
z-index: 40000;
|
||||
${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`
|
||||
@ -26,10 +40,10 @@ export const Header = styled.div`
|
||||
|
||||
export const HeaderTitle = styled.span`
|
||||
box-sizing: border-box;
|
||||
color: #5e6c84;
|
||||
color: #c2c6dc;
|
||||
display: block;
|
||||
line-height: 40px;
|
||||
border-bottom: 1px solid rgba(9, 30, 66, 0.13);
|
||||
border-bottom: 1px solid #414561;
|
||||
margin: 0 12px;
|
||||
overflow: hidden;
|
||||
padding: 0 32px;
|
||||
@ -46,23 +60,30 @@ export const Content = styled.div`
|
||||
padding: 0 12px 12px;
|
||||
`;
|
||||
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;
|
||||
width: 100%;
|
||||
background-color: #fafbfc;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 2px #dfe1e6;
|
||||
color: #172b4d;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
padding: 8px 12px;
|
||||
font-size: 14px;
|
||||
font-family: 'Droid Sans';
|
||||
font-weight: 400;
|
||||
transition-property: background-color, border-color, box-shadow;
|
||||
transition-duration: 85ms;
|
||||
transition-timing-function: ease;
|
||||
|
||||
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 Section = styled.div`
|
||||
@ -70,7 +91,7 @@ export const Section = styled.div`
|
||||
`;
|
||||
|
||||
export const SectionTitle = styled.h4`
|
||||
color: #5e6c84;
|
||||
color: #c2c6dc;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.04em;
|
||||
@ -95,7 +116,7 @@ export const CardLabel = styled.span<{ active: boolean; color: string }>`
|
||||
props.active &&
|
||||
css`
|
||||
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;
|
||||
`}
|
||||
|
||||
@ -113,6 +134,7 @@ export const CardLabel = styled.span<{ active: boolean; color: string }>`
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-height: 31px;
|
||||
`;
|
||||
|
||||
export const CloseButton = styled.div`
|
||||
@ -126,8 +148,6 @@ export const CloseButton = styled.div`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 40;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
@ -142,14 +162,14 @@ export const LabelIcon = styled.div`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
height: 20px;
|
||||
height: 100%;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
width: 20px;
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
|
||||
&: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`
|
||||
margin: 4px 0 12px;
|
||||
width: 100%;
|
||||
background-color: #fafbfc;
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 2px #dfe1e6;
|
||||
color: #172b4d;
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
line-height: 20px;
|
||||
margin-bottom: 12px;
|
||||
padding: 8px 12px;
|
||||
background: #262c49;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-image: initial;
|
||||
|
||||
font-size: 12px;
|
||||
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 }>`
|
||||
@ -208,6 +236,7 @@ export const LabelBox = styled.span<{ color: string }>`
|
||||
padding: 0;
|
||||
width: 48px;
|
||||
|
||||
cursor: pointer;
|
||||
background-color: ${props => props.color};
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
@ -217,6 +246,7 @@ export const LabelBox = styled.span<{ color: string }>`
|
||||
`;
|
||||
|
||||
export const SaveButton = styled.input`
|
||||
cursor: pointer;
|
||||
background-color: #5aac44;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
@ -239,8 +269,7 @@ export const DeleteButton = styled.input`
|
||||
border: none;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-weight: 400;
|
||||
type="submit"font-weight: 400;
|
||||
line-height: 20px;
|
||||
margin: 8px 4px 0 0;
|
||||
padding: 6px 12px;
|
||||
@ -248,3 +277,53 @@ export const DeleteButton = styled.input`
|
||||
border-radius: 3px;
|
||||
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;
|
||||
`;
|
||||
|
@ -1,30 +1,219 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Cross } from 'shared/icons';
|
||||
import React, { useRef, createContext, RefObject, useState, useContext } from 'react';
|
||||
import { Cross, AngleLeft } from 'shared/icons';
|
||||
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 = {
|
||||
title: string;
|
||||
type PopupContextState = {
|
||||
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;
|
||||
left: number;
|
||||
invert: boolean;
|
||||
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();
|
||||
useOnOutsideClick($containerRef, true, onClose, null);
|
||||
|
||||
return (
|
||||
<Container left={left} top={top} ref={$containerRef}>
|
||||
<Container invert={false} left={left} top={top} ref={$containerRef}>
|
||||
<Wrapper>
|
||||
{onPrevious && (
|
||||
<PreviousButton onClick={onPrevious}>
|
||||
<AngleLeft color="#c2c6dc" />
|
||||
</PreviousButton>
|
||||
)}
|
||||
{noHeader ? (
|
||||
<CloseButton onClick={() => onClose()}>
|
||||
<Cross color="#c2c6dc" />
|
||||
</CloseButton>
|
||||
) : (
|
||||
<Header>
|
||||
<HeaderTitle>{title}</HeaderTitle>
|
||||
<CloseButton onClick={() => onClose()}>
|
||||
<Cross />
|
||||
<Cross color="#c2c6dc" />
|
||||
</CloseButton>
|
||||
</Header>
|
||||
)}
|
||||
<Content>{children}</Content>
|
||||
</Wrapper>
|
||||
</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;
|
||||
|
@ -61,7 +61,7 @@ export const Default = () => {
|
||||
onSaveName={action('on save name')}
|
||||
onOpenComposer={action('on open composer')}
|
||||
tasks={[]}
|
||||
onExtraMenuOpen={(taskGroupID, pos, size) => console.log(taskGroupID, pos, size)}
|
||||
onExtraMenuOpen={(taskGroupID, $targetRef) => console.log(taskGroupID, $targetRef)}
|
||||
>
|
||||
<ListCards>
|
||||
<Card
|
||||
|
@ -2,7 +2,7 @@ import styled, { keyframes } from 'styled-components';
|
||||
import TextareaAutosize from 'react-autosize-textarea';
|
||||
|
||||
export const Wrapper = styled.div<{ open: boolean }>`
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
bottom: 0;
|
||||
color: #fff;
|
||||
left: 0;
|
||||
|
@ -286,4 +286,7 @@ export const UnassignedLabel = styled.div`
|
||||
color: rgb(137, 147, 164);
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
`;
|
||||
|
@ -47,6 +47,7 @@ export const Default = () => {
|
||||
onTaskDescriptionChange={(_task, desc) => setDescription(desc)}
|
||||
onDeleteTask={action('delete task')}
|
||||
onCloseModal={action('close modal')}
|
||||
onMemberProfile={action('profile')}
|
||||
onOpenAddMemberPopup={action('open add member popup')}
|
||||
onOpenAddLabelPopup={action('open add label popup')}
|
||||
/>
|
||||
|
@ -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 = {
|
||||
task: Task;
|
||||
onTaskNameChange: (task: Task, newName: string) => void;
|
||||
onTaskDescriptionChange: (task: Task, newDescription: string) => void;
|
||||
onDeleteTask: (task: Task) => void;
|
||||
onOpenAddMemberPopup: (task: Task, bounds: ElementBounds) => void;
|
||||
onOpenAddLabelPopup: (task: Task, bounds: ElementBounds) => void;
|
||||
onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
||||
onCloseModal: () => void;
|
||||
};
|
||||
|
||||
@ -111,6 +125,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
onCloseModal,
|
||||
onOpenAddMemberPopup,
|
||||
onOpenAddLabelPopup,
|
||||
onMemberProfile,
|
||||
}) => {
|
||||
const [editorOpen, setEditorOpen] = useState(false);
|
||||
const [description, setDescription] = useState(task.description ?? '');
|
||||
@ -130,23 +145,14 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
const $unassignedRef = useRef<HTMLDivElement>(null);
|
||||
const $addMemberRef = useRef<HTMLDivElement>(null);
|
||||
const onUnassignedClick = () => {
|
||||
const bounds = convertDivElementRefToBounds($unassignedRef);
|
||||
if (bounds) {
|
||||
onOpenAddMemberPopup(task, bounds);
|
||||
}
|
||||
onOpenAddMemberPopup(task, $unassignedRef);
|
||||
};
|
||||
const onAddMember = () => {
|
||||
const bounds = convertDivElementRefToBounds($addMemberRef);
|
||||
if (bounds) {
|
||||
onOpenAddMemberPopup(task, bounds);
|
||||
}
|
||||
onOpenAddMemberPopup(task, $addMemberRef);
|
||||
};
|
||||
const $addLabelRef = useRef<HTMLDivElement>(null);
|
||||
const onAddLabel = () => {
|
||||
const bounds = convertDivElementRefToBounds($addLabelRef);
|
||||
if (bounds) {
|
||||
onOpenAddLabelPopup(task, bounds);
|
||||
}
|
||||
onOpenAddLabelPopup(task, $addLabelRef);
|
||||
};
|
||||
console.log(task);
|
||||
return (
|
||||
@ -204,14 +210,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
||||
) : (
|
||||
<>
|
||||
{task.members &&
|
||||
task.members.map(member => {
|
||||
console.log(member);
|
||||
return (
|
||||
<TaskDetailAssignee key={member.userID}>
|
||||
<ProfileIcon>{member.profileIcon.initials ?? ''}</ProfileIcon>
|
||||
</TaskDetailAssignee>
|
||||
);
|
||||
})}
|
||||
task.members.map(member => <TaskAssignee member={member} onMemberProfile={onMemberProfile} />)}
|
||||
<TaskDetailsAddMember ref={$addMemberRef} onClick={onAddMember}>
|
||||
<TaskDetailsAddMemberIcon>
|
||||
<Plus size={16} color="#c2c6dc" />
|
||||
|
@ -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`
|
||||
width: 100%;
|
||||
@ -76,8 +77,10 @@ export const ProfileIcon = styled.div<{ bgColor: string }>`
|
||||
`;
|
||||
|
||||
export const ProjectMeta = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding-top: 9px;
|
||||
margin-left: -14px;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
min-height: 51px;
|
||||
`;
|
||||
@ -91,11 +94,11 @@ export const ProjectTabs = styled.div`
|
||||
max-width: 100%;
|
||||
`;
|
||||
|
||||
export const ProjectTab = styled.span`
|
||||
export const ProjectTab = styled.span<{ active?: boolean }>`
|
||||
font-size: 80%;
|
||||
color: #c2c6dc;
|
||||
font-size: 15px;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
line-height: normal;
|
||||
min-width: 1px;
|
||||
@ -103,16 +106,71 @@ export const ProjectTab = styled.span`
|
||||
transition-property: box-shadow, color;
|
||||
white-space: nowrap;
|
||||
flex: 0 1 auto;
|
||||
|
||||
padding-bottom: 12px;
|
||||
|
||||
&:not(:last-child) {
|
||||
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`
|
||||
color: #c2c6dc;
|
||||
margin-top: 9px;
|
||||
font-weight: 600;
|
||||
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);
|
||||
}
|
||||
`;
|
||||
|
@ -38,6 +38,7 @@ export const Default = () => {
|
||||
<NormalizeStyles />
|
||||
<BaseStyles />
|
||||
<TopNavbar
|
||||
projectName="Projects"
|
||||
bgColor="#7367F0"
|
||||
firstName="Jordan"
|
||||
lastName="Knott"
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { Bell } from 'shared/icons';
|
||||
import { Star, Bell, Cog, AngleDown } from 'shared/icons';
|
||||
|
||||
import {
|
||||
NotificationContainer,
|
||||
GlobalActions,
|
||||
ProjectActions,
|
||||
ProjectSwitcher,
|
||||
Separator,
|
||||
ProjectMeta,
|
||||
ProjectName,
|
||||
ProjectTabs,
|
||||
@ -13,6 +15,7 @@ import {
|
||||
NavbarHeader,
|
||||
Breadcrumbs,
|
||||
BreadcrumpSeparator,
|
||||
ProjectSettingsButton,
|
||||
ProfileIcon,
|
||||
ProfileContainer,
|
||||
ProfileNameWrapper,
|
||||
@ -21,6 +24,7 @@ import {
|
||||
} from './Styles';
|
||||
|
||||
type NavBarProps = {
|
||||
projectName: string;
|
||||
onProfileClick: (bottom: number, right: number) => void;
|
||||
onNotificationClick: () => void;
|
||||
bgColor: string;
|
||||
@ -29,6 +33,7 @@ type NavBarProps = {
|
||||
initials: string;
|
||||
};
|
||||
const NavBar: React.FC<NavBarProps> = ({
|
||||
projectName,
|
||||
onProfileClick,
|
||||
onNotificationClick,
|
||||
firstName,
|
||||
@ -47,10 +52,19 @@ const NavBar: React.FC<NavBarProps> = ({
|
||||
<NavbarHeader>
|
||||
<ProjectActions>
|
||||
<ProjectMeta>
|
||||
<ProjectName>Production Team</ProjectName>
|
||||
<ProjectSwitcher>Projects</ProjectSwitcher>
|
||||
<Separator>»</Separator>
|
||||
<ProjectName>{projectName}</ProjectName>
|
||||
<ProjectSettingsButton>
|
||||
<AngleDown color="#c2c6dc" />
|
||||
</ProjectSettingsButton>
|
||||
<Star filled color="#c2c6dc" />
|
||||
</ProjectMeta>
|
||||
<ProjectTabs>
|
||||
<ProjectTab>Board</ProjectTab>
|
||||
<ProjectTab active>Board</ProjectTab>
|
||||
<ProjectTab>Calender</ProjectTab>
|
||||
<ProjectTab>Timeline</ProjectTab>
|
||||
<ProjectTab>Wiki</ProjectTab>
|
||||
</ProjectTabs>
|
||||
</ProjectActions>
|
||||
<GlobalActions>
|
||||
|
@ -17,7 +17,7 @@ export type Scalars = {
|
||||
|
||||
export type ProjectLabel = {
|
||||
__typename?: 'ProjectLabel';
|
||||
projectLabelID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
createdDate: Scalars['Time'];
|
||||
colorHex: Scalars['String'];
|
||||
name?: Maybe<Scalars['String']>;
|
||||
@ -25,7 +25,7 @@ export type ProjectLabel = {
|
||||
|
||||
export type TaskLabel = {
|
||||
__typename?: 'TaskLabel';
|
||||
taskLabelID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
projectLabelID: Scalars['UUID'];
|
||||
assignedDate: Scalars['Time'];
|
||||
colorHex: Scalars['String'];
|
||||
@ -41,7 +41,7 @@ export type ProfileIcon = {
|
||||
|
||||
export type ProjectMember = {
|
||||
__typename?: 'ProjectMember';
|
||||
userID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
firstName: Scalars['String'];
|
||||
lastName: Scalars['String'];
|
||||
profileIcon: ProfileIcon;
|
||||
@ -49,7 +49,7 @@ export type ProjectMember = {
|
||||
|
||||
export type RefreshToken = {
|
||||
__typename?: 'RefreshToken';
|
||||
tokenId: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
userId: Scalars['UUID'];
|
||||
expiresAt: Scalars['Time'];
|
||||
createdAt: Scalars['Time'];
|
||||
@ -57,7 +57,7 @@ export type RefreshToken = {
|
||||
|
||||
export type UserAccount = {
|
||||
__typename?: 'UserAccount';
|
||||
userID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
email: Scalars['String'];
|
||||
createdAt: Scalars['Time'];
|
||||
firstName: Scalars['String'];
|
||||
@ -68,14 +68,14 @@ export type UserAccount = {
|
||||
|
||||
export type Team = {
|
||||
__typename?: 'Team';
|
||||
teamID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
createdAt: Scalars['Time'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type Project = {
|
||||
__typename?: 'Project';
|
||||
projectID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
createdAt: Scalars['Time'];
|
||||
name: Scalars['String'];
|
||||
team: Team;
|
||||
@ -87,7 +87,7 @@ export type Project = {
|
||||
|
||||
export type TaskGroup = {
|
||||
__typename?: 'TaskGroup';
|
||||
taskGroupID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
projectID: Scalars['String'];
|
||||
createdAt: Scalars['Time'];
|
||||
name: Scalars['String'];
|
||||
@ -97,7 +97,7 @@ export type TaskGroup = {
|
||||
|
||||
export type Task = {
|
||||
__typename?: 'Task';
|
||||
taskID: Scalars['ID'];
|
||||
id: Scalars['ID'];
|
||||
taskGroup: TaskGroup;
|
||||
createdAt: Scalars['Time'];
|
||||
name: Scalars['String'];
|
||||
@ -382,14 +382,29 @@ export type AssignTaskMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { assignTask: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID'>
|
||||
& Pick<Task, 'id'>
|
||||
& { assigned: Array<(
|
||||
{ __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 = {
|
||||
taskGroupID: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
@ -401,11 +416,18 @@ export type CreateTaskMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { createTask: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID' | 'name' | 'position'>
|
||||
& Pick<Task, 'id' | 'name' | 'position' | 'description'>
|
||||
& { 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' }
|
||||
& { createTaskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'taskGroupID' | 'name' | 'position'>
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
) }
|
||||
);
|
||||
|
||||
@ -449,10 +471,10 @@ export type DeleteTaskGroupMutation = (
|
||||
& Pick<DeleteTaskGroupPayload, 'ok' | 'affectedRows'>
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'taskGroupID'>
|
||||
& Pick<TaskGroup, 'id'>
|
||||
& { tasks: Array<(
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID' | 'name'>
|
||||
& Pick<Task, 'id' | 'name'>
|
||||
)> }
|
||||
) }
|
||||
) }
|
||||
@ -470,20 +492,23 @@ export type FindProjectQuery = (
|
||||
& Pick<Project, 'name'>
|
||||
& { members: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
|
||||
& { profileIcon: (
|
||||
{ __typename?: 'ProfileIcon' }
|
||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||
) }
|
||||
)>, labels: Array<(
|
||||
{ __typename?: 'ProjectLabel' }
|
||||
& Pick<ProjectLabel, 'id' | 'createdDate' | 'colorHex' | 'name'>
|
||||
)>, taskGroups: Array<(
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'taskGroupID' | 'name' | 'position'>
|
||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||
& { tasks: Array<(
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID' | 'name' | 'position' | 'description'>
|
||||
& Pick<Task, 'id' | 'name' | 'position' | 'description'>
|
||||
& { assigned: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
|
||||
& { profileIcon: (
|
||||
{ __typename?: 'ProfileIcon' }
|
||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||
@ -503,13 +528,13 @@ export type FindTaskQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { findTask: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID' | 'name' | 'description' | 'position'>
|
||||
& Pick<Task, 'id' | 'name' | 'description' | 'position'>
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'taskGroupID'>
|
||||
& Pick<TaskGroup, 'id'>
|
||||
), assigned: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
|
||||
& { profileIcon: (
|
||||
{ __typename?: 'ProfileIcon' }
|
||||
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||
@ -525,10 +550,10 @@ export type GetProjectsQuery = (
|
||||
{ __typename?: 'Query' }
|
||||
& { projects: Array<(
|
||||
{ __typename?: 'Project' }
|
||||
& Pick<Project, 'projectID' | 'name'>
|
||||
& Pick<Project, 'id' | 'name'>
|
||||
& { team: (
|
||||
{ __typename?: 'Team' }
|
||||
& Pick<Team, 'teamID' | 'name'>
|
||||
& Pick<Team, 'id' | 'name'>
|
||||
) }
|
||||
)> }
|
||||
);
|
||||
@ -558,10 +583,10 @@ export type UnassignTaskMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { unassignTask: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID'>
|
||||
& Pick<Task, 'id'>
|
||||
& { assigned: Array<(
|
||||
{ __typename?: 'ProjectMember' }
|
||||
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||
& Pick<ProjectMember, 'id' | 'firstName' | 'lastName'>
|
||||
)> }
|
||||
) }
|
||||
);
|
||||
@ -576,7 +601,7 @@ export type UpdateTaskDescriptionMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateTaskDescription: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID'>
|
||||
& Pick<Task, 'id'>
|
||||
) }
|
||||
);
|
||||
|
||||
@ -590,7 +615,7 @@ export type UpdateTaskGroupLocationMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateTaskGroupLocation: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'taskGroupID' | 'position'>
|
||||
& Pick<TaskGroup, 'id' | 'position'>
|
||||
) }
|
||||
);
|
||||
|
||||
@ -605,7 +630,7 @@ export type UpdateTaskLocationMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateTaskLocation: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID' | 'createdAt' | 'name' | 'position'>
|
||||
& Pick<Task, 'id' | 'createdAt' | 'name' | 'position'>
|
||||
) }
|
||||
);
|
||||
|
||||
@ -619,7 +644,7 @@ export type UpdateTaskNameMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { updateTaskName: (
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID' | 'name' | 'position'>
|
||||
& Pick<Task, 'id' | 'name' | 'position'>
|
||||
) }
|
||||
);
|
||||
|
||||
@ -627,12 +652,12 @@ export type UpdateTaskNameMutation = (
|
||||
export const AssignTaskDocument = gql`
|
||||
mutation assignTask($taskID: UUID!, $userID: UUID!) {
|
||||
assignTask(input: {taskID: $taskID, userID: $userID}) {
|
||||
id
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
taskID
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -662,15 +687,63 @@ export function useAssignTaskMutation(baseOptions?: ApolloReactHooks.MutationHoo
|
||||
export type AssignTaskMutationHookResult = ReturnType<typeof useAssignTaskMutation>;
|
||||
export type AssignTaskMutationResult = ApolloReactCommon.MutationResult<AssignTaskMutation>;
|
||||
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`
|
||||
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||
createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) {
|
||||
taskID
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
}
|
||||
id
|
||||
name
|
||||
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`
|
||||
mutation createTaskGroup($projectID: String!, $name: String!, $position: Float!) {
|
||||
createTaskGroup(input: {projectID: $projectID, name: $name, position: $position}) {
|
||||
taskGroupID
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
@ -775,9 +848,9 @@ export const DeleteTaskGroupDocument = gql`
|
||||
ok
|
||||
affectedRows
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
id
|
||||
tasks {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
@ -814,7 +887,7 @@ export const FindProjectDocument = gql`
|
||||
findProject(input: {projectId: $projectId}) {
|
||||
name
|
||||
members {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profileIcon {
|
||||
@ -823,17 +896,23 @@ export const FindProjectDocument = gql`
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
labels {
|
||||
id
|
||||
createdDate
|
||||
colorHex
|
||||
name
|
||||
}
|
||||
taskGroups {
|
||||
taskGroupID
|
||||
id
|
||||
name
|
||||
position
|
||||
tasks {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
position
|
||||
description
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profileIcon {
|
||||
@ -876,15 +955,15 @@ export type FindProjectQueryResult = ApolloReactCommon.QueryResult<FindProjectQu
|
||||
export const FindTaskDocument = gql`
|
||||
query findTask($taskID: UUID!) {
|
||||
findTask(input: {taskID: $taskID}) {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
description
|
||||
position
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
id
|
||||
}
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profileIcon {
|
||||
@ -925,10 +1004,10 @@ export type FindTaskQueryResult = ApolloReactCommon.QueryResult<FindTaskQuery, F
|
||||
export const GetProjectsDocument = gql`
|
||||
query getProjects {
|
||||
projects {
|
||||
projectID
|
||||
id
|
||||
name
|
||||
team {
|
||||
teamID
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
@ -1000,11 +1079,11 @@ export const UnassignTaskDocument = gql`
|
||||
mutation unassignTask($taskID: UUID!, $userID: UUID!) {
|
||||
unassignTask(input: {taskID: $taskID, userID: $userID}) {
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
taskID
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1037,7 +1116,7 @@ export type UnassignTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<
|
||||
export const UpdateTaskDescriptionDocument = gql`
|
||||
mutation updateTaskDescription($taskID: UUID!, $description: String!) {
|
||||
updateTaskDescription(input: {taskID: $taskID, description: $description}) {
|
||||
taskID
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1070,7 +1149,7 @@ export type UpdateTaskDescriptionMutationOptions = ApolloReactCommon.BaseMutatio
|
||||
export const UpdateTaskGroupLocationDocument = gql`
|
||||
mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) {
|
||||
updateTaskGroupLocation(input: {taskGroupID: $taskGroupID, position: $position}) {
|
||||
taskGroupID
|
||||
id
|
||||
position
|
||||
}
|
||||
}
|
||||
@ -1104,7 +1183,7 @@ export type UpdateTaskGroupLocationMutationOptions = ApolloReactCommon.BaseMutat
|
||||
export const UpdateTaskLocationDocument = gql`
|
||||
mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) {
|
||||
updateTaskLocation(input: {taskID: $taskID, taskGroupID: $taskGroupID, position: $position}) {
|
||||
taskID
|
||||
id
|
||||
createdAt
|
||||
name
|
||||
position
|
||||
@ -1141,7 +1220,7 @@ export type UpdateTaskLocationMutationOptions = ApolloReactCommon.BaseMutationOp
|
||||
export const UpdateTaskNameDocument = gql`
|
||||
mutation updateTaskName($taskID: String!, $name: String!) {
|
||||
updateTaskName(input: {taskID: $taskID, name: $name}) {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
mutation assignTask($taskID: UUID!, $userID: UUID!) {
|
||||
assignTask(input: {taskID: $taskID, userID: $userID}) {
|
||||
id
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
taskID
|
||||
}
|
||||
}
|
||||
|
8
web/src/shared/graphql/createProjectLabel.graphqls
Normal file
8
web/src/shared/graphql/createProjectLabel.graphqls
Normal 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
|
||||
}
|
||||
}
|
@ -1,10 +1,21 @@
|
||||
mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) {
|
||||
createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) {
|
||||
taskID
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
}
|
||||
id
|
||||
name
|
||||
position
|
||||
description
|
||||
taskGroup {
|
||||
id
|
||||
}
|
||||
assigned {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profileIcon {
|
||||
url
|
||||
initials
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ mutation createTaskGroup( $projectID: String!, $name: String!, $position: Float!
|
||||
createTaskGroup(
|
||||
input: { projectID: $projectID, name: $name, position: $position }
|
||||
) {
|
||||
taskGroupID
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ mutation deleteTaskGroup($taskGroupID: UUID!) {
|
||||
ok
|
||||
affectedRows
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
id
|
||||
tasks {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ query findProject($projectId: String!) {
|
||||
findProject(input: { projectId: $projectId }) {
|
||||
name
|
||||
members {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profileIcon {
|
||||
@ -11,17 +11,23 @@ query findProject($projectId: String!) {
|
||||
bgColor
|
||||
}
|
||||
}
|
||||
labels {
|
||||
id
|
||||
createdDate
|
||||
colorHex
|
||||
name
|
||||
}
|
||||
taskGroups {
|
||||
taskGroupID
|
||||
id
|
||||
name
|
||||
position
|
||||
tasks {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
position
|
||||
description
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profileIcon {
|
||||
|
@ -1,14 +1,14 @@
|
||||
query findTask($taskID: UUID!) {
|
||||
findTask(input: {taskID: $taskID}) {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
description
|
||||
position
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
id
|
||||
}
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
profileIcon {
|
||||
|
@ -1,9 +1,9 @@
|
||||
query getProjects {
|
||||
projects {
|
||||
projectID
|
||||
id
|
||||
name
|
||||
team {
|
||||
teamID
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
mutation unassignTask($taskID: UUID!, $userID: UUID!) {
|
||||
unassignTask(input: {taskID: $taskID, userID: $userID}) {
|
||||
assigned {
|
||||
userID
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
taskID
|
||||
id
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
mutation updateTaskDescription($taskID: UUID!, $description: String!) {
|
||||
updateTaskDescription(input: {taskID: $taskID, description: $description}) {
|
||||
taskID
|
||||
id
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) {
|
||||
updateTaskGroupLocation(input:{taskGroupID:$taskGroupID, position: $position}) {
|
||||
taskGroupID
|
||||
id
|
||||
position
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
mutation updateTaskLocation($taskID: String!, $taskGroupID: String!, $position: Float!) {
|
||||
updateTaskLocation(input: { taskID: $taskID, taskGroupID: $taskGroupID, position: $position }) {
|
||||
taskID
|
||||
id
|
||||
createdAt
|
||||
name
|
||||
position
|
||||
|
@ -1,6 +1,6 @@
|
||||
mutation updateTaskName($taskID: String!, $name: String!) {
|
||||
updateTaskName(input: { taskID: $taskID, name: $name }) {
|
||||
taskID
|
||||
id
|
||||
name
|
||||
position
|
||||
}
|
||||
|
27
web/src/shared/icons/AngleDown.tsx
Normal file
27
web/src/shared/icons/AngleDown.tsx
Normal 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;
|
||||
|
24
web/src/shared/icons/AngleLeft.tsx
Normal file
24
web/src/shared/icons/AngleLeft.tsx
Normal 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;
|
@ -7,8 +7,8 @@ type Props = {
|
||||
|
||||
const Bell = ({ size, color }: Props) => {
|
||||
return (
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16">
|
||||
<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" />
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 448 512">
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
24
web/src/shared/icons/Bolt.tsx
Normal file
24
web/src/shared/icons/Bolt.tsx
Normal 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;
|
24
web/src/shared/icons/Cog.tsx
Normal file
24
web/src/shared/icons/Cog.tsx
Normal 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;
|
@ -7,8 +7,8 @@ type Props = {
|
||||
|
||||
const Cross = ({ size, color }: Props) => {
|
||||
return (
|
||||
<svg fill={color} xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 16 16">
|
||||
<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" />
|
||||
<svg fill={color} width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512">
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
36
web/src/shared/icons/Star.tsx
Normal file
36
web/src/shared/icons/Star.tsx
Normal 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;
|
||||
|
24
web/src/shared/icons/Tags.tsx
Normal file
24
web/src/shared/icons/Tags.tsx
Normal 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;
|
24
web/src/shared/icons/ToggleOn.tsx
Normal file
24
web/src/shared/icons/ToggleOn.tsx
Normal 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;
|
@ -1,6 +1,10 @@
|
||||
import Cross from './Cross';
|
||||
import Cog from './Cog';
|
||||
import Bolt from './Bolt';
|
||||
import Plus from './Plus';
|
||||
import Bell from './Bell';
|
||||
import AngleLeft from './AngleLeft';
|
||||
import AngleDown from './AngleDown';
|
||||
import Bin from './Bin';
|
||||
import Pencil from './Pencil';
|
||||
import Checkmark from './Checkmark';
|
||||
@ -13,5 +17,31 @@ import Stack from './Stack';
|
||||
import Question from './Question';
|
||||
import Exit from './Exit';
|
||||
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,
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import produce from 'immer';
|
||||
|
||||
export const addTask = (currentState: BoardState, newTask: Task) => {
|
||||
return produce(currentState, (draftState: BoardState) => {
|
||||
currentState.tasks[newTask.taskID] = newTask;
|
||||
draftState.tasks[newTask.taskID] = newTask;
|
||||
});
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user