feature: add due dates to tasks
This commit is contained in:
parent
a12e9c1e50
commit
b6f0e8b6b2
@ -94,6 +94,7 @@ type ComplexityRoot struct {
|
|||||||
UpdateProjectLabelName func(childComplexity int, input UpdateProjectLabelName) int
|
UpdateProjectLabelName func(childComplexity int, input UpdateProjectLabelName) int
|
||||||
UpdateProjectName func(childComplexity int, input *UpdateProjectName) int
|
UpdateProjectName func(childComplexity int, input *UpdateProjectName) int
|
||||||
UpdateTaskDescription func(childComplexity int, input UpdateTaskDescriptionInput) int
|
UpdateTaskDescription func(childComplexity int, input UpdateTaskDescriptionInput) int
|
||||||
|
UpdateTaskDueDate func(childComplexity int, input UpdateTaskDueDate) int
|
||||||
UpdateTaskGroupLocation func(childComplexity int, input NewTaskGroupLocation) int
|
UpdateTaskGroupLocation func(childComplexity int, input NewTaskGroupLocation) int
|
||||||
UpdateTaskGroupName func(childComplexity int, input UpdateTaskGroupName) int
|
UpdateTaskGroupName func(childComplexity int, input UpdateTaskGroupName) int
|
||||||
UpdateTaskLocation func(childComplexity int, input NewTaskLocation) int
|
UpdateTaskLocation func(childComplexity int, input NewTaskLocation) int
|
||||||
@ -153,6 +154,7 @@ type ComplexityRoot struct {
|
|||||||
Assigned func(childComplexity int) int
|
Assigned func(childComplexity int) int
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
Description func(childComplexity int) int
|
Description func(childComplexity int) int
|
||||||
|
DueDate func(childComplexity int) int
|
||||||
ID func(childComplexity int) int
|
ID func(childComplexity int) int
|
||||||
Labels func(childComplexity int) int
|
Labels func(childComplexity int) int
|
||||||
Name func(childComplexity int) int
|
Name func(childComplexity int) int
|
||||||
@ -228,6 +230,7 @@ type MutationResolver interface {
|
|||||||
UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*pg.Task, error)
|
UpdateTaskDescription(ctx context.Context, input UpdateTaskDescriptionInput) (*pg.Task, error)
|
||||||
UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error)
|
UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error)
|
||||||
UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error)
|
UpdateTaskName(ctx context.Context, input UpdateTaskName) (*pg.Task, error)
|
||||||
|
UpdateTaskDueDate(ctx context.Context, input UpdateTaskDueDate) (*pg.Task, error)
|
||||||
DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error)
|
DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error)
|
||||||
AssignTask(ctx context.Context, input *AssignTaskInput) (*pg.Task, error)
|
AssignTask(ctx context.Context, input *AssignTaskInput) (*pg.Task, error)
|
||||||
UnassignTask(ctx context.Context, input *UnassignTaskInput) (*pg.Task, error)
|
UnassignTask(ctx context.Context, input *UnassignTaskInput) (*pg.Task, error)
|
||||||
@ -267,6 +270,7 @@ type TaskResolver interface {
|
|||||||
TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error)
|
TaskGroup(ctx context.Context, obj *pg.Task) (*pg.TaskGroup, error)
|
||||||
|
|
||||||
Description(ctx context.Context, obj *pg.Task) (*string, error)
|
Description(ctx context.Context, obj *pg.Task) (*string, error)
|
||||||
|
DueDate(ctx context.Context, obj *pg.Task) (*time.Time, error)
|
||||||
Assigned(ctx context.Context, obj *pg.Task) ([]ProjectMember, error)
|
Assigned(ctx context.Context, obj *pg.Task) ([]ProjectMember, error)
|
||||||
Labels(ctx context.Context, obj *pg.Task) ([]pg.TaskLabel, error)
|
Labels(ctx context.Context, obj *pg.Task) ([]pg.TaskLabel, error)
|
||||||
}
|
}
|
||||||
@ -619,6 +623,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Mutation.UpdateTaskDescription(childComplexity, args["input"].(UpdateTaskDescriptionInput)), true
|
return e.complexity.Mutation.UpdateTaskDescription(childComplexity, args["input"].(UpdateTaskDescriptionInput)), true
|
||||||
|
|
||||||
|
case "Mutation.updateTaskDueDate":
|
||||||
|
if e.complexity.Mutation.UpdateTaskDueDate == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_updateTaskDueDate_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.UpdateTaskDueDate(childComplexity, args["input"].(UpdateTaskDueDate)), true
|
||||||
|
|
||||||
case "Mutation.updateTaskGroupLocation":
|
case "Mutation.updateTaskGroupLocation":
|
||||||
if e.complexity.Mutation.UpdateTaskGroupLocation == nil {
|
if e.complexity.Mutation.UpdateTaskGroupLocation == nil {
|
||||||
break
|
break
|
||||||
@ -925,6 +941,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||||||
|
|
||||||
return e.complexity.Task.Description(childComplexity), true
|
return e.complexity.Task.Description(childComplexity), true
|
||||||
|
|
||||||
|
case "Task.dueDate":
|
||||||
|
if e.complexity.Task.DueDate == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Task.DueDate(childComplexity), true
|
||||||
|
|
||||||
case "Task.id":
|
case "Task.id":
|
||||||
if e.complexity.Task.ID == nil {
|
if e.complexity.Task.ID == nil {
|
||||||
break
|
break
|
||||||
@ -1271,6 +1294,7 @@ type Task {
|
|||||||
name: String!
|
name: String!
|
||||||
position: Float!
|
position: Float!
|
||||||
description: String
|
description: String
|
||||||
|
dueDate: Time
|
||||||
assigned: [ProjectMember!]!
|
assigned: [ProjectMember!]!
|
||||||
labels: [TaskLabel!]!
|
labels: [TaskLabel!]!
|
||||||
}
|
}
|
||||||
@ -1448,6 +1472,11 @@ input UpdateTaskGroupName {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input UpdateTaskDueDate {
|
||||||
|
taskID: UUID!
|
||||||
|
dueDate: Time
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||||
|
|
||||||
@ -1478,6 +1507,7 @@ type Mutation {
|
|||||||
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
|
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
|
||||||
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
|
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
|
||||||
updateTaskName(input: UpdateTaskName!): Task!
|
updateTaskName(input: UpdateTaskName!): Task!
|
||||||
|
updateTaskDueDate(input: UpdateTaskDueDate!): Task!
|
||||||
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
||||||
assignTask(input: AssignTaskInput): Task!
|
assignTask(input: AssignTaskInput): Task!
|
||||||
unassignTask(input: UnassignTaskInput): Task!
|
unassignTask(input: UnassignTaskInput): Task!
|
||||||
@ -1786,6 +1816,20 @@ func (ec *executionContext) field_Mutation_updateTaskDescription_args(ctx contex
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_updateTaskDueDate_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 UpdateTaskDueDate
|
||||||
|
if tmp, ok := rawArgs["input"]; ok {
|
||||||
|
arg0, err = ec.unmarshalNUpdateTaskDueDate2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskDueDate(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["input"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_updateTaskGroupLocation_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_updateTaskGroupLocation_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
@ -3115,6 +3159,47 @@ func (ec *executionContext) _Mutation_updateTaskName(ctx context.Context, field
|
|||||||
return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res)
|
return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_updateTaskDueDate(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_updateTaskDueDate_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().UpdateTaskDueDate(rctx, args["input"].(UpdateTaskDueDate))
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
if !graphql.HasFieldError(ctx, fc) {
|
||||||
|
ec.Errorf(ctx, "must not be null")
|
||||||
|
}
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*pg.Task)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNTask2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTask(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_deleteTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -4619,6 +4704,37 @@ func (ec *executionContext) _Task_description(ctx context.Context, field graphql
|
|||||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Task_dueDate(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))
|
||||||
|
ret = graphql.Null
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fc := &graphql.FieldContext{
|
||||||
|
Object: "Task",
|
||||||
|
Field: field,
|
||||||
|
Args: nil,
|
||||||
|
IsMethod: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
|
ctx = rctx // use context from middleware stack in children
|
||||||
|
return ec.resolvers.Task().DueDate(rctx, obj)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*time.Time)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Task_assigned(ctx context.Context, field graphql.CollectedField, obj *pg.Task) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Task_assigned(ctx context.Context, field graphql.CollectedField, obj *pg.Task) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -7166,6 +7282,30 @@ func (ec *executionContext) unmarshalInputUpdateTaskDescriptionInput(ctx context
|
|||||||
return it, nil
|
return it, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputUpdateTaskDueDate(ctx context.Context, obj interface{}) (UpdateTaskDueDate, error) {
|
||||||
|
var it UpdateTaskDueDate
|
||||||
|
var asMap = obj.(map[string]interface{})
|
||||||
|
|
||||||
|
for k, v := range asMap {
|
||||||
|
switch k {
|
||||||
|
case "taskID":
|
||||||
|
var err error
|
||||||
|
it.TaskID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "dueDate":
|
||||||
|
var err error
|
||||||
|
it.DueDate, err = ec.unmarshalOTime2ᚖtimeᚐTime(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputUpdateTaskGroupName(ctx context.Context, obj interface{}) (UpdateTaskGroupName, error) {
|
func (ec *executionContext) unmarshalInputUpdateTaskGroupName(ctx context.Context, obj interface{}) (UpdateTaskGroupName, error) {
|
||||||
var it UpdateTaskGroupName
|
var it UpdateTaskGroupName
|
||||||
var asMap = obj.(map[string]interface{})
|
var asMap = obj.(map[string]interface{})
|
||||||
@ -7462,6 +7602,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
case "updateTaskDueDate":
|
||||||
|
out.Values[i] = ec._Mutation_updateTaskDueDate(ctx, field)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
case "deleteTask":
|
case "deleteTask":
|
||||||
out.Values[i] = ec._Mutation_deleteTask(ctx, field)
|
out.Values[i] = ec._Mutation_deleteTask(ctx, field)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
@ -8012,6 +8157,17 @@ func (ec *executionContext) _Task(ctx context.Context, sel ast.SelectionSet, obj
|
|||||||
res = ec._Task_description(ctx, field, obj)
|
res = ec._Task_description(ctx, field, obj)
|
||||||
return res
|
return res
|
||||||
})
|
})
|
||||||
|
case "dueDate":
|
||||||
|
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_dueDate(ctx, field, obj)
|
||||||
|
return res
|
||||||
|
})
|
||||||
case "assigned":
|
case "assigned":
|
||||||
field := field
|
field := field
|
||||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||||
@ -9265,6 +9421,10 @@ func (ec *executionContext) unmarshalNUpdateTaskDescriptionInput2githubᚗcomᚋ
|
|||||||
return ec.unmarshalInputUpdateTaskDescriptionInput(ctx, v)
|
return ec.unmarshalInputUpdateTaskDescriptionInput(ctx, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNUpdateTaskDueDate2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskDueDate(ctx context.Context, v interface{}) (UpdateTaskDueDate, error) {
|
||||||
|
return ec.unmarshalInputUpdateTaskDueDate(ctx, v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNUpdateTaskGroupName2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskGroupName(ctx context.Context, v interface{}) (UpdateTaskGroupName, error) {
|
func (ec *executionContext) unmarshalNUpdateTaskGroupName2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskGroupName(ctx context.Context, v interface{}) (UpdateTaskGroupName, error) {
|
||||||
return ec.unmarshalInputUpdateTaskGroupName(ctx, v)
|
return ec.unmarshalInputUpdateTaskGroupName(ctx, v)
|
||||||
}
|
}
|
||||||
@ -9658,6 +9818,29 @@ func (ec *executionContext) marshalOString2ᚖstring(ctx context.Context, sel as
|
|||||||
return ec.marshalOString2string(ctx, sel, *v)
|
return ec.marshalOString2string(ctx, sel, *v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalOTime2timeᚐTime(ctx context.Context, v interface{}) (time.Time, error) {
|
||||||
|
return graphql.UnmarshalTime(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalOTime2timeᚐTime(ctx context.Context, sel ast.SelectionSet, v time.Time) graphql.Marshaler {
|
||||||
|
return graphql.MarshalTime(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalOTime2ᚖtimeᚐTime(ctx context.Context, v interface{}) (*time.Time, error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
res, err := ec.unmarshalOTime2timeᚐTime(ctx, v)
|
||||||
|
return &res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalOTime2ᚖtimeᚐTime(ctx context.Context, sel ast.SelectionSet, v *time.Time) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
return ec.marshalOTime2timeᚐTime(ctx, sel, *v)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalOUnassignTaskInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUnassignTaskInput(ctx context.Context, v interface{}) (UnassignTaskInput, error) {
|
func (ec *executionContext) unmarshalOUnassignTaskInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUnassignTaskInput(ctx context.Context, v interface{}) (UnassignTaskInput, error) {
|
||||||
return ec.unmarshalInputUnassignTaskInput(ctx, v)
|
return ec.unmarshalInputUnassignTaskInput(ctx, v)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
package graph
|
package graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jordanknott/project-citadel/api/pg"
|
"github.com/jordanknott/project-citadel/api/pg"
|
||||||
)
|
)
|
||||||
@ -168,6 +170,11 @@ type UpdateTaskDescriptionInput struct {
|
|||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateTaskDueDate struct {
|
||||||
|
TaskID uuid.UUID `json:"taskID"`
|
||||||
|
DueDate *time.Time `json:"dueDate"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateTaskGroupName struct {
|
type UpdateTaskGroupName struct {
|
||||||
TaskGroupID uuid.UUID `json:"taskGroupID"`
|
TaskGroupID uuid.UUID `json:"taskGroupID"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -84,6 +84,7 @@ type Task {
|
|||||||
name: String!
|
name: String!
|
||||||
position: Float!
|
position: Float!
|
||||||
description: String
|
description: String
|
||||||
|
dueDate: Time
|
||||||
assigned: [ProjectMember!]!
|
assigned: [ProjectMember!]!
|
||||||
labels: [TaskLabel!]!
|
labels: [TaskLabel!]!
|
||||||
}
|
}
|
||||||
@ -261,6 +262,11 @@ input UpdateTaskGroupName {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input UpdateTaskDueDate {
|
||||||
|
taskID: UUID!
|
||||||
|
dueDate: Time
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||||
|
|
||||||
@ -291,6 +297,7 @@ type Mutation {
|
|||||||
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
|
updateTaskDescription(input: UpdateTaskDescriptionInput!): Task!
|
||||||
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
|
updateTaskLocation(input: NewTaskLocation!): UpdateTaskLocationPayload!
|
||||||
updateTaskName(input: UpdateTaskName!): Task!
|
updateTaskName(input: UpdateTaskName!): Task!
|
||||||
|
updateTaskDueDate(input: UpdateTaskDueDate!): Task!
|
||||||
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
||||||
assignTask(input: AssignTaskInput): Task!
|
assignTask(input: AssignTaskInput): Task!
|
||||||
unassignTask(input: UnassignTaskInput): Task!
|
unassignTask(input: UnassignTaskInput): Task!
|
||||||
|
@ -260,6 +260,21 @@ func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskN
|
|||||||
return &task, err
|
return &task, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTaskDueDate) (*pg.Task, error) {
|
||||||
|
var dueDate sql.NullTime
|
||||||
|
if input.DueDate == nil {
|
||||||
|
dueDate = sql.NullTime{Valid: false, Time: time.Now()}
|
||||||
|
} else {
|
||||||
|
dueDate = sql.NullTime{Valid: true, Time: *input.DueDate}
|
||||||
|
}
|
||||||
|
task, err := r.Repository.UpdateTaskDueDate(ctx, pg.UpdateTaskDueDateParams{
|
||||||
|
TaskID: input.TaskID,
|
||||||
|
DueDate: dueDate,
|
||||||
|
})
|
||||||
|
|
||||||
|
return &task, err
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) {
|
func (r *mutationResolver) DeleteTask(ctx context.Context, input DeleteTaskInput) (*DeleteTaskPayload, error) {
|
||||||
taskID, err := uuid.Parse(input.TaskID)
|
taskID, err := uuid.Parse(input.TaskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -484,6 +499,13 @@ func (r *taskResolver) Description(ctx context.Context, obj *pg.Task) (*string,
|
|||||||
return &task.Description.String, nil
|
return &task.Description.String, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *taskResolver) DueDate(ctx context.Context, obj *pg.Task) (*time.Time, error) {
|
||||||
|
if obj.DueDate.Valid {
|
||||||
|
return &obj.DueDate.Time, nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *taskResolver) Assigned(ctx context.Context, obj *pg.Task) ([]ProjectMember, error) {
|
func (r *taskResolver) Assigned(ctx context.Context, obj *pg.Task) ([]ProjectMember, error) {
|
||||||
taskMemberLinks, err := r.Repository.GetAssignedMembersForTask(ctx, obj.TaskID)
|
taskMemberLinks, err := r.Repository.GetAssignedMembersForTask(ctx, obj.TaskID)
|
||||||
taskMembers := []ProjectMember{}
|
taskMembers := []ProjectMember{}
|
||||||
|
@ -30,6 +30,7 @@ type Repository interface {
|
|||||||
UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNameByIDParams) (Project, error)
|
UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNameByIDParams) (Project, error)
|
||||||
|
|
||||||
DeleteTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) error
|
DeleteTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) error
|
||||||
|
UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error)
|
||||||
CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error)
|
CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error)
|
||||||
GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error)
|
GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error)
|
||||||
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)
|
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)
|
||||||
|
@ -56,11 +56,13 @@ type Querier interface {
|
|||||||
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
||||||
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
||||||
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
||||||
|
SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error)
|
||||||
UpdateProjectLabel(ctx context.Context, arg UpdateProjectLabelParams) (ProjectLabel, error)
|
UpdateProjectLabel(ctx context.Context, arg UpdateProjectLabelParams) (ProjectLabel, error)
|
||||||
UpdateProjectLabelColor(ctx context.Context, arg UpdateProjectLabelColorParams) (ProjectLabel, error)
|
UpdateProjectLabelColor(ctx context.Context, arg UpdateProjectLabelColorParams) (ProjectLabel, error)
|
||||||
UpdateProjectLabelName(ctx context.Context, arg UpdateProjectLabelNameParams) (ProjectLabel, error)
|
UpdateProjectLabelName(ctx context.Context, arg UpdateProjectLabelNameParams) (ProjectLabel, error)
|
||||||
UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNameByIDParams) (Project, error)
|
UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNameByIDParams) (Project, error)
|
||||||
UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error)
|
UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error)
|
||||||
|
UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error)
|
||||||
UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error)
|
UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error)
|
||||||
UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocationParams) (Task, error)
|
UpdateTaskLocation(ctx context.Context, arg UpdateTaskLocationParams) (Task, error)
|
||||||
UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams) (Task, error)
|
UpdateTaskName(ctx context.Context, arg UpdateTaskNameParams) (Task, error)
|
||||||
|
@ -177,6 +177,30 @@ func (q *Queries) UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescr
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updateTaskDueDate = `-- name: UpdateTaskDueDate :one
|
||||||
|
UPDATE task SET due_date = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateTaskDueDateParams struct {
|
||||||
|
TaskID uuid.UUID `json:"task_id"`
|
||||||
|
DueDate sql.NullTime `json:"due_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, updateTaskDueDate, arg.TaskID, arg.DueDate)
|
||||||
|
var i Task
|
||||||
|
err := row.Scan(
|
||||||
|
&i.TaskID,
|
||||||
|
&i.TaskGroupID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.Name,
|
||||||
|
&i.Position,
|
||||||
|
&i.Description,
|
||||||
|
&i.DueDate,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const updateTaskLocation = `-- name: UpdateTaskLocation :one
|
const updateTaskLocation = `-- name: UpdateTaskLocation :one
|
||||||
UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date
|
UPDATE task SET task_group_id = $2, position = $3 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date
|
||||||
`
|
`
|
||||||
|
@ -135,6 +135,28 @@ func (q *Queries) GetTaskGroupsForProject(ctx context.Context, projectID uuid.UU
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setTaskGroupName = `-- name: SetTaskGroupName :one
|
||||||
|
UPDATE task_group SET name = $2 WHERE task_group_id = $1 RETURNING task_group_id, project_id, created_at, name, position
|
||||||
|
`
|
||||||
|
|
||||||
|
type SetTaskGroupNameParams struct {
|
||||||
|
TaskGroupID uuid.UUID `json:"task_group_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, setTaskGroupName, arg.TaskGroupID, arg.Name)
|
||||||
|
var i TaskGroup
|
||||||
|
err := row.Scan(
|
||||||
|
&i.TaskGroupID,
|
||||||
|
&i.ProjectID,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.Name,
|
||||||
|
&i.Position,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const updateTaskGroupLocation = `-- name: UpdateTaskGroupLocation :one
|
const updateTaskGroupLocation = `-- name: UpdateTaskGroupLocation :one
|
||||||
UPDATE task_group SET position = $2 WHERE task_group_id = $1 RETURNING task_group_id, project_id, created_at, name, position
|
UPDATE task_group SET position = $2 WHERE task_group_id = $1 RETURNING task_group_id, project_id, created_at, name, position
|
||||||
`
|
`
|
||||||
|
@ -25,3 +25,6 @@ UPDATE task SET name = $2 WHERE task_id = $1 RETURNING *;
|
|||||||
|
|
||||||
-- name: DeleteTasksByTaskGroupID :execrows
|
-- name: DeleteTasksByTaskGroupID :execrows
|
||||||
DELETE FROM task where task_group_id = $1;
|
DELETE FROM task where task_group_id = $1;
|
||||||
|
|
||||||
|
-- name: UpdateTaskDueDate :one
|
||||||
|
UPDATE task SET due_date = $2 WHERE task_id = $1 RETURNING *;
|
||||||
|
@ -111,5 +111,19 @@ export default createGlobalStyle`
|
|||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: #262c49;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: #7367f0;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
${mixin.placeholderColor(color.textLight)}
|
${mixin.placeholderColor(color.textLight)}
|
||||||
`;
|
`;
|
||||||
|
@ -17,6 +17,7 @@ const theme: DefaultTheme = {
|
|||||||
primary: '194, 198, 220',
|
primary: '194, 198, 220',
|
||||||
secondary: '255, 255, 255',
|
secondary: '255, 255, 255',
|
||||||
},
|
},
|
||||||
|
border: '65, 69, 97',
|
||||||
bg: {
|
bg: {
|
||||||
primary: '16, 22, 58',
|
primary: '16, 22, 58',
|
||||||
secondary: '38, 44, 73',
|
secondary: '38, 44, 73',
|
||||||
|
@ -4,9 +4,15 @@ import TaskDetails from 'shared/components/TaskDetails';
|
|||||||
import PopupMenu, { Popup, usePopup } from 'shared/components/PopupMenu';
|
import PopupMenu, { Popup, usePopup } from 'shared/components/PopupMenu';
|
||||||
import MemberManager from 'shared/components/MemberManager';
|
import MemberManager from 'shared/components/MemberManager';
|
||||||
import { useRouteMatch, useHistory } from 'react-router';
|
import { useRouteMatch, useHistory } from 'react-router';
|
||||||
import { useFindTaskQuery, useAssignTaskMutation, useUnassignTaskMutation } from 'shared/generated/graphql';
|
import {
|
||||||
|
useFindTaskQuery,
|
||||||
|
useUpdateTaskDueDateMutation,
|
||||||
|
useAssignTaskMutation,
|
||||||
|
useUnassignTaskMutation,
|
||||||
|
} from 'shared/generated/graphql';
|
||||||
import UserIDContext from 'App/context';
|
import UserIDContext from 'App/context';
|
||||||
import MiniProfile from 'shared/components/MiniProfile';
|
import MiniProfile from 'shared/components/MiniProfile';
|
||||||
|
import DueDateManager from 'shared/components/DueDateManager';
|
||||||
|
|
||||||
type DetailsProps = {
|
type DetailsProps = {
|
||||||
taskID: string;
|
taskID: string;
|
||||||
@ -32,12 +38,18 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
refreshCache,
|
refreshCache,
|
||||||
}) => {
|
}) => {
|
||||||
const { userID } = useContext(UserIDContext);
|
const { userID } = useContext(UserIDContext);
|
||||||
const { showPopup } = usePopup();
|
const { showPopup, hidePopup } = usePopup();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
const [currentMemberTask, setCurrentMemberTask] = useState('');
|
const [currentMemberTask, setCurrentMemberTask] = useState('');
|
||||||
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
||||||
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
|
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
|
||||||
|
const [updateTaskDueDate] = useUpdateTaskDueDateMutation({
|
||||||
|
onCompleted: () => {
|
||||||
|
refetch();
|
||||||
|
refreshCache();
|
||||||
|
},
|
||||||
|
});
|
||||||
const [assignTask] = useAssignTaskMutation({
|
const [assignTask] = useAssignTaskMutation({
|
||||||
onCompleted: () => {
|
onCompleted: () => {
|
||||||
refetch();
|
refetch();
|
||||||
@ -56,6 +68,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return <div>loading</div>;
|
return <div>loading</div>;
|
||||||
}
|
}
|
||||||
|
console.log(data.findTask);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
@ -108,6 +121,29 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
onOpenAddLabelPopup={onOpenAddLabelPopup}
|
||||||
|
onOpenDueDatePopop={(task, $targetRef) => {
|
||||||
|
showPopup(
|
||||||
|
$targetRef,
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
title={'Change Due Date'}
|
||||||
|
tab={0}
|
||||||
|
onClose={() => {
|
||||||
|
hidePopup();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DueDateManager
|
||||||
|
task={task}
|
||||||
|
onDueDateChange={(t, newDueDate) => {
|
||||||
|
console.log(`${newDueDate}`);
|
||||||
|
updateTaskDueDate({ variables: { taskID: t.id, dueDate: newDueDate } });
|
||||||
|
hidePopup();
|
||||||
|
}}
|
||||||
|
onCancel={() => {}}
|
||||||
|
/>
|
||||||
|
</Popup>,
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -229,14 +229,14 @@ const ProjectAction = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: var(--color-text);
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--color-text-hover);
|
color: rgba(${props => props.theme.colors.text.secondary});
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -490,15 +490,15 @@ const Project = () => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tags size={13} color="var(--color-icon)" />
|
<Tags width={13} height={13} />
|
||||||
<ProjectActionText>Labels</ProjectActionText>
|
<ProjectActionText>Labels</ProjectActionText>
|
||||||
</ProjectAction>
|
</ProjectAction>
|
||||||
<ProjectAction>
|
<ProjectAction>
|
||||||
<ToggleOn size={13} color="var(--color-icon)" />
|
<ToggleOn width={13} height={13} />
|
||||||
<ProjectActionText>Fields</ProjectActionText>
|
<ProjectActionText>Fields</ProjectActionText>
|
||||||
</ProjectAction>
|
</ProjectAction>
|
||||||
<ProjectAction>
|
<ProjectAction>
|
||||||
<Bolt size={13} color="var(--color-icon)" />
|
<Bolt width={13} height={13} />
|
||||||
<ProjectActionText>Rules</ProjectActionText>
|
<ProjectActionText>Rules</ProjectActionText>
|
||||||
</ProjectAction>
|
</ProjectAction>
|
||||||
</ProjectActions>
|
</ProjectActions>
|
||||||
|
4
web/src/citadel.d.ts
vendored
4
web/src/citadel.d.ts
vendored
@ -32,8 +32,8 @@ type LoginFormData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type DueDateFormData = {
|
type DueDateFormData = {
|
||||||
endDate: Date;
|
endDate: string;
|
||||||
endTime: string | null;
|
endTime: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type LoginProps = {
|
type LoginProps = {
|
||||||
|
1
web/src/projects.d.ts
vendored
1
web/src/projects.d.ts
vendored
@ -35,6 +35,7 @@ type Task = {
|
|||||||
taskGroup: InnerTaskGroup;
|
taskGroup: InnerTaskGroup;
|
||||||
name: string;
|
name: string;
|
||||||
position: number;
|
position: number;
|
||||||
|
dueDate?: string;
|
||||||
labels: TaskLabel[];
|
labels: TaskLabel[];
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
assigned?: Array<TaskUser>;
|
assigned?: Array<TaskUser>;
|
||||||
|
@ -107,6 +107,7 @@ type ButtonProps = {
|
|||||||
variant?: 'filled' | 'outline' | 'flat' | 'lineDown' | 'gradient' | 'relief';
|
variant?: 'filled' | 'outline' | 'flat' | 'lineDown' | 'gradient' | 'relief';
|
||||||
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
|
color?: 'primary' | 'danger' | 'success' | 'warning' | 'dark';
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
type?: 'button' | 'submit';
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
};
|
};
|
||||||
@ -116,6 +117,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
fontSize = '14px',
|
fontSize = '14px',
|
||||||
color = 'primary',
|
color = 'primary',
|
||||||
variant = 'filled',
|
variant = 'filled',
|
||||||
|
type = 'button',
|
||||||
onClick,
|
onClick,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
@ -128,38 +130,38 @@ const Button: React.FC<ButtonProps> = ({
|
|||||||
switch (variant) {
|
switch (variant) {
|
||||||
case 'filled':
|
case 'filled':
|
||||||
return (
|
return (
|
||||||
<Filled onClick={handleClick} className={className} disabled={disabled} color={color}>
|
<Filled type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||||
<Text fontSize={fontSize}>{children}</Text>
|
<Text fontSize={fontSize}>{children}</Text>
|
||||||
</Filled>
|
</Filled>
|
||||||
);
|
);
|
||||||
case 'outline':
|
case 'outline':
|
||||||
return (
|
return (
|
||||||
<Outline onClick={handleClick} className={className} disabled={disabled} color={color}>
|
<Outline type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||||
<Text fontSize={fontSize}>{children}</Text>
|
<Text fontSize={fontSize}>{children}</Text>
|
||||||
</Outline>
|
</Outline>
|
||||||
);
|
);
|
||||||
case 'flat':
|
case 'flat':
|
||||||
return (
|
return (
|
||||||
<Flat onClick={handleClick} className={className} disabled={disabled} color={color}>
|
<Flat type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||||
<Text fontSize={fontSize}>{children}</Text>
|
<Text fontSize={fontSize}>{children}</Text>
|
||||||
</Flat>
|
</Flat>
|
||||||
);
|
);
|
||||||
case 'lineDown':
|
case 'lineDown':
|
||||||
return (
|
return (
|
||||||
<LineDown onClick={handleClick} className={className} disabled={disabled} color={color}>
|
<LineDown type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||||
<Text fontSize={fontSize}>{children}</Text>
|
<Text fontSize={fontSize}>{children}</Text>
|
||||||
<LineX color={color} />
|
<LineX color={color} />
|
||||||
</LineDown>
|
</LineDown>
|
||||||
);
|
);
|
||||||
case 'gradient':
|
case 'gradient':
|
||||||
return (
|
return (
|
||||||
<Gradient onClick={handleClick} className={className} disabled={disabled} color={color}>
|
<Gradient type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||||
<Text fontSize={fontSize}>{children}</Text>
|
<Text fontSize={fontSize}>{children}</Text>
|
||||||
</Gradient>
|
</Gradient>
|
||||||
);
|
);
|
||||||
case 'relief':
|
case 'relief':
|
||||||
return (
|
return (
|
||||||
<Relief onClick={handleClick} className={className} disabled={disabled} color={color}>
|
<Relief type={type} onClick={handleClick} className={className} disabled={disabled} color={color}>
|
||||||
<Text fontSize={fontSize}>{children}</Text>
|
<Text fontSize={fontSize}>{children}</Text>
|
||||||
</Relief>
|
</Relief>
|
||||||
);
|
);
|
||||||
|
@ -22,7 +22,7 @@ export const EditorTextarea = styled(TextareaAutosize)`
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: var(--color-input-text-focus);
|
color: rgba(${props => props.theme.colors.text.secondary});
|
||||||
&:focus {
|
&:focus {
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -84,7 +84,7 @@ export const ListCardContainer = styled.div<{ isActive: boolean; editable: boole
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
background-color: ${props =>
|
background-color: ${props =>
|
||||||
props.isActive && !props.editable ? mixin.darken('#262c49', 0.1) : 'var(--color-background)'};
|
props.isActive && !props.editable ? mixin.darken('#262c49', 0.1) : `rgba(${props.theme.colors.bg.secondary})`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ListCardInnerContainer = styled.div`
|
export const ListCardInnerContainer = styled.div`
|
||||||
@ -145,7 +145,7 @@ export const CardTitle = styled.span`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
color: var(--color-text);
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CardMembers = styled.div`
|
export const CardMembers = styled.div`
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import DueDateManager from '.';
|
import BaseStyles from 'App/BaseStyles';
|
||||||
|
import NormalizeStyles from 'App/NormalizeStyles';
|
||||||
|
import { theme } from 'App/ThemeStyles';
|
||||||
|
import styled, { ThemeProvider } from 'styled-components';
|
||||||
import { Popup } from '../PopupMenu';
|
import { Popup } from '../PopupMenu';
|
||||||
import styled from 'styled-components';
|
import DueDateManager from '.';
|
||||||
|
|
||||||
const PopupWrapper = styled.div`
|
const PopupWrapper = styled.div`
|
||||||
width: 300px;
|
width: 310px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: DueDateManager,
|
component: DueDateManager,
|
||||||
title: 'DueDateManager',
|
title: 'DueDateManager',
|
||||||
@ -20,6 +24,10 @@ export default {
|
|||||||
|
|
||||||
export const Default = () => {
|
export const Default = () => {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<NormalizeStyles />
|
||||||
|
<BaseStyles />
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
<PopupWrapper>
|
<PopupWrapper>
|
||||||
<Popup title={null} tab={0}>
|
<Popup title={null} tab={0}>
|
||||||
<DueDateManager
|
<DueDateManager
|
||||||
@ -59,5 +67,7 @@ export const Default = () => {
|
|||||||
/>
|
/>
|
||||||
</Popup>
|
</Popup>
|
||||||
</PopupWrapper>
|
</PopupWrapper>
|
||||||
|
</ThemeProvider>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import Button from 'shared/components/Button';
|
||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
import Input from 'shared/components/Input';
|
||||||
|
|
||||||
export const Wrapper = styled.div`
|
export const Wrapper = styled.div`
|
||||||
display: flex
|
display: flex
|
||||||
@ -8,7 +10,37 @@ display: flex
|
|||||||
background: #262c49;
|
background: #262c49;
|
||||||
font-family: 'Droid Sans', sans-serif;
|
font-family: 'Droid Sans', sans-serif;
|
||||||
border: none;
|
border: none;
|
||||||
|
}
|
||||||
|
& .react-datepicker__triangle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
& .react-datepicker-popper {
|
||||||
|
z-index: 10000;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .react-datepicker-time__header {
|
||||||
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
|
}
|
||||||
|
& .react-datepicker__time-list-item {
|
||||||
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
|
}
|
||||||
|
& .react-datepicker__time-container .react-datepicker__time
|
||||||
|
.react-datepicker__time-box ul.react-datepicker__time-list
|
||||||
|
li.react-datepicker__time-list-item:hover {
|
||||||
|
color: rgba(${props => props.theme.colors.text.secondary});
|
||||||
|
background: rgba(${props => props.theme.colors.bg.secondary});
|
||||||
|
}
|
||||||
|
& .react-datepicker__time-container .react-datepicker__time {
|
||||||
|
background: rgba(${props => props.theme.colors.bg.primary});
|
||||||
|
}
|
||||||
|
& .react-datepicker--time-only {
|
||||||
|
background: rgba(${props => props.theme.colors.bg.primary});
|
||||||
|
border: 1px solid rgba(${props => props.theme.colors.border});
|
||||||
|
}
|
||||||
|
|
||||||
|
& .react-datepicker * {
|
||||||
|
box-sizing: content-box;
|
||||||
}
|
}
|
||||||
& .react-datepicker__day-name {
|
& .react-datepicker__day-name {
|
||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
@ -56,6 +88,9 @@ display: flex
|
|||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
& .react-datepicker__header--time {
|
||||||
|
border-bottom: 1px solid rgba(${props => props.theme.colors.border});
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -66,21 +101,10 @@ export const DueDatePickerWrapper = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ConfirmAddDueDate = styled.div`
|
export const ConfirmAddDueDate = styled(Button)`
|
||||||
background-color: #5aac44;
|
|
||||||
box-shadow: none;
|
|
||||||
border: none;
|
|
||||||
color: #fff;
|
|
||||||
float: left;
|
float: left;
|
||||||
margin: 0 4px 0 0;
|
margin: 0 4px 0 0;
|
||||||
cursor: pointer;
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 20px;
|
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
text-align: center;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 14px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CancelDueDate = styled.div`
|
export const CancelDueDate = styled.div`
|
||||||
@ -92,6 +116,12 @@ export const CancelDueDate = styled.div`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DueDateInput = styled(Input)`
|
||||||
|
margin-top: 15px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
padding-right: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
export const ActionWrapper = styled.div`
|
export const ActionWrapper = styled.div`
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, forwardRef } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import DatePicker from 'react-datepicker';
|
import DatePicker from 'react-datepicker';
|
||||||
import { Cross } from 'shared/icons';
|
import { Cross } from 'shared/icons';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { Wrapper, ActionWrapper, DueDatePickerWrapper, ConfirmAddDueDate, CancelDueDate } from './Styles';
|
import { Wrapper, ActionWrapper, DueDateInput, DueDatePickerWrapper, ConfirmAddDueDate, CancelDueDate } from './Styles';
|
||||||
|
|
||||||
import 'react-datepicker/dist/react-datepicker.css';
|
import 'react-datepicker/dist/react-datepicker.css';
|
||||||
import { getYear, getMonth } from 'date-fns';
|
import { getYear, getMonth } from 'date-fns';
|
||||||
@ -17,6 +17,14 @@ type DueDateManagerProps = {
|
|||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Form = styled.form`
|
||||||
|
padding-top: 25px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const FormField = styled.div`
|
||||||
|
width: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
`;
|
||||||
const HeaderSelectLabel = styled.div`
|
const HeaderSelectLabel = styled.div`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -104,12 +112,17 @@ const HeaderActions = styled.div`
|
|||||||
const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange, onCancel }) => {
|
const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange, onCancel }) => {
|
||||||
const now = moment();
|
const now = moment();
|
||||||
const [textStartDate, setTextStartDate] = useState(now.format('YYYY-MM-DD'));
|
const [textStartDate, setTextStartDate] = useState(now.format('YYYY-MM-DD'));
|
||||||
|
|
||||||
const [startDate, setStartDate] = useState(new Date());
|
const [startDate, setStartDate] = useState(new Date());
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTextStartDate(moment(startDate).format('YYYY-MM-DD'));
|
setTextStartDate(moment(startDate).format('YYYY-MM-DD'));
|
||||||
}, [startDate]);
|
}, [startDate]);
|
||||||
|
|
||||||
|
const [textEndTime, setTextEndTime] = useState(now.format('h:mm A'));
|
||||||
|
const [endTime, setEndTime] = useState(now.toDate());
|
||||||
|
useEffect(() => {
|
||||||
|
setTextEndTime(moment(endTime).format('h:mm A'));
|
||||||
|
}, [endTime]);
|
||||||
|
|
||||||
const years = _.range(2010, getYear(new Date()) + 10, 1);
|
const years = _.range(2010, getYear(new Date()) + 10, 1);
|
||||||
const months = [
|
const months = [
|
||||||
'January',
|
'January',
|
||||||
@ -125,29 +138,75 @@ const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange,
|
|||||||
'November',
|
'November',
|
||||||
'December',
|
'December',
|
||||||
];
|
];
|
||||||
const { register, handleSubmit, errors, setError, formState } = useForm<DueDateFormData>();
|
const { register, handleSubmit, errors, setValue, setError, formState } = useForm<DueDateFormData>();
|
||||||
|
const saveDueDate = (data: any) => {
|
||||||
|
console.log(data);
|
||||||
|
const newDate = moment(`${data.endDate} ${data.endTime}`, 'YYYY-MM-DD h:mm A');
|
||||||
|
if (newDate.isValid()) {
|
||||||
|
onDueDateChange(task, newDate.toDate());
|
||||||
|
}
|
||||||
|
};
|
||||||
console.log(errors);
|
console.log(errors);
|
||||||
|
register({ name: 'endTime' }, { required: 'End time is required' });
|
||||||
|
useEffect(() => {
|
||||||
|
setValue('endTime', now.format('h:mm A'));
|
||||||
|
}, []);
|
||||||
|
const CustomTimeInput = forwardRef(({ value, onClick }: any, $ref: any) => {
|
||||||
|
return (
|
||||||
|
<DueDateInput
|
||||||
|
id="endTime"
|
||||||
|
name="endTime"
|
||||||
|
ref={$ref}
|
||||||
|
onChange={e => {
|
||||||
|
console.log(`onCahnge ${e.currentTarget.value}`);
|
||||||
|
setTextEndTime(e.currentTarget.value);
|
||||||
|
setValue('endTime', e.currentTarget.value);
|
||||||
|
}}
|
||||||
|
width="100%"
|
||||||
|
variant="alternate"
|
||||||
|
label="Date"
|
||||||
|
onClick={onClick}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
console.log(`textStartDate ${textStartDate}`);
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<form>
|
<Form onSubmit={handleSubmit(saveDueDate)}>
|
||||||
<input
|
<FormField>
|
||||||
type="text"
|
<DueDateInput
|
||||||
id="endDate"
|
id="endDate"
|
||||||
name="endDate"
|
name="endDate"
|
||||||
|
width="100%"
|
||||||
|
variant="alternate"
|
||||||
|
label="Date"
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
setTextStartDate(e.currentTarget.value);
|
setTextStartDate(e.currentTarget.value);
|
||||||
}}
|
}}
|
||||||
value={textStartDate}
|
value={textStartDate}
|
||||||
ref={register({
|
ref={register({
|
||||||
required: 'End due date is required.',
|
required: 'End date is required.',
|
||||||
validate: value => {
|
|
||||||
const isValid = moment(value, 'YYYY-MM-DD').isValid();
|
|
||||||
console.log(`${value} - ${isValid}`);
|
|
||||||
return isValid;
|
|
||||||
},
|
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</form>
|
</FormField>
|
||||||
|
<FormField>
|
||||||
|
<DatePicker
|
||||||
|
selected={endTime}
|
||||||
|
onChange={date => {
|
||||||
|
const changedDate = moment(date ?? new Date());
|
||||||
|
console.log(`changed ${date}`);
|
||||||
|
setEndTime(changedDate.toDate());
|
||||||
|
setValue('endTime', changedDate.format('h:mm A'));
|
||||||
|
}}
|
||||||
|
showTimeSelect
|
||||||
|
showTimeSelectOnly
|
||||||
|
timeIntervals={15}
|
||||||
|
timeCaption="Time"
|
||||||
|
dateFormat="h:mm aa"
|
||||||
|
customInput={<CustomTimeInput />}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
<DueDatePickerWrapper>
|
<DueDatePickerWrapper>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
useWeekdaysShort
|
useWeekdaysShort
|
||||||
@ -195,15 +254,27 @@ const DueDateManager: React.FC<DueDateManagerProps> = ({ task, onDueDateChange,
|
|||||||
)}
|
)}
|
||||||
selected={startDate}
|
selected={startDate}
|
||||||
inline
|
inline
|
||||||
onChange={date => setStartDate(date ?? new Date())}
|
onChange={date => {
|
||||||
|
setStartDate(date ?? new Date());
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</DueDatePickerWrapper>
|
</DueDatePickerWrapper>
|
||||||
<ActionWrapper>
|
<ActionWrapper>
|
||||||
<ConfirmAddDueDate onClick={() => onDueDateChange(task, startDate)}>Save</ConfirmAddDueDate>
|
<ConfirmAddDueDate
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
// const newDate = moment(startDate).format('YYYY-MM-DD');
|
||||||
|
// const newTime = moment(endTime).format('h:mm A');
|
||||||
|
// onDueDateChange(task, moment(`${newDate} ${newTime}`, 'YYYY-MM-DD h:mm A').toDate());
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</ConfirmAddDueDate>
|
||||||
<CancelDueDate onClick={onCancel}>
|
<CancelDueDate onClick={onCancel}>
|
||||||
<Cross size={16} color="#c2c6dc" />
|
<Cross size={16} color="#c2c6dc" />
|
||||||
</CancelDueDate>
|
</CancelDueDate>
|
||||||
</ActionWrapper>
|
</ActionWrapper>
|
||||||
|
</Form>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import styled from 'styled-components/macro';
|
import styled, { css } from 'styled-components/macro';
|
||||||
|
|
||||||
const InputWrapper = styled.div<{ width: string }>`
|
const InputWrapper = styled.div<{ width: string }>`
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -32,7 +32,13 @@ const InputLabel = styled.span<{ width: string }>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InputInput = styled.input<{ hasIcon: boolean; width: string; focusBg: string; borderColor: string }>`
|
const InputInput = styled.input<{
|
||||||
|
hasValue: boolean;
|
||||||
|
hasIcon: boolean;
|
||||||
|
width: string;
|
||||||
|
focusBg: string;
|
||||||
|
borderColor: string;
|
||||||
|
}>`
|
||||||
width: ${props => props.width};
|
width: ${props => props.width};
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
@ -54,6 +60,14 @@ const InputInput = styled.input<{ hasIcon: boolean; width: string; focusBg: stri
|
|||||||
color: rgba(115, 103, 240);
|
color: rgba(115, 103, 240);
|
||||||
transform: translate(-3px, -90%);
|
transform: translate(-3px, -90%);
|
||||||
}
|
}
|
||||||
|
${props =>
|
||||||
|
props.hasValue &&
|
||||||
|
css`
|
||||||
|
& ~ ${InputLabel} {
|
||||||
|
color: rgba(115, 103, 240);
|
||||||
|
transform: translate(-3px, -90%);
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Icon = styled.div`
|
const Icon = styled.div`
|
||||||
@ -68,14 +82,55 @@ type InputProps = {
|
|||||||
width?: string;
|
width?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
className?: string;
|
||||||
|
value?: string;
|
||||||
|
onClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
|
||||||
|
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Input: React.FC<InputProps> = ({ width = 'auto', variant = 'normal', label, placeholder, icon }) => {
|
const Input = React.forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
width = 'auto',
|
||||||
|
variant = 'normal',
|
||||||
|
label,
|
||||||
|
placeholder,
|
||||||
|
icon,
|
||||||
|
name,
|
||||||
|
onChange,
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
value: initialValue,
|
||||||
|
id,
|
||||||
|
}: InputProps,
|
||||||
|
$ref: any,
|
||||||
|
) => {
|
||||||
|
const [value, setValue] = useState(initialValue ?? '');
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialValue) {
|
||||||
|
setValue(initialValue);
|
||||||
|
}
|
||||||
|
}, [initialValue]);
|
||||||
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
|
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
|
||||||
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
|
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setValue(e.currentTarget.value);
|
||||||
|
if (onChange) {
|
||||||
|
onChange(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<InputWrapper width={width}>
|
<InputWrapper className={className} width={width}>
|
||||||
<InputInput
|
<InputInput
|
||||||
|
hasValue={value !== ''}
|
||||||
|
ref={$ref}
|
||||||
|
id={id}
|
||||||
|
name={name}
|
||||||
|
onClick={onClick}
|
||||||
|
onChange={handleChange}
|
||||||
|
value={value}
|
||||||
hasIcon={typeof icon !== 'undefined'}
|
hasIcon={typeof icon !== 'undefined'}
|
||||||
width={width}
|
width={width}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
@ -86,6 +141,7 @@ const Input: React.FC<InputProps> = ({ width = 'auto', variant = 'normal', label
|
|||||||
<Icon>{icon && icon}</Icon>
|
<Icon>{icon && icon}</Icon>
|
||||||
</InputWrapper>
|
</InputWrapper>
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default Input;
|
export default Input;
|
||||||
|
@ -12,6 +12,7 @@ import {
|
|||||||
} from 'shared/utils/draggables';
|
} from 'shared/utils/draggables';
|
||||||
|
|
||||||
import { Container, BoardWrapper } from './Styles';
|
import { Container, BoardWrapper } from './Styles';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
interface SimpleProps {
|
interface SimpleProps {
|
||||||
taskGroups: Array<TaskGroup>;
|
taskGroups: Array<TaskGroup>;
|
||||||
@ -165,6 +166,14 @@ const SimpleLists: React.FC<SimpleProps> = ({
|
|||||||
taskGroupID={taskGroup.id}
|
taskGroupID={taskGroup.id}
|
||||||
description=""
|
description=""
|
||||||
labels={task.labels.map(label => label.projectLabel)}
|
labels={task.labels.map(label => label.projectLabel)}
|
||||||
|
dueDate={
|
||||||
|
task.dueDate
|
||||||
|
? {
|
||||||
|
isPastDue: false,
|
||||||
|
formattedDate: moment(task.dueDate).format('MMM D, YYYY'),
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
title={task.name}
|
title={task.name}
|
||||||
members={task.assigned}
|
members={task.assigned}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -66,6 +66,7 @@ export const Default = () => {
|
|||||||
onMemberProfile={action('profile')}
|
onMemberProfile={action('profile')}
|
||||||
onOpenAddMemberPopup={action('open add member popup')}
|
onOpenAddMemberPopup={action('open add member popup')}
|
||||||
onOpenAddLabelPopup={action('open add label popup')}
|
onOpenAddLabelPopup={action('open add label popup')}
|
||||||
|
onOpenDueDatePopop={action('open due date popup')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -39,6 +39,7 @@ import {
|
|||||||
} from './Styles';
|
} from './Styles';
|
||||||
|
|
||||||
import convertDivElementRefToBounds from 'shared/utils/boundingRect';
|
import convertDivElementRefToBounds from 'shared/utils/boundingRect';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
type TaskContentProps = {
|
type TaskContentProps = {
|
||||||
onEditContent: () => void;
|
onEditContent: () => void;
|
||||||
@ -106,6 +107,7 @@ type TaskDetailsProps = {
|
|||||||
onDeleteTask: (task: Task) => void;
|
onDeleteTask: (task: Task) => void;
|
||||||
onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||||
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||||
|
onOpenDueDatePopop: (task: Task, $targetRef: React.RefObject<HTMLElement>) => void;
|
||||||
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
onMemberProfile: ($targetRef: React.RefObject<HTMLElement>, memberID: string) => void;
|
||||||
onCloseModal: () => void;
|
onCloseModal: () => void;
|
||||||
};
|
};
|
||||||
@ -118,6 +120,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
onCloseModal,
|
onCloseModal,
|
||||||
onOpenAddMemberPopup,
|
onOpenAddMemberPopup,
|
||||||
onOpenAddLabelPopup,
|
onOpenAddLabelPopup,
|
||||||
|
onOpenDueDatePopop,
|
||||||
onMemberProfile,
|
onMemberProfile,
|
||||||
}) => {
|
}) => {
|
||||||
const [editorOpen, setEditorOpen] = useState(false);
|
const [editorOpen, setEditorOpen] = useState(false);
|
||||||
@ -143,6 +146,7 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
const onAddMember = () => {
|
const onAddMember = () => {
|
||||||
onOpenAddMemberPopup(task, $addMemberRef);
|
onOpenAddMemberPopup(task, $addMemberRef);
|
||||||
};
|
};
|
||||||
|
const $dueDateLabel = useRef<HTMLDivElement>(null);
|
||||||
const $addLabelRef = useRef<HTMLDivElement>(null);
|
const $addLabelRef = useRef<HTMLDivElement>(null);
|
||||||
const onAddLabel = () => {
|
const onAddLabel = () => {
|
||||||
onOpenAddLabelPopup(task, $addLabelRef);
|
onOpenAddLabelPopup(task, $addLabelRef);
|
||||||
@ -229,7 +233,15 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
</TaskDetailsAddLabel>
|
</TaskDetailsAddLabel>
|
||||||
</TaskDetailLabels>
|
</TaskDetailLabels>
|
||||||
<TaskDetailSectionTitle>Due Date</TaskDetailSectionTitle>
|
<TaskDetailSectionTitle>Due Date</TaskDetailSectionTitle>
|
||||||
<NoDueDateLabel>No due date</NoDueDateLabel>
|
{task.dueDate ? (
|
||||||
|
<NoDueDateLabel ref={$dueDateLabel} onClick={() => onOpenDueDatePopop(task, $dueDateLabel)}>
|
||||||
|
{moment(task.dueDate).format('MMM D [at] h:mm A')}
|
||||||
|
</NoDueDateLabel>
|
||||||
|
) : (
|
||||||
|
<NoDueDateLabel ref={$dueDateLabel} onClick={() => onOpenDueDatePopop(task, $dueDateLabel)}>
|
||||||
|
No due date
|
||||||
|
</NoDueDateLabel>
|
||||||
|
)}
|
||||||
</TaskDetailsSidebar>
|
</TaskDetailsSidebar>
|
||||||
</TaskDetailsWrapper>
|
</TaskDetailsWrapper>
|
||||||
</>
|
</>
|
||||||
|
@ -110,6 +110,7 @@ export type Task = {
|
|||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
position: Scalars['Float'];
|
position: Scalars['Float'];
|
||||||
description?: Maybe<Scalars['String']>;
|
description?: Maybe<Scalars['String']>;
|
||||||
|
dueDate?: Maybe<Scalars['Time']>;
|
||||||
assigned: Array<ProjectMember>;
|
assigned: Array<ProjectMember>;
|
||||||
labels: Array<TaskLabel>;
|
labels: Array<TaskLabel>;
|
||||||
};
|
};
|
||||||
@ -310,6 +311,16 @@ export type UpdateTaskLocationPayload = {
|
|||||||
task: Task;
|
task: Task;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UpdateTaskGroupName = {
|
||||||
|
taskGroupID: Scalars['UUID'];
|
||||||
|
name: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateTaskDueDate = {
|
||||||
|
taskID: Scalars['UUID'];
|
||||||
|
dueDate?: Maybe<Scalars['Time']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
createRefreshToken: RefreshToken;
|
createRefreshToken: RefreshToken;
|
||||||
@ -325,6 +336,7 @@ export type Mutation = {
|
|||||||
updateProjectLabelColor: ProjectLabel;
|
updateProjectLabelColor: ProjectLabel;
|
||||||
createTaskGroup: TaskGroup;
|
createTaskGroup: TaskGroup;
|
||||||
updateTaskGroupLocation: TaskGroup;
|
updateTaskGroupLocation: TaskGroup;
|
||||||
|
updateTaskGroupName: TaskGroup;
|
||||||
deleteTaskGroup: DeleteTaskGroupPayload;
|
deleteTaskGroup: DeleteTaskGroupPayload;
|
||||||
addTaskLabel: Task;
|
addTaskLabel: Task;
|
||||||
removeTaskLabel: Task;
|
removeTaskLabel: Task;
|
||||||
@ -333,6 +345,7 @@ export type Mutation = {
|
|||||||
updateTaskDescription: Task;
|
updateTaskDescription: Task;
|
||||||
updateTaskLocation: UpdateTaskLocationPayload;
|
updateTaskLocation: UpdateTaskLocationPayload;
|
||||||
updateTaskName: Task;
|
updateTaskName: Task;
|
||||||
|
updateTaskDueDate: Task;
|
||||||
deleteTask: DeleteTaskPayload;
|
deleteTask: DeleteTaskPayload;
|
||||||
assignTask: Task;
|
assignTask: Task;
|
||||||
unassignTask: Task;
|
unassignTask: Task;
|
||||||
@ -400,6 +413,11 @@ export type MutationUpdateTaskGroupLocationArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationUpdateTaskGroupNameArgs = {
|
||||||
|
input: UpdateTaskGroupName;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeleteTaskGroupArgs = {
|
export type MutationDeleteTaskGroupArgs = {
|
||||||
input: DeleteTaskGroupInput;
|
input: DeleteTaskGroupInput;
|
||||||
};
|
};
|
||||||
@ -440,6 +458,11 @@ export type MutationUpdateTaskNameArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationUpdateTaskDueDateArgs = {
|
||||||
|
input: UpdateTaskDueDate;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationDeleteTaskArgs = {
|
export type MutationDeleteTaskArgs = {
|
||||||
input: DeleteTaskInput;
|
input: DeleteTaskInput;
|
||||||
};
|
};
|
||||||
@ -658,7 +681,7 @@ export type FindProjectQuery = (
|
|||||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||||
& { tasks: Array<(
|
& { tasks: Array<(
|
||||||
{ __typename?: 'Task' }
|
{ __typename?: 'Task' }
|
||||||
& Pick<Task, 'id' | 'name' | 'position' | 'description'>
|
& Pick<Task, 'id' | 'name' | 'position' | 'description' | 'dueDate'>
|
||||||
& { taskGroup: (
|
& { taskGroup: (
|
||||||
{ __typename?: 'TaskGroup' }
|
{ __typename?: 'TaskGroup' }
|
||||||
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
& Pick<TaskGroup, 'id' | 'name' | 'position'>
|
||||||
@ -698,7 +721,7 @@ export type FindTaskQuery = (
|
|||||||
{ __typename?: 'Query' }
|
{ __typename?: 'Query' }
|
||||||
& { findTask: (
|
& { findTask: (
|
||||||
{ __typename?: 'Task' }
|
{ __typename?: 'Task' }
|
||||||
& Pick<Task, 'id' | 'name' | 'description' | 'position'>
|
& Pick<Task, 'id' | 'name' | 'description' | 'dueDate' | 'position'>
|
||||||
& { taskGroup: (
|
& { taskGroup: (
|
||||||
{ __typename?: 'TaskGroup' }
|
{ __typename?: 'TaskGroup' }
|
||||||
& Pick<TaskGroup, 'id'>
|
& Pick<TaskGroup, 'id'>
|
||||||
@ -852,6 +875,20 @@ export type UpdateTaskDescriptionMutation = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type UpdateTaskDueDateMutationVariables = {
|
||||||
|
taskID: Scalars['UUID'];
|
||||||
|
dueDate?: Maybe<Scalars['Time']>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type UpdateTaskDueDateMutation = (
|
||||||
|
{ __typename?: 'Mutation' }
|
||||||
|
& { updateTaskDueDate: (
|
||||||
|
{ __typename?: 'Task' }
|
||||||
|
& Pick<Task, 'id' | 'dueDate'>
|
||||||
|
) }
|
||||||
|
);
|
||||||
|
|
||||||
export type UpdateTaskGroupLocationMutationVariables = {
|
export type UpdateTaskGroupLocationMutationVariables = {
|
||||||
taskGroupID: Scalars['UUID'];
|
taskGroupID: Scalars['UUID'];
|
||||||
position: Scalars['Float'];
|
position: Scalars['Float'];
|
||||||
@ -1298,6 +1335,7 @@ export const FindProjectDocument = gql`
|
|||||||
name
|
name
|
||||||
position
|
position
|
||||||
description
|
description
|
||||||
|
dueDate
|
||||||
taskGroup {
|
taskGroup {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@ -1370,6 +1408,7 @@ export const FindTaskDocument = gql`
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
description
|
description
|
||||||
|
dueDate
|
||||||
position
|
position
|
||||||
taskGroup {
|
taskGroup {
|
||||||
id
|
id
|
||||||
@ -1705,6 +1744,40 @@ export function useUpdateTaskDescriptionMutation(baseOptions?: ApolloReactHooks.
|
|||||||
export type UpdateTaskDescriptionMutationHookResult = ReturnType<typeof useUpdateTaskDescriptionMutation>;
|
export type UpdateTaskDescriptionMutationHookResult = ReturnType<typeof useUpdateTaskDescriptionMutation>;
|
||||||
export type UpdateTaskDescriptionMutationResult = ApolloReactCommon.MutationResult<UpdateTaskDescriptionMutation>;
|
export type UpdateTaskDescriptionMutationResult = ApolloReactCommon.MutationResult<UpdateTaskDescriptionMutation>;
|
||||||
export type UpdateTaskDescriptionMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskDescriptionMutation, UpdateTaskDescriptionMutationVariables>;
|
export type UpdateTaskDescriptionMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskDescriptionMutation, UpdateTaskDescriptionMutationVariables>;
|
||||||
|
export const UpdateTaskDueDateDocument = gql`
|
||||||
|
mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time) {
|
||||||
|
updateTaskDueDate(input: {taskID: $taskID, dueDate: $dueDate}) {
|
||||||
|
id
|
||||||
|
dueDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type UpdateTaskDueDateMutationFn = ApolloReactCommon.MutationFunction<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useUpdateTaskDueDateMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useUpdateTaskDueDateMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useUpdateTaskDueDateMutation` 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 [updateTaskDueDateMutation, { data, loading, error }] = useUpdateTaskDueDateMutation({
|
||||||
|
* variables: {
|
||||||
|
* taskID: // value for 'taskID'
|
||||||
|
* dueDate: // value for 'dueDate'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useUpdateTaskDueDateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>) {
|
||||||
|
return ApolloReactHooks.useMutation<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>(UpdateTaskDueDateDocument, baseOptions);
|
||||||
|
}
|
||||||
|
export type UpdateTaskDueDateMutationHookResult = ReturnType<typeof useUpdateTaskDueDateMutation>;
|
||||||
|
export type UpdateTaskDueDateMutationResult = ApolloReactCommon.MutationResult<UpdateTaskDueDateMutation>;
|
||||||
|
export type UpdateTaskDueDateMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskDueDateMutation, UpdateTaskDueDateMutationVariables>;
|
||||||
export const UpdateTaskGroupLocationDocument = gql`
|
export const UpdateTaskGroupLocationDocument = gql`
|
||||||
mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) {
|
mutation updateTaskGroupLocation($taskGroupID: UUID!, $position: Float!) {
|
||||||
updateTaskGroupLocation(input: {taskGroupID: $taskGroupID, position: $position}) {
|
updateTaskGroupLocation(input: {taskGroupID: $taskGroupID, position: $position}) {
|
||||||
|
@ -30,6 +30,7 @@ query findProject($projectId: String!) {
|
|||||||
name
|
name
|
||||||
position
|
position
|
||||||
description
|
description
|
||||||
|
dueDate
|
||||||
taskGroup {
|
taskGroup {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
@ -3,6 +3,7 @@ query findTask($taskID: UUID!) {
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
description
|
description
|
||||||
|
dueDate
|
||||||
position
|
position
|
||||||
taskGroup {
|
taskGroup {
|
||||||
id
|
id
|
||||||
|
12
web/src/shared/graphql/updateTaskDueDate.graphqls
Normal file
12
web/src/shared/graphql/updateTaskDueDate.graphqls
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
mutation updateTaskDueDate($taskID: UUID!, $dueDate: Time) {
|
||||||
|
updateTaskDueDate (
|
||||||
|
input: {
|
||||||
|
taskID: $taskID
|
||||||
|
dueDate: $dueDate
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
dueDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
type Props = {
|
const Bolt: React.FC<IconProps> = ({ width = '16px', height = '16px' }) => {
|
||||||
size: number | string;
|
|
||||||
color: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Bolt = ({ size, color }: Props) => {
|
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" width={size} height={size}>
|
<Icon width={width} height={height} viewBox="0 0 320 512">
|
||||||
<path
|
<path 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" />
|
||||||
fill={color}
|
</Icon>
|
||||||
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;
|
export default Bolt;
|
||||||
|
28
web/src/shared/icons/Icon.tsx
Normal file
28
web/src/shared/icons/Icon.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components/macro';
|
||||||
|
|
||||||
|
export type IconProps = {
|
||||||
|
width: number | string;
|
||||||
|
height: number | string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
width: number | string;
|
||||||
|
height: number | string;
|
||||||
|
viewBox: string;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Svg = styled.svg`
|
||||||
|
fill: rgba(${props => props.theme.colors.text.primary});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Icon: React.FC<Props> = ({ width, height, viewBox, className, children }) => {
|
||||||
|
return (
|
||||||
|
<Svg className={className} width={width} height={height} xmlns="http://www.w3.org/2000/svg" viewBox={viewBox}>
|
||||||
|
{children}
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Icon;
|
@ -1,24 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import Icon, { IconProps } from './Icon';
|
||||||
|
|
||||||
type Props = {
|
const Tags: React.FC<IconProps> = ({ width = '16px', height = '16px' }) => {
|
||||||
size: number | string;
|
|
||||||
color: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Tags = ({ size, color }: Props) => {
|
|
||||||
return (
|
return (
|
||||||
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
|
<Icon width={width} height={height} viewBox="0 0 640 512">
|
||||||
<path
|
<path 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" />
|
||||||
fill={color}
|
</Icon>
|
||||||
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;
|
export default Tags;
|
||||||
|
@ -1,24 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
type Props = {
|
import Icon, { IconProps } from './Icon';
|
||||||
size: number | string;
|
|
||||||
color: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ToggleOn = ({ size, color }: Props) => {
|
const ToggleOn: React.FC<IconProps> = ({ width = '16px', height = '16px' }) => {
|
||||||
return (
|
return (
|
||||||
<svg width={size} height={size} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
<Icon width={width} height={height} viewBox="0 0 576 512">
|
||||||
<path
|
<path 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" />
|
||||||
fill={color}
|
</Icon>
|
||||||
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;
|
export default ToggleOn;
|
||||||
|
Loading…
Reference in New Issue
Block a user