From c250ce574b135c3c06c6a7e7799c9828a736224b Mon Sep 17 00:00:00 2001 From: Jordan Knott Date: Sat, 11 Apr 2020 14:24:45 -0500 Subject: [PATCH] feature: ability to delete task groups --- api/graph/generated.go | 404 +++++++++++++++--- api/graph/models_gen.go | 11 + api/graph/schema.graphqls | 20 +- api/graph/schema.resolvers.go | 36 +- api/pg/pg.go | 17 +- api/pg/querier.go | 2 + api/pg/task.sql.go | 12 + api/pg/task_group.sql.go | 12 + api/query/task.sql | 3 + api/query/task_group.sql | 3 + web/.eslintrc.json | 1 + web/package.json | 1 + web/src/Projects/Project/index.tsx | 47 +- web/src/citadel.d.ts | 5 + web/src/shared/components/AddList/Styles.ts | 2 + web/src/shared/components/AddList/index.tsx | 43 +- .../ListActions/ListActions.stories.tsx | 3 +- .../shared/components/ListActions/index.tsx | 6 +- .../shared/components/Lists/Lists.stories.tsx | 8 +- web/src/shared/components/Lists/Styles.ts | 4 +- web/src/shared/components/Lists/index.tsx | 217 ++++++---- .../PopupMenu/PopupMenu.stories.tsx | 2 +- web/src/shared/generated/graphql.tsx | 90 +++- .../shared/graphql/deleteTaskGroup.graphqls | 13 + web/src/shared/utils/arrays.ts | 22 - web/src/shared/utils/draggables.ts | 56 ++- web/yarn.lock | 5 + 27 files changed, 824 insertions(+), 221 deletions(-) create mode 100644 web/src/shared/graphql/deleteTaskGroup.graphqls delete mode 100644 web/src/shared/utils/arrays.ts diff --git a/api/graph/generated.go b/api/graph/generated.go index d66b8dd..ba66368 100644 --- a/api/graph/generated.go +++ b/api/graph/generated.go @@ -50,6 +50,12 @@ type DirectiveRoot struct { } type ComplexityRoot struct { + DeleteTaskGroupPayload struct { + AffectedRows func(childComplexity int) int + Ok func(childComplexity int) int + TaskGroup func(childComplexity int) int + } + DeleteTaskPayload struct { TaskID func(childComplexity int) int } @@ -63,6 +69,7 @@ type ComplexityRoot struct { CreateTeam func(childComplexity int, input NewTeam) int CreateUserAccount func(childComplexity int, input NewUserAccount) int DeleteTask func(childComplexity int, input DeleteTaskInput) int + DeleteTaskGroup func(childComplexity int, input DeleteTaskGroupInput) int LogoutUser func(childComplexity int, input LogoutUser) int UpdateTaskGroupLocation func(childComplexity int, input NewTaskGroupLocation) int UpdateTaskLocation func(childComplexity int, input NewTaskLocation) int @@ -142,11 +149,12 @@ type MutationResolver interface { CreateProject(ctx context.Context, input NewProject) (*pg.Project, error) CreateTaskGroup(ctx context.Context, input NewTaskGroup) (*pg.TaskGroup, error) UpdateTaskGroupLocation(ctx context.Context, input NewTaskGroupLocation) (*pg.TaskGroup, error) + DeleteTaskGroup(ctx context.Context, input DeleteTaskGroupInput) (*DeleteTaskGroupPayload, error) CreateTask(ctx context.Context, input NewTask) (*pg.Task, error) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*pg.Task, error) - LogoutUser(ctx context.Context, input LogoutUser) (bool, error) UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) + LogoutUser(ctx context.Context, input LogoutUser) (bool, error) } type OrganizationResolver interface { Teams(ctx context.Context, obj *pg.Organization) ([]pg.Team, error) @@ -192,6 +200,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in _ = ec switch typeName + "." + field { + case "DeleteTaskGroupPayload.affectedRows": + if e.complexity.DeleteTaskGroupPayload.AffectedRows == nil { + break + } + + return e.complexity.DeleteTaskGroupPayload.AffectedRows(childComplexity), true + + case "DeleteTaskGroupPayload.ok": + if e.complexity.DeleteTaskGroupPayload.Ok == nil { + break + } + + return e.complexity.DeleteTaskGroupPayload.Ok(childComplexity), true + + case "DeleteTaskGroupPayload.taskGroup": + if e.complexity.DeleteTaskGroupPayload.TaskGroup == nil { + break + } + + return e.complexity.DeleteTaskGroupPayload.TaskGroup(childComplexity), true + case "DeleteTaskPayload.taskID": if e.complexity.DeleteTaskPayload.TaskID == nil { break @@ -295,6 +324,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.DeleteTask(childComplexity, args["input"].(DeleteTaskInput)), true + case "Mutation.deleteTaskGroup": + if e.complexity.Mutation.DeleteTaskGroup == nil { + break + } + + args, err := ec.field_Mutation_deleteTaskGroup_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteTaskGroup(childComplexity, args["input"].(DeleteTaskGroupInput)), true + case "Mutation.logoutUser": if e.complexity.Mutation.LogoutUser == nil { break @@ -845,19 +886,37 @@ input NewTaskGroupLocation { position: Float! } +input DeleteTaskGroupInput { + taskGroupID: UUID! +} + +type DeleteTaskGroupPayload { + ok: Boolean! + affectedRows: Int! + taskGroup: TaskGroup! +} + type Mutation { createRefreshToken(input: NewRefreshToken!): RefreshToken! + createUserAccount(input: NewUserAccount!): UserAccount! + createOrganization(input: NewOrganization!): Organization! + createTeam(input: NewTeam!): Team! + createProject(input: NewProject!): Project! + createTaskGroup(input: NewTaskGroup!): TaskGroup! updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup! + deleteTaskGroup(input: DeleteTaskGroupInput!): DeleteTaskGroupPayload! + createTask(input: NewTask!): Task! updateTaskLocation(input: NewTaskLocation!): Task! - logoutUser(input: LogoutUser!): Boolean! updateTaskName(input: UpdateTaskName!): Task! deleteTask(input: DeleteTaskInput!): DeleteTaskPayload! + + logoutUser(input: LogoutUser!): Boolean! } `, BuiltIn: false}, } @@ -965,6 +1024,20 @@ func (ec *executionContext) field_Mutation_createUserAccount_args(ctx context.Co return args, nil } +func (ec *executionContext) field_Mutation_deleteTaskGroup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 DeleteTaskGroupInput + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalNDeleteTaskGroupInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskGroupInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_deleteTask_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1127,6 +1200,108 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg // region **************************** field.gotpl ***************************** +func (ec *executionContext) _DeleteTaskGroupPayload_ok(ctx context.Context, field graphql.CollectedField, obj *DeleteTaskGroupPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTaskGroupPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Ok, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) _DeleteTaskGroupPayload_affectedRows(ctx context.Context, field graphql.CollectedField, obj *DeleteTaskGroupPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTaskGroupPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.AffectedRows, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) _DeleteTaskGroupPayload_taskGroup(ctx context.Context, field graphql.CollectedField, obj *DeleteTaskGroupPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTaskGroupPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.TaskGroup, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*pg.TaskGroup) + fc.Result = res + return ec.marshalNTaskGroup2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskGroup(ctx, field.Selections, res) +} + func (ec *executionContext) _DeleteTaskPayload_taskID(ctx context.Context, field graphql.CollectedField, obj *DeleteTaskPayload) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -1448,6 +1623,47 @@ func (ec *executionContext) _Mutation_updateTaskGroupLocation(ctx context.Contex return ec.marshalNTaskGroup2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskGroup(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_deleteTaskGroup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_deleteTaskGroup_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteTaskGroup(rctx, args["input"].(DeleteTaskGroupInput)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*DeleteTaskGroupPayload) + fc.Result = res + return ec.marshalNDeleteTaskGroupPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskGroupPayload(ctx, field.Selections, res) +} + func (ec *executionContext) _Mutation_createTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -1530,47 +1746,6 @@ func (ec *executionContext) _Mutation_updateTaskLocation(ctx context.Context, fi return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res) } -func (ec *executionContext) _Mutation_logoutUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - fc := &graphql.FieldContext{ - Object: "Mutation", - Field: field, - Args: nil, - IsMethod: true, - } - - ctx = graphql.WithFieldContext(ctx, fc) - rawArgs := field.ArgumentMap(ec.Variables) - args, err := ec.field_Mutation_logoutUser_args(ctx, rawArgs) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - fc.Args = args - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.Mutation().LogoutUser(rctx, args["input"].(LogoutUser)) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(bool) - fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) -} - func (ec *executionContext) _Mutation_updateTaskName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -1653,6 +1828,47 @@ func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field grap return ec.marshalNDeleteTaskPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskPayload(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_logoutUser(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_logoutUser_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().LogoutUser(rctx, args["input"].(LogoutUser)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + func (ec *executionContext) _Organization_organizationID(ctx context.Context, field graphql.CollectedField, obj *pg.Organization) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -4158,6 +4374,24 @@ func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.Co // region **************************** input.gotpl ***************************** +func (ec *executionContext) unmarshalInputDeleteTaskGroupInput(ctx context.Context, obj interface{}) (DeleteTaskGroupInput, error) { + var it DeleteTaskGroupInput + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "taskGroupID": + var err error + it.TaskGroupID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputDeleteTaskInput(ctx context.Context, obj interface{}) (DeleteTaskInput, error) { var it DeleteTaskInput var asMap = obj.(map[string]interface{}) @@ -4514,6 +4748,43 @@ func (ec *executionContext) unmarshalInputUpdateTaskName(ctx context.Context, ob // region **************************** object.gotpl **************************** +var deleteTaskGroupPayloadImplementors = []string{"DeleteTaskGroupPayload"} + +func (ec *executionContext) _DeleteTaskGroupPayload(ctx context.Context, sel ast.SelectionSet, obj *DeleteTaskGroupPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deleteTaskGroupPayloadImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DeleteTaskGroupPayload") + case "ok": + out.Values[i] = ec._DeleteTaskGroupPayload_ok(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "affectedRows": + out.Values[i] = ec._DeleteTaskGroupPayload_affectedRows(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "taskGroup": + out.Values[i] = ec._DeleteTaskGroupPayload_taskGroup(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var deleteTaskPayloadImplementors = []string{"DeleteTaskPayload"} func (ec *executionContext) _DeleteTaskPayload(ctx context.Context, sel ast.SelectionSet, obj *DeleteTaskPayload) graphql.Marshaler { @@ -4591,6 +4862,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "deleteTaskGroup": + out.Values[i] = ec._Mutation_deleteTaskGroup(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } case "createTask": out.Values[i] = ec._Mutation_createTask(ctx, field) if out.Values[i] == graphql.Null { @@ -4601,11 +4877,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } - case "logoutUser": - out.Values[i] = ec._Mutation_logoutUser(ctx, field) - if out.Values[i] == graphql.Null { - invalids++ - } case "updateTaskName": out.Values[i] = ec._Mutation_updateTaskName(ctx, field) if out.Values[i] == graphql.Null { @@ -4616,6 +4887,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "logoutUser": + out.Values[i] = ec._Mutation_logoutUser(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -5396,6 +5672,24 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se return res } +func (ec *executionContext) unmarshalNDeleteTaskGroupInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskGroupInput(ctx context.Context, v interface{}) (DeleteTaskGroupInput, error) { + return ec.unmarshalInputDeleteTaskGroupInput(ctx, v) +} + +func (ec *executionContext) marshalNDeleteTaskGroupPayload2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskGroupPayload(ctx context.Context, sel ast.SelectionSet, v DeleteTaskGroupPayload) graphql.Marshaler { + return ec._DeleteTaskGroupPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNDeleteTaskGroupPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskGroupPayload(ctx context.Context, sel ast.SelectionSet, v *DeleteTaskGroupPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._DeleteTaskGroupPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNDeleteTaskInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskInput(ctx context.Context, v interface{}) (DeleteTaskInput, error) { return ec.unmarshalInputDeleteTaskInput(ctx, v) } @@ -5450,6 +5744,20 @@ func (ec *executionContext) marshalNID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx c return res } +func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { + return graphql.UnmarshalInt(v) +} + +func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + res := graphql.MarshalInt(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + } + return res +} + func (ec *executionContext) unmarshalNLogoutUser2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐLogoutUser(ctx context.Context, v interface{}) (LogoutUser, error) { return ec.unmarshalInputLogoutUser(ctx, v) } diff --git a/api/graph/models_gen.go b/api/graph/models_gen.go index 07a839f..250208f 100644 --- a/api/graph/models_gen.go +++ b/api/graph/models_gen.go @@ -4,8 +4,19 @@ package graph import ( "github.com/google/uuid" + "github.com/jordanknott/project-citadel/api/pg" ) +type DeleteTaskGroupInput struct { + TaskGroupID uuid.UUID `json:"taskGroupID"` +} + +type DeleteTaskGroupPayload struct { + Ok bool `json:"ok"` + AffectedRows int `json:"affectedRows"` + TaskGroup *pg.TaskGroup `json:"taskGroup"` +} + type DeleteTaskInput struct { TaskID string `json:"taskID"` } diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index 00d7cdd..452c952 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -141,17 +141,35 @@ input NewTaskGroupLocation { position: Float! } +input DeleteTaskGroupInput { + taskGroupID: UUID! +} + +type DeleteTaskGroupPayload { + ok: Boolean! + affectedRows: Int! + taskGroup: TaskGroup! +} + type Mutation { createRefreshToken(input: NewRefreshToken!): RefreshToken! + createUserAccount(input: NewUserAccount!): UserAccount! + createOrganization(input: NewOrganization!): Organization! + createTeam(input: NewTeam!): Team! + createProject(input: NewProject!): Project! + createTaskGroup(input: NewTaskGroup!): TaskGroup! updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup! + deleteTaskGroup(input: DeleteTaskGroupInput!): DeleteTaskGroupPayload! + createTask(input: NewTask!): Task! updateTaskLocation(input: NewTaskLocation!): Task! - logoutUser(input: LogoutUser!): Boolean! updateTaskName(input: UpdateTaskName!): Task! deleteTask(input: DeleteTaskInput!): DeleteTaskPayload! + + logoutUser(input: LogoutUser!): Boolean! } diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index e76aa2d..dc2785f 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -73,6 +73,22 @@ func (r *mutationResolver) UpdateTaskGroupLocation(ctx context.Context, input Ne return &taskGroup, err } +func (r *mutationResolver) DeleteTaskGroup(ctx context.Context, input DeleteTaskGroupInput) (*DeleteTaskGroupPayload, error) { + deletedTasks, err := r.Repository.DeleteTasksByTaskGroupID(ctx, input.TaskGroupID) + if err != nil { + return &DeleteTaskGroupPayload{}, err + } + taskGroup, err := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID) + if err != nil { + return &DeleteTaskGroupPayload{}, err + } + deletedTaskGroups, err := r.Repository.DeleteTaskGroupByID(ctx, input.TaskGroupID) + if err != nil { + return &DeleteTaskGroupPayload{}, err + } + return &DeleteTaskGroupPayload{true, int(deletedTasks + deletedTaskGroups), &taskGroup}, nil +} + func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*pg.Task, error) { taskGroupID, err := uuid.Parse(input.TaskGroupID) createdAt := time.Now().UTC() @@ -98,16 +114,6 @@ func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTask return &task, err } -func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) { - userID, err := uuid.Parse(input.UserID) - if err != nil { - return false, err - } - - err = r.Repository.DeleteRefreshTokenByUserID(ctx, userID) - return true, err -} - func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error) { taskID, err := uuid.Parse(input.TaskID) if err != nil { @@ -133,6 +139,16 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput return &DeleteTaskPayload{taskID.String()}, nil } +func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) { + userID, err := uuid.Parse(input.UserID) + if err != nil { + return false, err + } + + err = r.Repository.DeleteRefreshTokenByUserID(ctx, userID) + return true, err +} + func (r *organizationResolver) Teams(ctx context.Context, obj *pg.Organization) ([]pg.Team, error) { teams, err := r.Repository.GetTeamsForOrganization(ctx, obj.OrganizationID) return teams, err diff --git a/api/pg/pg.go b/api/pg/pg.go index b46fa2f..d0cd7ee 100644 --- a/api/pg/pg.go +++ b/api/pg/pg.go @@ -11,26 +11,35 @@ type Repository interface { DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error) GetAllTeams(ctx context.Context) ([]Team, error) + CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) GetAllProjects(ctx context.Context) ([]Project, error) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error) - GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) - UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error) - GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error) + CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error) + GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error) GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error) + GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) + CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error) GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error) DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error + + DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) + DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) + UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error) CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error) GetAllTaskGroups(ctx context.Context) ([]TaskGroup, error) - GetAllOrganizations(ctx context.Context) ([]Organization, error) GetTaskGroupsForProject(ctx context.Context, projectID uuid.UUID) ([]TaskGroup, error) GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error) + + GetAllOrganizations(ctx context.Context) ([]Organization, error) CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (Organization, error) + GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error) + CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error) GetAllTasks(ctx context.Context) ([]Task, error) GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error) diff --git a/api/pg/querier.go b/api/pg/querier.go index 6708785..9cfc696 100644 --- a/api/pg/querier.go +++ b/api/pg/querier.go @@ -20,6 +20,8 @@ type Querier interface { DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error + DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) + DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error GetAllOrganizations(ctx context.Context) ([]Organization, error) GetAllProjects(ctx context.Context) ([]Project, error) diff --git a/api/pg/task.sql.go b/api/pg/task.sql.go index 03dfff5..f257abf 100644 --- a/api/pg/task.sql.go +++ b/api/pg/task.sql.go @@ -49,6 +49,18 @@ func (q *Queries) DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error { return err } +const deleteTasksByTaskGroupID = `-- name: DeleteTasksByTaskGroupID :execrows +DELETE FROM task where task_group_id = $1 +` + +func (q *Queries) DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteTasksByTaskGroupID, taskGroupID) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + const getAllTasks = `-- name: GetAllTasks :many SELECT task_id, task_group_id, created_at, name, position FROM task ` diff --git a/api/pg/task_group.sql.go b/api/pg/task_group.sql.go index 8c4411c..a7e1105 100644 --- a/api/pg/task_group.sql.go +++ b/api/pg/task_group.sql.go @@ -40,6 +40,18 @@ func (q *Queries) CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams return i, err } +const deleteTaskGroupByID = `-- name: DeleteTaskGroupByID :execrows +DELETE FROM task_group WHERE task_group_id = $1 +` + +func (q *Queries) DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) { + result, err := q.db.ExecContext(ctx, deleteTaskGroupByID, taskGroupID) + if err != nil { + return 0, err + } + return result.RowsAffected() +} + const getAllTaskGroups = `-- name: GetAllTaskGroups :many SELECT task_group_id, project_id, created_at, name, position FROM task_group ` diff --git a/api/query/task.sql b/api/query/task.sql index fae98ee..8d78e14 100644 --- a/api/query/task.sql +++ b/api/query/task.sql @@ -16,3 +16,6 @@ DELETE FROM task WHERE task_id = $1; -- name: UpdateTaskName :one UPDATE task SET name = $2 WHERE task_id = $1 RETURNING *; + +-- name: DeleteTasksByTaskGroupID :execrows +DELETE FROM task where task_group_id = $1; diff --git a/api/query/task_group.sql b/api/query/task_group.sql index 70ec5a6..23138a4 100644 --- a/api/query/task_group.sql +++ b/api/query/task_group.sql @@ -13,3 +13,6 @@ SELECT * FROM task_group WHERE task_group_id = $1; -- name: UpdateTaskGroupLocation :one UPDATE task_group SET position = $2 WHERE task_group_id = $1 RETURNING *; + +-- name: DeleteTaskGroupByID :execrows +DELETE FROM task_group WHERE task_group_id = $1; diff --git a/web/.eslintrc.json b/web/.eslintrc.json index 8b7a6bc..55c78e7 100644 --- a/web/.eslintrc.json +++ b/web/.eslintrc.json @@ -33,6 +33,7 @@ "react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }], "react/prop-types": 0, "react/jsx-props-no-spreading": "off", + "no-param-reassign": "off", "import/extensions": [ "error", "ignorePackages", diff --git a/web/package.json b/web/package.json index 316d9bc..f60fb04 100644 --- a/web/package.json +++ b/web/package.json @@ -39,6 +39,7 @@ "graphql": "^15.0.0", "graphql-tag": "^2.10.3", "history": "^4.10.1", + "immer": "^6.0.3", "lodash": "^4.17.15", "prop-types": "^15.7.2", "react": "^16.12.0", diff --git a/web/src/Projects/Project/index.tsx b/web/src/Projects/Project/index.tsx index 14779e2..b50b0c0 100644 --- a/web/src/Projects/Project/index.tsx +++ b/web/src/Projects/Project/index.tsx @@ -1,4 +1,5 @@ import React, { useState } from 'react'; +import produce from 'immer'; import styled from 'styled-components/macro'; import { useParams } from 'react-router-dom'; import { @@ -9,6 +10,7 @@ import { useUpdateTaskLocationMutation, useUpdateTaskGroupLocationMutation, useCreateTaskGroupMutation, + useDeleteTaskGroupMutation, } from 'shared/generated/graphql'; import Navbar from 'App/Navbar'; @@ -79,6 +81,26 @@ const Project = () => { const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState); const [updateTaskLocation] = useUpdateTaskLocationMutation(); const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation(); + const [deleteTaskGroup] = useDeleteTaskGroupMutation({ + onCompleted: deletedTaskGroupData => { + const nextState = produce(listsData, (draftState: State) => { + delete draftState.columns[deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID]; + const filteredTasks = Object.keys(listsData.tasks) + .filter( + taskID => + listsData.tasks[taskID].taskGroup.taskGroupID !== + deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID, + ) + .reduce((obj: TaskState, key: string) => { + obj[key] = listsData.tasks[key]; + return obj; + }, {}); + draftState.tasks = filteredTasks; + }); + + setListsData(nextState); + }, + }); const [createTaskGroup] = useCreateTaskGroupMutation({ onCompleted: newTaskGroupData => { const newListsData = { @@ -167,9 +189,14 @@ const Project = () => { setListsData(newListsData); }, }); - const onCardDrop = (droppedTask: any) => { + const onCardDrop = (droppedTask: Task) => { + console.log(droppedTask); updateTaskLocation({ - variables: { taskID: droppedTask.taskID, taskGroupID: droppedTask.taskGroupID, position: droppedTask.position }, + variables: { + taskID: droppedTask.taskID, + taskGroupID: droppedTask.taskGroup.taskGroupID, + position: droppedTask.position, + }, }); const newState = { ...listsData, @@ -229,6 +256,14 @@ const Project = () => { { + setPopupData({ + isOpen: true, + left: pos.left, + top: pos.top + size.height + 5, + taskGroupID, + }); + }} onQuickEditorOpen={onQuickEditorOpen} onCardCreate={onCardCreate} {...listsData} @@ -271,7 +306,13 @@ const Project = () => { onClose={() => setPopupData(initialPopupState)} left={popupData.left} > - + { + deleteTaskGroup({ variables: { taskGroupID } }); + setPopupData(initialPopupState); + }} + /> )} diff --git a/web/src/citadel.d.ts b/web/src/citadel.d.ts index ffde3c4..0cccf87 100644 --- a/web/src/citadel.d.ts +++ b/web/src/citadel.d.ts @@ -1,3 +1,8 @@ +interface DraggableElement { + id: string; + position: number; +} + type ContextMenuEvent = { left: number; top: number; diff --git a/web/src/shared/components/AddList/Styles.ts b/web/src/shared/components/AddList/Styles.ts index 7ff1472..e67b286 100644 --- a/web/src/shared/components/AddList/Styles.ts +++ b/web/src/shared/components/AddList/Styles.ts @@ -1,6 +1,8 @@ import styled, { css } from 'styled-components'; import TextareaAutosize from 'react-autosize-textarea/lib'; +export const Container = styled.div``; + export const Wrapper = styled.div<{ editorOpen: boolean }>` display: inline-block; background-color: hsla(0, 0%, 100%, 0.24); diff --git a/web/src/shared/components/AddList/index.tsx b/web/src/shared/components/AddList/index.tsx index 8a143e9..85dc40c 100644 --- a/web/src/shared/components/AddList/index.tsx +++ b/web/src/shared/components/AddList/index.tsx @@ -3,6 +3,7 @@ import { Plus, Cross } from 'shared/icons'; import useOnOutsideClick from 'shared/hooks/onOutsideClick'; import { + Container, Wrapper, Placeholder, AddIconWrapper, @@ -79,26 +80,28 @@ const AddList: React.FC = ({ onSave }) => { useOnOutsideClick($wrapperRef, editorOpen, onOutsideClick, null); return ( - { - if (!editorOpen) { - setEditorOpen(true); - } - }} - > - {editorOpen ? ( - setEditorOpen(false)} onSave={onSave} /> - ) : ( - - - - - Add another list - - )} - + + { + if (!editorOpen) { + setEditorOpen(true); + } + }} + > + {editorOpen ? ( + setEditorOpen(false)} onSave={onSave} /> + ) : ( + + + + + Add another list + + )} + + ); }; diff --git a/web/src/shared/components/ListActions/ListActions.stories.tsx b/web/src/shared/components/ListActions/ListActions.stories.tsx index 9fb88fd..5fb8b62 100644 --- a/web/src/shared/components/ListActions/ListActions.stories.tsx +++ b/web/src/shared/components/ListActions/ListActions.stories.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { action } from '@storybook/addon-actions'; import ListActions from '.'; export default { @@ -13,5 +14,5 @@ export default { }; export const Default = () => { - return ; + return ; }; diff --git a/web/src/shared/components/ListActions/index.tsx b/web/src/shared/components/ListActions/index.tsx index 6cce235..0a0ef06 100644 --- a/web/src/shared/components/ListActions/index.tsx +++ b/web/src/shared/components/ListActions/index.tsx @@ -3,8 +3,10 @@ import { ListActionsWrapper, ListActionItemWrapper, ListActionItem, ListSeparato type Props = { taskGroupID: string; + + onArchiveTaskGroup: (taskGroupID: string) => void; }; -const LabelManager = ({ taskGroupID }: Props) => { +const LabelManager: React.FC = ({ taskGroupID, onArchiveTaskGroup }) => { return ( <> @@ -38,7 +40,7 @@ const LabelManager = ({ taskGroupID }: Props) => { - + onArchiveTaskGroup(taskGroupID)}> Archive This List diff --git a/web/src/shared/components/Lists/Lists.stories.tsx b/web/src/shared/components/Lists/Lists.stories.tsx index 2c06b3b..e9d6d80 100644 --- a/web/src/shared/components/Lists/Lists.stories.tsx +++ b/web/src/shared/components/Lists/Lists.stories.tsx @@ -70,25 +70,28 @@ export const Default = () => { ...listsData, tasks: { ...listsData.tasks, - [droppedTask.id]: droppedTask, + [droppedTask.taskGroupID]: droppedTask, }, }; console.log(newState); setListsData(newState); }; const onListDrop = (droppedColumn: any) => { + console.log(droppedColumn); const newState = { ...listsData, columns: { ...listsData.columns, - [droppedColumn.id]: droppedColumn, + [droppedColumn.taskGroupID]: droppedColumn, }, }; + console.log(newState); setListsData(newState); }; return ( { onCardDrop={onCardDrop} onListDrop={onListDrop} onCreateList={action('create list')} + onExtraMenuOpen={action('extra menu open')} /> ); }; diff --git a/web/src/shared/components/Lists/Styles.ts b/web/src/shared/components/Lists/Styles.ts index 2f5a384..69b13aa 100644 --- a/web/src/shared/components/Lists/Styles.ts +++ b/web/src/shared/components/Lists/Styles.ts @@ -1,7 +1,6 @@ import styled from 'styled-components'; export const Container = styled.div` - flex-grow: 1; user-select: none; white-space: nowrap; margin-bottom: 8px; @@ -10,4 +9,7 @@ export const Container = styled.div` padding-bottom: 8px; `; +export const BoardWrapper = styled.div` + display: flex; +`; export default Container; diff --git a/web/src/shared/components/Lists/index.tsx b/web/src/shared/components/Lists/index.tsx index cb76c69..1610461 100644 --- a/web/src/shared/components/Lists/index.tsx +++ b/web/src/shared/components/Lists/index.tsx @@ -11,7 +11,7 @@ import { getAfterDropDraggableList, } from 'shared/utils/draggables'; -import { Container } from './Styles'; +import { Container, BoardWrapper } from './Styles'; interface Columns { [key: string]: TaskGroup; @@ -23,25 +23,56 @@ interface Tasks { type Props = { columns: Columns; tasks: Tasks; - onCardDrop: any; - onListDrop: any; + onCardDrop: (task: Task) => void; + onListDrop: (taskGroup: TaskGroup) => void; onCardCreate: (taskGroupID: string, name: string) => void; onQuickEditorOpen: (e: ContextMenuEvent) => void; onCreateList: (listName: string) => void; + onExtraMenuOpen: (taskGroupID: string, pos: ElementPosition, size: ElementSize) => void; }; -const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEditorOpen, onCreateList }: Props) => { +const Lists: React.FC = ({ + columns, + tasks, + onCardDrop, + onListDrop, + onCardCreate, + onQuickEditorOpen, + onCreateList, + onExtraMenuOpen, +}) => { const onDragEnd = ({ draggableId, source, destination, type }: DropResult) => { if (typeof destination === 'undefined') return; if (!isPositionChanged(source, destination)) return; const isList = type === 'column'; const isSameList = destination.droppableId === source.droppableId; - const droppedDraggable = isList ? columns[draggableId] : tasks[draggableId]; + const droppedDraggable: DraggableElement = isList + ? { + id: draggableId, + position: columns[draggableId].position, + } + : { + id: draggableId, + position: tasks[draggableId].position, + }; const beforeDropDraggables = isList - ? getSortedDraggables(Object.values(columns)) - : getSortedDraggables(Object.values(tasks).filter((t: any) => t.taskGroupID === destination.droppableId)); + ? getSortedDraggables( + Object.values(columns).map(column => { + return { id: column.taskGroupID, position: column.position }; + }), + ) + : getSortedDraggables( + Object.values(tasks) + .filter((t: any) => t.taskGroup.taskGroupID === destination.droppableId) + .map(task => { + return { id: task.taskID, position: task.position }; + }), + ); + console.log(beforeDropDraggables); + console.log(destination); + console.log(droppedDraggable); const afterDropDraggables = getAfterDropDraggableList( beforeDropDraggables, droppedDraggable, @@ -49,16 +80,19 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd isSameList, destination, ); + console.log(afterDropDraggables); const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index); if (isList) { + const droppedList = columns[droppedDraggable.id]; onListDrop({ - ...droppedDraggable, + ...droppedList, position: newPosition, }); } else { + const droppedCard = tasks[droppedDraggable.id]; const newCard = { - ...droppedDraggable, + ...droppedCard, position: newPosition, taskGroupID: destination.droppableId, }; @@ -66,89 +100,96 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd } }; - const orderedColumns = getSortedDraggables(Object.values(columns)); + const orderedColumns = getSortedDraggables( + Object.values(columns).map(column => { + return { id: column.taskGroupID, position: column.position }; + }), + ); const [currentComposer, setCurrentComposer] = useState(''); return ( - - - {provided => ( - - {orderedColumns.map((column: TaskGroup, index: number) => { - const columnCards = getSortedDraggables( - Object.values(tasks).filter((t: Task) => t.taskGroup.taskGroupID === column.taskGroupID), - ); - return ( - - {columnDragProvided => ( - setCurrentComposer(id)} - isComposerOpen={currentComposer === column.taskGroupID} - onSaveName={name => console.log(name)} - index={index} - tasks={columnCards} - ref={columnDragProvided.innerRef} - wrapperProps={columnDragProvided.draggableProps} - headerProps={columnDragProvided.dragHandleProps} - onExtraMenuOpen={(taskGroupID, pos, size) => console.log(taskGroupID, pos, size)} - > - - {columnDropProvided => ( - - {columnCards.map((task: Task, taskIndex: any) => { - return ( - - {taskProvided => { - return ( - console.log(e)} - onContextMenu={onQuickEditorOpen} - /> - ); - }} - - ); - })} - {columnDropProvided.placeholder} + + + + {provided => ( + + {orderedColumns.map((columnDraggable, index: number) => { + const column = columns[columnDraggable.id]; + const columnCards = Object.values(tasks) + .filter((t: Task) => t.taskGroup.taskGroupID === column.taskGroupID) + .sort((a, b) => a.position - b.position); + return ( + + {columnDragProvided => ( + setCurrentComposer(id)} + isComposerOpen={currentComposer === column.taskGroupID} + onSaveName={name => console.log(name)} + index={index} + tasks={columnCards} + ref={columnDragProvided.innerRef} + wrapperProps={columnDragProvided.draggableProps} + headerProps={columnDragProvided.dragHandleProps} + onExtraMenuOpen={onExtraMenuOpen} + > + + {columnDropProvided => ( + + {columnCards.map((task: Task, taskIndex: any) => { + return ( + + {taskProvided => { + return ( + console.log(e)} + onContextMenu={onQuickEditorOpen} + /> + ); + }} + + ); + })} + {columnDropProvided.placeholder} - {currentComposer === column.taskGroupID && ( - { - setCurrentComposer(''); - }} - onCreateCard={name => { - onCardCreate(column.taskGroupID, name); - }} - isOpen - /> - )} - - )} - - - )} - - ); - })} - {provided.placeholder} - - - )} - - + {currentComposer === column.taskGroupID && ( + { + setCurrentComposer(''); + }} + onCreateCard={name => { + onCardCreate(column.taskGroupID, name); + }} + isOpen + /> + )} + + )} + + + )} + + ); + })} + {provided.placeholder} + + )} + + + + ); }; diff --git a/web/src/shared/components/PopupMenu/PopupMenu.stories.tsx b/web/src/shared/components/PopupMenu/PopupMenu.stories.tsx index 4a24782..f78fea6 100644 --- a/web/src/shared/components/PopupMenu/PopupMenu.stories.tsx +++ b/web/src/shared/components/PopupMenu/PopupMenu.stories.tsx @@ -77,7 +77,7 @@ export const ListActionsPopup = () => { onClose={() => setPopupData(initalState)} left={popupData.left} > - + )}