feat: redesign due date manager

This commit is contained in:
Jordan Knott
2021-11-05 22:35:57 -05:00
parent df6140a10f
commit 0d00fc7518
34 changed files with 2204 additions and 196 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -60,6 +60,16 @@ type CreateTaskCommentPayload struct {
Comment *db.TaskComment `json:"comment"`
}
type CreateTaskDueDateNotification struct {
TaskID uuid.UUID `json:"taskID"`
Period int `json:"period"`
Duration DueDateNotificationDuration `json:"duration"`
}
type CreateTaskDueDateNotificationsResult struct {
Notifications []DueDateNotification `json:"notifications"`
}
type CreateTeamMember struct {
UserID uuid.UUID `json:"userID"`
TeamID uuid.UUID `json:"teamID"`
@@ -144,6 +154,14 @@ type DeleteTaskCommentPayload struct {
CommentID uuid.UUID `json:"commentID"`
}
type DeleteTaskDueDateNotification struct {
ID uuid.UUID `json:"id"`
}
type DeleteTaskDueDateNotificationsResult struct {
Notifications []uuid.UUID `json:"notifications"`
}
type DeleteTaskGroupInput struct {
TaskGroupID uuid.UUID `json:"taskGroupID"`
}
@@ -203,6 +221,17 @@ type DeleteUserAccountPayload struct {
UserAccount *db.UserAccount `json:"userAccount"`
}
type DueDate struct {
At *time.Time `json:"at"`
Notifications []DueDateNotification `json:"notifications"`
}
type DueDateNotification struct {
ID uuid.UUID `json:"id"`
Period int `json:"period"`
Duration DueDateNotificationDuration `json:"duration"`
}
type DuplicateTaskGroup struct {
ProjectID uuid.UUID `json:"projectID"`
TaskGroupID uuid.UUID `json:"taskGroupID"`
@@ -599,6 +628,16 @@ type UpdateTaskDueDate struct {
DueDate *time.Time `json:"dueDate"`
}
type UpdateTaskDueDateNotification struct {
ID uuid.UUID `json:"id"`
Period int `json:"period"`
Duration DueDateNotificationDuration `json:"duration"`
}
type UpdateTaskDueDateNotificationsResult struct {
Notifications []DueDateNotification `json:"notifications"`
}
type UpdateTaskGroupName struct {
TaskGroupID uuid.UUID `json:"taskGroupID"`
Name string `json:"name"`
@@ -821,6 +860,51 @@ func (e ActivityType) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
type DueDateNotificationDuration string
const (
DueDateNotificationDurationMinute DueDateNotificationDuration = "MINUTE"
DueDateNotificationDurationHour DueDateNotificationDuration = "HOUR"
DueDateNotificationDurationDay DueDateNotificationDuration = "DAY"
DueDateNotificationDurationWeek DueDateNotificationDuration = "WEEK"
)
var AllDueDateNotificationDuration = []DueDateNotificationDuration{
DueDateNotificationDurationMinute,
DueDateNotificationDurationHour,
DueDateNotificationDurationDay,
DueDateNotificationDurationWeek,
}
func (e DueDateNotificationDuration) IsValid() bool {
switch e {
case DueDateNotificationDurationMinute, DueDateNotificationDurationHour, DueDateNotificationDurationDay, DueDateNotificationDurationWeek:
return true
}
return false
}
func (e DueDateNotificationDuration) String() string {
return string(e)
}
func (e *DueDateNotificationDuration) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = DueDateNotificationDuration(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid DueDateNotificationDuration", str)
}
return nil
}
func (e DueDateNotificationDuration) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
type MyTasksSort string
const (

View File

@@ -1,3 +1,9 @@
enum DueDateNotificationDuration {
MINUTE
HOUR
DAY
WEEK
}
type TaskLabel {
id: ID!
@@ -20,6 +26,18 @@ type TaskBadges {
comments: CommentsBadge
}
type DueDateNotification {
id: ID!
period: Int!
duration: DueDateNotificationDuration!
}
type DueDate {
at: Time
notifications: [DueDateNotification!]!
}
type Task {
id: ID!
shortId: String!
@@ -29,7 +47,7 @@ type Task {
position: Float!
description: String
watched: Boolean!
dueDate: Time
dueDate: DueDate!
hasTime: Boolean!
complete: Boolean!
completedAt: Time
@@ -371,8 +389,45 @@ extend type Mutation {
Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
unassignTask(input: UnassignTaskInput):
Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
createTaskDueDateNotifications(input: [CreateTaskDueDateNotification!]!):
CreateTaskDueDateNotificationsResult!
updateTaskDueDateNotifications(input: [UpdateTaskDueDateNotification!]!):
UpdateTaskDueDateNotificationsResult!
deleteTaskDueDateNotifications(input: [DeleteTaskDueDateNotification!]!):
DeleteTaskDueDateNotificationsResult!
}
input DeleteTaskDueDateNotification {
id: UUID!
}
type DeleteTaskDueDateNotificationsResult {
notifications: [UUID!]!
}
input UpdateTaskDueDateNotification {
id: UUID!
period: Int!
duration: DueDateNotificationDuration!
}
type UpdateTaskDueDateNotificationsResult {
notifications: [DueDateNotification!]!
}
input CreateTaskDueDateNotification {
taskID: UUID!
period: Int!
duration: DueDateNotificationDuration!
}
type CreateTaskDueDateNotificationsResult {
notifications: [DueDateNotification!]!
}
input ToggleTaskWatch {
taskID: UUID!
}

View File

@@ -1,3 +1,9 @@
enum DueDateNotificationDuration {
MINUTE
HOUR
DAY
WEEK
}
type TaskLabel {
id: ID!
@@ -20,6 +26,18 @@ type TaskBadges {
comments: CommentsBadge
}
type DueDateNotification {
id: ID!
period: Int!
duration: DueDateNotificationDuration!
}
type DueDate {
at: Time
notifications: [DueDateNotification!]!
}
type Task {
id: ID!
shortId: String!
@@ -29,7 +47,7 @@ type Task {
position: Float!
description: String
watched: Boolean!
dueDate: Time
dueDate: DueDate!
hasTime: Boolean!
complete: Boolean!
completedAt: Time

View File

@@ -31,8 +31,45 @@ extend type Mutation {
Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
unassignTask(input: UnassignTaskInput):
Task! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
createTaskDueDateNotifications(input: [CreateTaskDueDateNotification!]!):
CreateTaskDueDateNotificationsResult!
updateTaskDueDateNotifications(input: [UpdateTaskDueDateNotification!]!):
UpdateTaskDueDateNotificationsResult!
deleteTaskDueDateNotifications(input: [DeleteTaskDueDateNotification!]!):
DeleteTaskDueDateNotificationsResult!
}
input DeleteTaskDueDateNotification {
id: UUID!
}
type DeleteTaskDueDateNotificationsResult {
notifications: [UUID!]!
}
input UpdateTaskDueDateNotification {
id: UUID!
period: Int!
duration: DueDateNotificationDuration!
}
type UpdateTaskDueDateNotificationsResult {
notifications: [DueDateNotification!]!
}
input CreateTaskDueDateNotification {
taskID: UUID!
period: Int!
duration: DueDateNotificationDuration!
}
type CreateTaskDueDateNotificationsResult {
notifications: [DueDateNotification!]!
}
input ToggleTaskWatch {
taskID: UUID!
}

View File

@@ -8,7 +8,7 @@ import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"strconv"
"time"
"github.com/google/uuid"
@@ -501,8 +501,20 @@ func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTa
if err != nil {
return &db.Task{}, err
}
isSame := false
if prevTask.DueDate.Valid && input.DueDate != nil {
if prevTask.DueDate.Time == *input.DueDate && prevTask.HasTime == input.HasTime {
isSame = true
}
}
log.WithFields(log.Fields{
"isSame": isSame,
"prev": prevTask.HasTime,
"new": input.HasTime,
}).Info("chekcing same")
data := map[string]string{}
var activityType = TASK_DUE_DATE_ADDED
data["HasTime"] = strconv.FormatBool(input.HasTime)
if input.DueDate == nil && prevTask.DueDate.Valid {
activityType = TASK_DUE_DATE_REMOVED
data["PrevDueDate"] = prevTask.DueDate.Time.String()
@@ -529,13 +541,15 @@ func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTa
})
createdAt := time.Now().UTC()
d, _ := json.Marshal(data)
_, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{
TaskID: task.TaskID,
Data: d,
CausedBy: userID,
CreatedAt: createdAt,
ActivityTypeID: activityType,
})
if !isSame {
_, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{
TaskID: task.TaskID,
Data: d,
CausedBy: userID,
CreatedAt: createdAt,
ActivityTypeID: activityType,
})
}
} else {
task, err = r.Repository.GetTaskByID(ctx, input.TaskID)
}
@@ -670,6 +684,73 @@ func (r *mutationResolver) UnassignTask(ctx context.Context, input *UnassignTask
return &task, nil
}
func (r *mutationResolver) CreateTaskDueDateNotifications(ctx context.Context, input []CreateTaskDueDateNotification) (*CreateTaskDueDateNotificationsResult, error) {
reminders := []DueDateNotification{}
for _, in := range input {
n, err := r.Repository.CreateDueDateReminder(ctx, db.CreateDueDateReminderParams{
TaskID: in.TaskID,
Period: int32(in.Period),
Duration: in.Duration.String(),
})
if err != nil {
return &CreateTaskDueDateNotificationsResult{}, err
}
duration := DueDateNotificationDuration(n.Duration)
if !duration.IsValid() {
log.WithField("duration", n.Duration).Error("invalid duration found")
return &CreateTaskDueDateNotificationsResult{}, errors.New("invalid duration")
}
reminders = append(reminders, DueDateNotification{
ID: n.DueDateReminderID,
Period: int(n.Period),
Duration: duration,
})
}
return &CreateTaskDueDateNotificationsResult{
Notifications: reminders,
}, nil
}
func (r *mutationResolver) UpdateTaskDueDateNotifications(ctx context.Context, input []UpdateTaskDueDateNotification) (*UpdateTaskDueDateNotificationsResult, error) {
reminders := []DueDateNotification{}
for _, in := range input {
n, err := r.Repository.UpdateDueDateReminder(ctx, db.UpdateDueDateReminderParams{
DueDateReminderID: in.ID,
Period: int32(in.Period),
Duration: in.Duration.String(),
})
if err != nil {
return &UpdateTaskDueDateNotificationsResult{}, err
}
duration := DueDateNotificationDuration(n.Duration)
if !duration.IsValid() {
log.WithField("duration", n.Duration).Error("invalid duration found")
return &UpdateTaskDueDateNotificationsResult{}, errors.New("invalid duration")
}
reminders = append(reminders, DueDateNotification{
ID: n.DueDateReminderID,
Period: int(n.Period),
Duration: duration,
})
}
return &UpdateTaskDueDateNotificationsResult{
Notifications: reminders,
}, nil
}
func (r *mutationResolver) DeleteTaskDueDateNotifications(ctx context.Context, input []DeleteTaskDueDateNotification) (*DeleteTaskDueDateNotificationsResult, error) {
ids := []uuid.UUID{}
for _, n := range input {
err := r.Repository.DeleteDueDateReminder(ctx, n.ID)
if err != nil {
log.WithError(err).Error("error while deleting task due date notification")
return &DeleteTaskDueDateNotificationsResult{}, err
}
ids = append(ids, n.ID)
}
return &DeleteTaskDueDateNotificationsResult{Notifications: ids}, nil
}
func (r *queryResolver) FindTask(ctx context.Context, input FindTask) (*db.Task, error) {
var taskID uuid.UUID
var err error
@@ -724,11 +805,34 @@ func (r *taskResolver) Watched(ctx context.Context, obj *db.Task) (bool, error)
return true, nil
}
func (r *taskResolver) DueDate(ctx context.Context, obj *db.Task) (*time.Time, error) {
if obj.DueDate.Valid {
return &obj.DueDate.Time, nil
func (r *taskResolver) DueDate(ctx context.Context, obj *db.Task) (*DueDate, error) {
nots, err := r.Repository.GetDueDateRemindersForTaskID(ctx, obj.TaskID)
if err != nil {
log.WithError(err).Error("error while fetching due date reminders")
return &DueDate{}, err
}
return nil, nil
reminders := []DueDateNotification{}
for _, n := range nots {
duration := DueDateNotificationDuration(n.Duration)
if !duration.IsValid() {
log.WithField("duration", n.Duration).Error("invalid duration found")
return &DueDate{}, errors.New("invalid duration")
}
reminders = append(reminders, DueDateNotification{
ID: n.DueDateReminderID,
Period: int(n.Period),
Duration: duration,
})
}
var time *time.Time
if obj.DueDate.Valid {
time = &obj.DueDate.Time
}
return &DueDate{
At: time,
Notifications: reminders,
}, nil
}
func (r *taskResolver) CompletedAt(ctx context.Context, obj *db.Task) (*time.Time, error) {
@@ -904,7 +1008,10 @@ func (r *taskChecklistItemResolver) ID(ctx context.Context, obj *db.TaskChecklis
}
func (r *taskChecklistItemResolver) DueDate(ctx context.Context, obj *db.TaskChecklistItem) (*time.Time, error) {
panic(fmt.Errorf("not implemented"))
if obj.DueDate.Valid {
return &obj.DueDate.Time, nil
}
return nil, nil
}
func (r *taskCommentResolver) ID(ctx context.Context, obj *db.TaskComment) (uuid.UUID, error) {