feat: add task activity
This commit is contained in:
@ -4,6 +4,7 @@ package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@ -103,6 +104,22 @@ type Task struct {
|
||||
CompletedAt sql.NullTime `json:"completed_at"`
|
||||
}
|
||||
|
||||
type TaskActivity struct {
|
||||
TaskActivityID uuid.UUID `json:"task_activity_id"`
|
||||
Active bool `json:"active"`
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CausedBy uuid.UUID `json:"caused_by"`
|
||||
ActivityTypeID int32 `json:"activity_type_id"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
type TaskActivityType struct {
|
||||
TaskActivityTypeID int32 `json:"task_activity_type_id"`
|
||||
Code string `json:"code"`
|
||||
Template string `json:"template"`
|
||||
}
|
||||
|
||||
type TaskAssigned struct {
|
||||
TaskAssignedID uuid.UUID `json:"task_assigned_id"`
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
@ -128,6 +145,16 @@ type TaskChecklistItem struct {
|
||||
DueDate sql.NullTime `json:"due_date"`
|
||||
}
|
||||
|
||||
type TaskComment struct {
|
||||
TaskCommentID uuid.UUID `json:"task_comment_id"`
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
CreatedBy uuid.UUID `json:"created_by"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type TaskGroup struct {
|
||||
TaskGroupID uuid.UUID `json:"task_group_id"`
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
|
@ -23,10 +23,12 @@ type Querier interface {
|
||||
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error)
|
||||
CreateSystemOption(ctx context.Context, arg CreateSystemOptionParams) (SystemOption, error)
|
||||
CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error)
|
||||
CreateTaskActivity(ctx context.Context, arg CreateTaskActivityParams) (TaskActivity, error)
|
||||
CreateTaskAll(ctx context.Context, arg CreateTaskAllParams) (Task, error)
|
||||
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
|
||||
CreateTaskChecklist(ctx context.Context, arg CreateTaskChecklistParams) (TaskChecklist, error)
|
||||
CreateTaskChecklistItem(ctx context.Context, arg CreateTaskChecklistItemParams) (TaskChecklistItem, error)
|
||||
CreateTaskComment(ctx context.Context, arg CreateTaskCommentParams) (TaskComment, error)
|
||||
CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error)
|
||||
CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error)
|
||||
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
||||
@ -47,6 +49,7 @@ type Querier interface {
|
||||
DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error
|
||||
DeleteTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) error
|
||||
DeleteTaskChecklistItem(ctx context.Context, taskChecklistItemID uuid.UUID) error
|
||||
DeleteTaskCommentByID(ctx context.Context, taskCommentID uuid.UUID) (TaskComment, error)
|
||||
DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
||||
DeleteTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) error
|
||||
DeleteTaskLabelForTaskByProjectLabelID(ctx context.Context, arg DeleteTaskLabelForTaskByProjectLabelIDParams) error
|
||||
@ -55,6 +58,7 @@ type Querier interface {
|
||||
DeleteTeamMember(ctx context.Context, arg DeleteTeamMemberParams) error
|
||||
DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error
|
||||
DeleteUserAccountInvitedForEmail(ctx context.Context, email string) error
|
||||
GetActivityForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskActivity, error)
|
||||
GetAllNotificationsForUserID(ctx context.Context, notifierID uuid.UUID) ([]Notification, error)
|
||||
GetAllOrganizations(ctx context.Context) ([]Organization, error)
|
||||
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
||||
@ -65,6 +69,7 @@ type Querier interface {
|
||||
GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
||||
GetAllVisibleProjectsForUserID(ctx context.Context, userID uuid.UUID) ([]Project, error)
|
||||
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
||||
GetCommentsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskComment, error)
|
||||
GetConfirmTokenByEmail(ctx context.Context, email string) (UserAccountConfirmToken, error)
|
||||
GetConfirmTokenByID(ctx context.Context, confirmTokenID uuid.UUID) (UserAccountConfirmToken, error)
|
||||
GetEntityForNotificationID(ctx context.Context, notificationID uuid.UUID) (GetEntityForNotificationIDRow, error)
|
||||
@ -74,6 +79,7 @@ type Querier interface {
|
||||
GetInvitedUserByEmail(ctx context.Context, email string) (UserAccountInvited, error)
|
||||
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
||||
GetLabelColors(ctx context.Context) ([]LabelColor, error)
|
||||
GetLastMoveForTaskID(ctx context.Context, taskID uuid.UUID) (GetLastMoveForTaskIDRow, error)
|
||||
GetMemberData(ctx context.Context, projectID uuid.UUID) ([]UserAccount, error)
|
||||
GetMemberProjectIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
||||
GetMemberTeamIDsForUserID(ctx context.Context, userID uuid.UUID) ([]uuid.UUID, error)
|
||||
@ -113,6 +119,7 @@ type Querier interface {
|
||||
GetTeamRolesForUserID(ctx context.Context, userID uuid.UUID) ([]GetTeamRolesForUserIDRow, error)
|
||||
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
||||
GetTeamsForUserIDWhereAdmin(ctx context.Context, userID uuid.UUID) ([]Team, error)
|
||||
GetTemplateForActivityID(ctx context.Context, taskActivityTypeID int32) (string, error)
|
||||
GetUserAccountByEmail(ctx context.Context, email string) (UserAccount, error)
|
||||
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
||||
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
||||
@ -120,6 +127,7 @@ type Querier interface {
|
||||
HasActiveUser(ctx context.Context) (bool, error)
|
||||
HasAnyUser(ctx context.Context) (bool, error)
|
||||
SetFirstUserActive(ctx context.Context) (UserAccount, error)
|
||||
SetInactiveLastMoveForTaskID(ctx context.Context, taskID uuid.UUID) error
|
||||
SetTaskChecklistItemComplete(ctx context.Context, arg SetTaskChecklistItemCompleteParams) (TaskChecklistItem, error)
|
||||
SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams) (Task, error)
|
||||
SetTaskGroupName(ctx context.Context, arg SetTaskGroupNameParams) (TaskGroup, error)
|
||||
@ -134,6 +142,7 @@ type Querier interface {
|
||||
UpdateTaskChecklistItemName(ctx context.Context, arg UpdateTaskChecklistItemNameParams) (TaskChecklistItem, error)
|
||||
UpdateTaskChecklistName(ctx context.Context, arg UpdateTaskChecklistNameParams) (TaskChecklist, error)
|
||||
UpdateTaskChecklistPosition(ctx context.Context, arg UpdateTaskChecklistPositionParams) (TaskChecklist, error)
|
||||
UpdateTaskComment(ctx context.Context, arg UpdateTaskCommentParams) (TaskComment, 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)
|
||||
|
@ -43,3 +43,16 @@ UPDATE task SET complete = $2, completed_at = $3 WHERE task_id = $1 RETURNING *;
|
||||
SELECT project_id FROM task
|
||||
INNER JOIN task_group ON task_group.task_group_id = task.task_group_id
|
||||
WHERE task_id = $1;
|
||||
|
||||
-- name: CreateTaskComment :one
|
||||
INSERT INTO task_comment (task_id, message, created_at, created_by)
|
||||
VALUES ($1, $2, $3, $4) RETURNING *;
|
||||
|
||||
-- name: GetCommentsForTaskID :many
|
||||
SELECT * FROM task_comment WHERE task_id = $1 ORDER BY created_at;
|
||||
|
||||
-- name: DeleteTaskCommentByID :one
|
||||
DELETE FROM task_comment WHERE task_comment_id = $1 RETURNING *;
|
||||
|
||||
-- name: UpdateTaskComment :one
|
||||
UPDATE task_comment SET message = $2, updated_at = $3 WHERE task_comment_id = $1 RETURNING *;
|
||||
|
22
internal/db/query/task_activity.sql
Normal file
22
internal/db/query/task_activity.sql
Normal file
@ -0,0 +1,22 @@
|
||||
-- name: CreateTaskActivity :one
|
||||
INSERT INTO task_activity (task_id, caused_by, created_at, activity_type_id, data)
|
||||
VALUES ($1, $2, $3, $4, $5) RETURNING *;
|
||||
|
||||
-- name: GetActivityForTaskID :many
|
||||
SELECT * FROM task_activity WHERE task_id = $1 AND active = true;
|
||||
|
||||
-- name: GetTemplateForActivityID :one
|
||||
SELECT template FROM task_activity_type WHERE task_activity_type_id = $1;
|
||||
|
||||
-- name: GetLastMoveForTaskID :one
|
||||
SELECT active, created_at, data->>'CurTaskGroupID' AS cur_task_group_id, data->>'PrevTaskGroupID' AS prev_task_group_id FROM task_activity
|
||||
WHERE task_id = $1 AND activity_type_id = 2 AND created_at >= NOW() - INTERVAL '5 minutes'
|
||||
ORDER BY created_at DESC LIMIT 1;
|
||||
|
||||
-- name: SetInactiveLastMoveForTaskID :exec
|
||||
UPDATE task_activity SET active = false WHERE task_activity_id = (
|
||||
SELECT task_activity_id FROM task_activity AS ta
|
||||
WHERE ta.activity_type_id = 2 AND ta.task_id = $1
|
||||
AND ta.created_at >= NOW() - INTERVAL '5 minutes'
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
);
|
@ -85,6 +85,38 @@ func (q *Queries) CreateTaskAll(ctx context.Context, arg CreateTaskAllParams) (T
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createTaskComment = `-- name: CreateTaskComment :one
|
||||
INSERT INTO task_comment (task_id, message, created_at, created_by)
|
||||
VALUES ($1, $2, $3, $4) RETURNING task_comment_id, task_id, created_at, updated_at, created_by, pinned, message
|
||||
`
|
||||
|
||||
type CreateTaskCommentParams struct {
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CreatedBy uuid.UUID `json:"created_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateTaskComment(ctx context.Context, arg CreateTaskCommentParams) (TaskComment, error) {
|
||||
row := q.db.QueryRowContext(ctx, createTaskComment,
|
||||
arg.TaskID,
|
||||
arg.Message,
|
||||
arg.CreatedAt,
|
||||
arg.CreatedBy,
|
||||
)
|
||||
var i TaskComment
|
||||
err := row.Scan(
|
||||
&i.TaskCommentID,
|
||||
&i.TaskID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.Pinned,
|
||||
&i.Message,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteTaskByID = `-- name: DeleteTaskByID :exec
|
||||
DELETE FROM task WHERE task_id = $1
|
||||
`
|
||||
@ -94,6 +126,25 @@ func (q *Queries) DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteTaskCommentByID = `-- name: DeleteTaskCommentByID :one
|
||||
DELETE FROM task_comment WHERE task_comment_id = $1 RETURNING task_comment_id, task_id, created_at, updated_at, created_by, pinned, message
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteTaskCommentByID(ctx context.Context, taskCommentID uuid.UUID) (TaskComment, error) {
|
||||
row := q.db.QueryRowContext(ctx, deleteTaskCommentByID, taskCommentID)
|
||||
var i TaskComment
|
||||
err := row.Scan(
|
||||
&i.TaskCommentID,
|
||||
&i.TaskID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.Pinned,
|
||||
&i.Message,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteTasksByTaskGroupID = `-- name: DeleteTasksByTaskGroupID :execrows
|
||||
DELETE FROM task where task_group_id = $1
|
||||
`
|
||||
@ -143,6 +194,41 @@ func (q *Queries) GetAllTasks(ctx context.Context) ([]Task, error) {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCommentsForTaskID = `-- name: GetCommentsForTaskID :many
|
||||
SELECT task_comment_id, task_id, created_at, updated_at, created_by, pinned, message FROM task_comment WHERE task_id = $1 ORDER BY created_at
|
||||
`
|
||||
|
||||
func (q *Queries) GetCommentsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskComment, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCommentsForTaskID, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []TaskComment
|
||||
for rows.Next() {
|
||||
var i TaskComment
|
||||
if err := rows.Scan(
|
||||
&i.TaskCommentID,
|
||||
&i.TaskID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.Pinned,
|
||||
&i.Message,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getProjectIDForTask = `-- name: GetProjectIDForTask :one
|
||||
SELECT project_id FROM task
|
||||
INNER JOIN task_group ON task_group.task_group_id = task.task_group_id
|
||||
@ -241,6 +327,31 @@ func (q *Queries) SetTaskComplete(ctx context.Context, arg SetTaskCompleteParams
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateTaskComment = `-- name: UpdateTaskComment :one
|
||||
UPDATE task_comment SET message = $2, updated_at = $3 WHERE task_comment_id = $1 RETURNING task_comment_id, task_id, created_at, updated_at, created_by, pinned, message
|
||||
`
|
||||
|
||||
type UpdateTaskCommentParams struct {
|
||||
TaskCommentID uuid.UUID `json:"task_comment_id"`
|
||||
Message string `json:"message"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateTaskComment(ctx context.Context, arg UpdateTaskCommentParams) (TaskComment, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateTaskComment, arg.TaskCommentID, arg.Message, arg.UpdatedAt)
|
||||
var i TaskComment
|
||||
err := row.Scan(
|
||||
&i.TaskCommentID,
|
||||
&i.TaskID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.Pinned,
|
||||
&i.Message,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateTaskDescription = `-- name: UpdateTaskDescription :one
|
||||
UPDATE task SET description = $2 WHERE task_id = $1 RETURNING task_id, task_group_id, created_at, name, position, description, due_date, complete, completed_at
|
||||
`
|
||||
|
131
internal/db/task_activity.sql.go
Normal file
131
internal/db/task_activity.sql.go
Normal file
@ -0,0 +1,131 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// source: task_activity.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const createTaskActivity = `-- name: CreateTaskActivity :one
|
||||
INSERT INTO task_activity (task_id, caused_by, created_at, activity_type_id, data)
|
||||
VALUES ($1, $2, $3, $4, $5) RETURNING task_activity_id, active, task_id, created_at, caused_by, activity_type_id, data
|
||||
`
|
||||
|
||||
type CreateTaskActivityParams struct {
|
||||
TaskID uuid.UUID `json:"task_id"`
|
||||
CausedBy uuid.UUID `json:"caused_by"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ActivityTypeID int32 `json:"activity_type_id"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateTaskActivity(ctx context.Context, arg CreateTaskActivityParams) (TaskActivity, error) {
|
||||
row := q.db.QueryRowContext(ctx, createTaskActivity,
|
||||
arg.TaskID,
|
||||
arg.CausedBy,
|
||||
arg.CreatedAt,
|
||||
arg.ActivityTypeID,
|
||||
arg.Data,
|
||||
)
|
||||
var i TaskActivity
|
||||
err := row.Scan(
|
||||
&i.TaskActivityID,
|
||||
&i.Active,
|
||||
&i.TaskID,
|
||||
&i.CreatedAt,
|
||||
&i.CausedBy,
|
||||
&i.ActivityTypeID,
|
||||
&i.Data,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getActivityForTaskID = `-- name: GetActivityForTaskID :many
|
||||
SELECT task_activity_id, active, task_id, created_at, caused_by, activity_type_id, data FROM task_activity WHERE task_id = $1 AND active = true
|
||||
`
|
||||
|
||||
func (q *Queries) GetActivityForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskActivity, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getActivityForTaskID, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []TaskActivity
|
||||
for rows.Next() {
|
||||
var i TaskActivity
|
||||
if err := rows.Scan(
|
||||
&i.TaskActivityID,
|
||||
&i.Active,
|
||||
&i.TaskID,
|
||||
&i.CreatedAt,
|
||||
&i.CausedBy,
|
||||
&i.ActivityTypeID,
|
||||
&i.Data,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getLastMoveForTaskID = `-- name: GetLastMoveForTaskID :one
|
||||
SELECT active, created_at, data->>'CurTaskGroupID' AS cur_task_group_id, data->>'PrevTaskGroupID' AS prev_task_group_id FROM task_activity
|
||||
WHERE task_id = $1 AND activity_type_id = 2 AND created_at >= NOW() - INTERVAL '5 minutes'
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
`
|
||||
|
||||
type GetLastMoveForTaskIDRow struct {
|
||||
Active bool `json:"active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
CurTaskGroupID interface{} `json:"cur_task_group_id"`
|
||||
PrevTaskGroupID interface{} `json:"prev_task_group_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetLastMoveForTaskID(ctx context.Context, taskID uuid.UUID) (GetLastMoveForTaskIDRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getLastMoveForTaskID, taskID)
|
||||
var i GetLastMoveForTaskIDRow
|
||||
err := row.Scan(
|
||||
&i.Active,
|
||||
&i.CreatedAt,
|
||||
&i.CurTaskGroupID,
|
||||
&i.PrevTaskGroupID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTemplateForActivityID = `-- name: GetTemplateForActivityID :one
|
||||
SELECT template FROM task_activity_type WHERE task_activity_type_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTemplateForActivityID(ctx context.Context, taskActivityTypeID int32) (string, error) {
|
||||
row := q.db.QueryRowContext(ctx, getTemplateForActivityID, taskActivityTypeID)
|
||||
var template string
|
||||
err := row.Scan(&template)
|
||||
return template, err
|
||||
}
|
||||
|
||||
const setInactiveLastMoveForTaskID = `-- name: SetInactiveLastMoveForTaskID :exec
|
||||
UPDATE task_activity SET active = false WHERE task_activity_id = (
|
||||
SELECT task_activity_id FROM task_activity AS ta
|
||||
WHERE ta.activity_type_id = 2 AND ta.task_id = $1
|
||||
AND ta.created_at >= NOW() - INTERVAL '5 minutes'
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) SetInactiveLastMoveForTaskID(ctx context.Context, taskID uuid.UUID) error {
|
||||
_, err := q.db.ExecContext(ctx, setInactiveLastMoveForTaskID, taskID)
|
||||
return err
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -255,3 +255,28 @@ func GetActionType(actionType int32) ActionType {
|
||||
panic("Not a valid entity type!")
|
||||
}
|
||||
}
|
||||
|
||||
type MemberType string
|
||||
|
||||
const (
|
||||
MemberTypeInvited MemberType = "INVITED"
|
||||
MemberTypeJoined MemberType = "JOINED"
|
||||
)
|
||||
|
||||
type MasterEntry struct {
|
||||
MemberType MemberType
|
||||
ID uuid.UUID
|
||||
}
|
||||
|
||||
const (
|
||||
TASK_ADDED_TO_TASK_GROUP int32 = 1
|
||||
TASK_MOVED_TO_TASK_GROUP int32 = 2
|
||||
TASK_MARK_COMPLETE int32 = 3
|
||||
TASK_MARK_INCOMPLETE int32 = 4
|
||||
TASK_DUE_DATE_CHANGED int32 = 5
|
||||
TASK_DUE_DATE_ADDED int32 = 6
|
||||
TASK_DUE_DATE_REMOVED int32 = 7
|
||||
TASK_CHECKLIST_CHANGED int32 = 8
|
||||
TASK_CHECKLIST_ADDED int32 = 9
|
||||
TASK_CHECKLIST_REMOVED int32 = 10
|
||||
)
|
||||
|
@ -41,3 +41,7 @@ func GetMemberList(ctx context.Context, r db.Repository, user db.UserAccount) (*
|
||||
|
||||
return &MemberList{Teams: teams, Projects: projects}, nil
|
||||
}
|
||||
|
||||
type ActivityData struct {
|
||||
Data map[string]string
|
||||
}
|
||||
|
@ -22,6 +22,12 @@ type AssignTaskInput struct {
|
||||
UserID uuid.UUID `json:"userID"`
|
||||
}
|
||||
|
||||
type CausedBy struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
ProfileIcon *ProfileIcon `json:"profileIcon"`
|
||||
}
|
||||
|
||||
type ChecklistBadge struct {
|
||||
Complete int `json:"complete"`
|
||||
Total int `json:"total"`
|
||||
@ -39,6 +45,16 @@ type CreateTaskChecklistItem struct {
|
||||
Position float64 `json:"position"`
|
||||
}
|
||||
|
||||
type CreateTaskComment struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type CreateTaskCommentPayload struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
Comment *db.TaskComment `json:"comment"`
|
||||
}
|
||||
|
||||
type CreateTeamMember struct {
|
||||
UserID uuid.UUID `json:"userID"`
|
||||
TeamID uuid.UUID `json:"teamID"`
|
||||
@ -49,6 +65,12 @@ type CreateTeamMemberPayload struct {
|
||||
TeamMember *Member `json:"teamMember"`
|
||||
}
|
||||
|
||||
type CreatedBy struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
ProfileIcon *ProfileIcon `json:"profileIcon"`
|
||||
}
|
||||
|
||||
type DeleteInvitedProjectMember struct {
|
||||
ProjectID uuid.UUID `json:"projectID"`
|
||||
Email string `json:"email"`
|
||||
@ -108,6 +130,15 @@ type DeleteTaskChecklistPayload struct {
|
||||
TaskChecklist *db.TaskChecklist `json:"taskChecklist"`
|
||||
}
|
||||
|
||||
type DeleteTaskComment struct {
|
||||
CommentID uuid.UUID `json:"commentID"`
|
||||
}
|
||||
|
||||
type DeleteTaskCommentPayload struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
CommentID uuid.UUID `json:"commentID"`
|
||||
}
|
||||
|
||||
type DeleteTaskGroupInput struct {
|
||||
TaskGroupID uuid.UUID `json:"taskGroupID"`
|
||||
}
|
||||
@ -374,6 +405,11 @@ type SortTaskGroupPayload struct {
|
||||
Tasks []db.Task `json:"tasks"`
|
||||
}
|
||||
|
||||
type TaskActivityData struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type TaskBadges struct {
|
||||
Checklist *ChecklistBadge `json:"checklist"`
|
||||
}
|
||||
@ -466,6 +502,16 @@ type UpdateTaskChecklistName struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type UpdateTaskComment struct {
|
||||
CommentID uuid.UUID `json:"commentID"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type UpdateTaskCommentPayload struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
Comment *db.TaskComment `json:"comment"`
|
||||
}
|
||||
|
||||
type UpdateTaskDescriptionInput struct {
|
||||
TaskID uuid.UUID `json:"taskID"`
|
||||
Description string `json:"description"`
|
||||
@ -615,6 +661,63 @@ func (e ActionType) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
type ActivityType string
|
||||
|
||||
const (
|
||||
ActivityTypeTaskAdded ActivityType = "TASK_ADDED"
|
||||
ActivityTypeTaskMoved ActivityType = "TASK_MOVED"
|
||||
ActivityTypeTaskMarkedComplete ActivityType = "TASK_MARKED_COMPLETE"
|
||||
ActivityTypeTaskMarkedIncomplete ActivityType = "TASK_MARKED_INCOMPLETE"
|
||||
ActivityTypeTaskDueDateChanged ActivityType = "TASK_DUE_DATE_CHANGED"
|
||||
ActivityTypeTaskDueDateAdded ActivityType = "TASK_DUE_DATE_ADDED"
|
||||
ActivityTypeTaskDueDateRemoved ActivityType = "TASK_DUE_DATE_REMOVED"
|
||||
ActivityTypeTaskChecklistChanged ActivityType = "TASK_CHECKLIST_CHANGED"
|
||||
ActivityTypeTaskChecklistAdded ActivityType = "TASK_CHECKLIST_ADDED"
|
||||
ActivityTypeTaskChecklistRemoved ActivityType = "TASK_CHECKLIST_REMOVED"
|
||||
)
|
||||
|
||||
var AllActivityType = []ActivityType{
|
||||
ActivityTypeTaskAdded,
|
||||
ActivityTypeTaskMoved,
|
||||
ActivityTypeTaskMarkedComplete,
|
||||
ActivityTypeTaskMarkedIncomplete,
|
||||
ActivityTypeTaskDueDateChanged,
|
||||
ActivityTypeTaskDueDateAdded,
|
||||
ActivityTypeTaskDueDateRemoved,
|
||||
ActivityTypeTaskChecklistChanged,
|
||||
ActivityTypeTaskChecklistAdded,
|
||||
ActivityTypeTaskChecklistRemoved,
|
||||
}
|
||||
|
||||
func (e ActivityType) IsValid() bool {
|
||||
switch e {
|
||||
case ActivityTypeTaskAdded, ActivityTypeTaskMoved, ActivityTypeTaskMarkedComplete, ActivityTypeTaskMarkedIncomplete, ActivityTypeTaskDueDateChanged, ActivityTypeTaskDueDateAdded, ActivityTypeTaskDueDateRemoved, ActivityTypeTaskChecklistChanged, ActivityTypeTaskChecklistAdded, ActivityTypeTaskChecklistRemoved:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e ActivityType) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *ActivityType) UnmarshalGQL(v interface{}) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = ActivityType(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid ActivityType", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e ActivityType) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
type ActorType string
|
||||
|
||||
const (
|
||||
|
@ -135,6 +135,38 @@ type TaskBadges {
|
||||
checklist: ChecklistBadge
|
||||
}
|
||||
|
||||
type CausedBy {
|
||||
id: ID!
|
||||
fullName: String!
|
||||
profileIcon: ProfileIcon
|
||||
}
|
||||
|
||||
type TaskActivityData {
|
||||
name: String!
|
||||
value: String!
|
||||
}
|
||||
|
||||
enum ActivityType {
|
||||
TASK_ADDED
|
||||
TASK_MOVED
|
||||
TASK_MARKED_COMPLETE
|
||||
TASK_MARKED_INCOMPLETE
|
||||
TASK_DUE_DATE_CHANGED
|
||||
TASK_DUE_DATE_ADDED
|
||||
TASK_DUE_DATE_REMOVED
|
||||
TASK_CHECKLIST_CHANGED
|
||||
TASK_CHECKLIST_ADDED
|
||||
TASK_CHECKLIST_REMOVED
|
||||
}
|
||||
|
||||
type TaskActivity {
|
||||
id: ID!
|
||||
type: ActivityType!
|
||||
data: [TaskActivityData!]!
|
||||
causedBy: CausedBy!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type Task {
|
||||
id: ID!
|
||||
taskGroup: TaskGroup!
|
||||
@ -149,6 +181,23 @@ type Task {
|
||||
labels: [TaskLabel!]!
|
||||
checklists: [TaskChecklist!]!
|
||||
badges: TaskBadges!
|
||||
activity: [TaskActivity!]!
|
||||
comments: [TaskComment!]!
|
||||
}
|
||||
|
||||
type CreatedBy {
|
||||
id: ID!
|
||||
fullName: String!
|
||||
profileIcon: ProfileIcon!
|
||||
}
|
||||
|
||||
type TaskComment {
|
||||
id: ID!
|
||||
createdAt: Time!
|
||||
updatedAt: Time
|
||||
message: String!
|
||||
createdBy: CreatedBy!
|
||||
pinned: Boolean!
|
||||
}
|
||||
|
||||
type Organization {
|
||||
@ -582,6 +631,44 @@ type DeleteTaskChecklistPayload {
|
||||
taskChecklist: TaskChecklist!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createTaskComment(input: CreateTaskComment):
|
||||
CreateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
|
||||
deleteTaskComment(input: DeleteTaskComment):
|
||||
DeleteTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
|
||||
updateTaskComment(input: UpdateTaskComment):
|
||||
UpdateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
|
||||
}
|
||||
|
||||
input CreateTaskComment {
|
||||
taskID: UUID!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type CreateTaskCommentPayload {
|
||||
taskID: UUID!
|
||||
comment: TaskComment!
|
||||
}
|
||||
|
||||
input UpdateTaskComment {
|
||||
commentID: UUID!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type UpdateTaskCommentPayload {
|
||||
taskID: UUID!
|
||||
comment: TaskComment!
|
||||
}
|
||||
|
||||
input DeleteTaskComment {
|
||||
commentID: UUID!
|
||||
}
|
||||
|
||||
type DeleteTaskCommentPayload {
|
||||
taskID: UUID!
|
||||
commentID: UUID!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
createTaskGroup(input: NewTaskGroup!):
|
||||
TaskGroup! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: PROJECT)
|
||||
@ -856,4 +943,3 @@ type DeleteUserAccountPayload {
|
||||
ok: Boolean!
|
||||
userAccount: UserAccount!
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
@ -17,12 +17,11 @@ import (
|
||||
"github.com/jordanknott/taskcafe/internal/db"
|
||||
"github.com/jordanknott/taskcafe/internal/logger"
|
||||
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||
gomail "gopkg.in/mail.v2"
|
||||
|
||||
hermes "github.com/matcornic/hermes/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
gomail "gopkg.in/mail.v2"
|
||||
)
|
||||
|
||||
func (r *labelColorResolver) ID(ctx context.Context, obj *db.LabelColor) (uuid.UUID, error) {
|
||||
@ -363,6 +362,28 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input NewTask) (*db.T
|
||||
createdAt := time.Now().UTC()
|
||||
logger.New(ctx).WithFields(log.Fields{"positon": input.Position, "taskGroupID": input.TaskGroupID}).Info("creating task")
|
||||
task, err := r.Repository.CreateTask(ctx, db.CreateTaskParams{input.TaskGroupID, createdAt, input.Name, input.Position})
|
||||
if err != nil {
|
||||
logger.New(ctx).WithError(err).Error("issue while creating task")
|
||||
return &db.Task{}, err
|
||||
}
|
||||
taskGroup, err := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID)
|
||||
if err != nil {
|
||||
logger.New(ctx).WithError(err).Error("issue while creating task")
|
||||
return &db.Task{}, err
|
||||
}
|
||||
data := map[string]string{
|
||||
"TaskGroup": taskGroup.Name,
|
||||
}
|
||||
userID, _ := GetUserID(ctx)
|
||||
d, err := json.Marshal(data)
|
||||
_, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{
|
||||
TaskID: task.TaskID,
|
||||
Data: d,
|
||||
CreatedAt: createdAt,
|
||||
CausedBy: userID,
|
||||
ActivityTypeID: 1,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.New(ctx).WithError(err).Error("issue while creating task")
|
||||
return &db.Task{}, err
|
||||
@ -387,12 +408,44 @@ func (r *mutationResolver) UpdateTaskDescription(ctx context.Context, input Upda
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateTaskLocation(ctx context.Context, input NewTaskLocation) (*UpdateTaskLocationPayload, error) {
|
||||
userID, _ := GetUserID(ctx)
|
||||
previousTask, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
return &UpdateTaskLocationPayload{}, err
|
||||
}
|
||||
task, err := r.Repository.UpdateTaskLocation(ctx, db.UpdateTaskLocationParams{input.TaskID, input.TaskGroupID, input.Position})
|
||||
task, _ := r.Repository.UpdateTaskLocation(ctx, db.UpdateTaskLocationParams{TaskID: input.TaskID, TaskGroupID: input.TaskGroupID, Position: input.Position})
|
||||
if previousTask.TaskGroupID != input.TaskGroupID {
|
||||
skipAndDelete := false
|
||||
lastMove, err := r.Repository.GetLastMoveForTaskID(ctx, input.TaskID)
|
||||
if err == nil {
|
||||
if lastMove.Active && lastMove.PrevTaskGroupID == input.TaskGroupID.String() {
|
||||
skipAndDelete = true
|
||||
}
|
||||
}
|
||||
if skipAndDelete {
|
||||
_ = r.Repository.SetInactiveLastMoveForTaskID(ctx, input.TaskID)
|
||||
} else {
|
||||
prevTaskGroup, _ := r.Repository.GetTaskGroupByID(ctx, previousTask.TaskGroupID)
|
||||
curTaskGroup, _ := r.Repository.GetTaskGroupByID(ctx, input.TaskGroupID)
|
||||
|
||||
data := map[string]string{
|
||||
"PrevTaskGroup": prevTaskGroup.Name,
|
||||
"PrevTaskGroupID": prevTaskGroup.TaskGroupID.String(),
|
||||
"CurTaskGroup": curTaskGroup.Name,
|
||||
"CurTaskGroupID": curTaskGroup.TaskGroupID.String(),
|
||||
}
|
||||
|
||||
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: 2,
|
||||
})
|
||||
}
|
||||
}
|
||||
return &UpdateTaskLocationPayload{Task: &task, PreviousTaskGroupID: previousTask.TaskGroupID}, err
|
||||
}
|
||||
|
||||
@ -403,6 +456,21 @@ func (r *mutationResolver) UpdateTaskName(ctx context.Context, input UpdateTaskN
|
||||
|
||||
func (r *mutationResolver) SetTaskComplete(ctx context.Context, input SetTaskComplete) (*db.Task, error) {
|
||||
completedAt := time.Now().UTC()
|
||||
data := map[string]string{}
|
||||
activityType := TASK_MARK_INCOMPLETE
|
||||
if input.Complete {
|
||||
activityType = TASK_MARK_COMPLETE
|
||||
}
|
||||
createdAt := time.Now().UTC()
|
||||
userID, _ := GetUserID(ctx)
|
||||
d, err := json.Marshal(data)
|
||||
_, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{
|
||||
TaskID: input.TaskID,
|
||||
Data: d,
|
||||
CausedBy: userID,
|
||||
CreatedAt: createdAt,
|
||||
ActivityTypeID: activityType,
|
||||
})
|
||||
task, err := r.Repository.SetTaskComplete(ctx, db.SetTaskCompleteParams{TaskID: input.TaskID, Complete: input.Complete, CompletedAt: sql.NullTime{Time: completedAt, Valid: true}})
|
||||
if err != nil {
|
||||
return &db.Task{}, err
|
||||
@ -411,6 +479,23 @@ func (r *mutationResolver) SetTaskComplete(ctx context.Context, input SetTaskCom
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTaskDueDate) (*db.Task, error) {
|
||||
userID, _ := GetUserID(ctx)
|
||||
prevTask, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
||||
if err != nil {
|
||||
return &db.Task{}, err
|
||||
}
|
||||
data := map[string]string{}
|
||||
var activityType = TASK_DUE_DATE_ADDED
|
||||
if input.DueDate == nil && prevTask.DueDate.Valid {
|
||||
activityType = TASK_DUE_DATE_REMOVED
|
||||
data["PrevDueDate"] = prevTask.DueDate.Time.String()
|
||||
} else if prevTask.DueDate.Valid {
|
||||
activityType = TASK_DUE_DATE_CHANGED
|
||||
data["PrevDueDate"] = prevTask.DueDate.Time.String()
|
||||
data["CurDueDate"] = input.DueDate.String()
|
||||
} else {
|
||||
data["DueDate"] = input.DueDate.String()
|
||||
}
|
||||
var dueDate sql.NullTime
|
||||
if input.DueDate == nil {
|
||||
dueDate = sql.NullTime{Valid: false, Time: time.Now()}
|
||||
@ -421,6 +506,15 @@ func (r *mutationResolver) UpdateTaskDueDate(ctx context.Context, input UpdateTa
|
||||
TaskID: input.TaskID,
|
||||
DueDate: dueDate,
|
||||
})
|
||||
createdAt := time.Now().UTC()
|
||||
d, err := json.Marshal(data)
|
||||
_, err = r.Repository.CreateTaskActivity(ctx, db.CreateTaskActivityParams{
|
||||
TaskID: task.TaskID,
|
||||
Data: d,
|
||||
CausedBy: userID,
|
||||
CreatedAt: createdAt,
|
||||
ActivityTypeID: activityType,
|
||||
})
|
||||
|
||||
return &task, err
|
||||
}
|
||||
@ -562,6 +656,33 @@ func (r *mutationResolver) UpdateTaskChecklistItemLocation(ctx context.Context,
|
||||
return &UpdateTaskChecklistItemLocationPayload{PrevChecklistID: currentChecklistItem.TaskChecklistID, TaskChecklistID: input.TaskChecklistID, ChecklistItem: &checklistItem}, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTaskComment(ctx context.Context, input *CreateTaskComment) (*CreateTaskCommentPayload, error) {
|
||||
userID, _ := GetUserID(ctx)
|
||||
createdAt := time.Now().UTC()
|
||||
comment, err := r.Repository.CreateTaskComment(ctx, db.CreateTaskCommentParams{
|
||||
TaskID: input.TaskID,
|
||||
CreatedAt: createdAt,
|
||||
CreatedBy: userID,
|
||||
Message: input.Message,
|
||||
})
|
||||
return &CreateTaskCommentPayload{Comment: &comment, TaskID: input.TaskID}, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) DeleteTaskComment(ctx context.Context, input *DeleteTaskComment) (*DeleteTaskCommentPayload, error) {
|
||||
task, err := r.Repository.DeleteTaskCommentByID(ctx, input.CommentID)
|
||||
return &DeleteTaskCommentPayload{TaskID: task.TaskID, CommentID: input.CommentID}, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) UpdateTaskComment(ctx context.Context, input *UpdateTaskComment) (*UpdateTaskCommentPayload, error) {
|
||||
updatedAt := time.Now().UTC()
|
||||
comment, err := r.Repository.UpdateTaskComment(ctx, db.UpdateTaskCommentParams{
|
||||
TaskCommentID: input.CommentID,
|
||||
UpdatedAt: sql.NullTime{Valid: true, Time: updatedAt},
|
||||
Message: input.Message,
|
||||
})
|
||||
return &UpdateTaskCommentPayload{Comment: &comment}, err
|
||||
}
|
||||
|
||||
func (r *mutationResolver) CreateTaskGroup(ctx context.Context, input NewTaskGroup) (*db.TaskGroup, error) {
|
||||
createdAt := time.Now().UTC()
|
||||
project, err := r.Repository.CreateTaskGroup(ctx,
|
||||
@ -1558,6 +1679,80 @@ func (r *taskResolver) Badges(ctx context.Context, obj *db.Task) (*TaskBadges, e
|
||||
return &TaskBadges{Checklist: &ChecklistBadge{Total: total, Complete: complete}}, nil
|
||||
}
|
||||
|
||||
func (r *taskResolver) Activity(ctx context.Context, obj *db.Task) ([]db.TaskActivity, error) {
|
||||
activity, err := r.Repository.GetActivityForTaskID(ctx, obj.TaskID)
|
||||
if err == sql.ErrNoRows {
|
||||
return []db.TaskActivity{}, nil
|
||||
}
|
||||
return activity, err
|
||||
}
|
||||
|
||||
func (r *taskResolver) Comments(ctx context.Context, obj *db.Task) ([]db.TaskComment, error) {
|
||||
comments, err := r.Repository.GetCommentsForTaskID(ctx, obj.TaskID)
|
||||
if err == sql.ErrNoRows {
|
||||
return []db.TaskComment{}, nil
|
||||
}
|
||||
return comments, err
|
||||
}
|
||||
|
||||
func (r *taskActivityResolver) ID(ctx context.Context, obj *db.TaskActivity) (uuid.UUID, error) {
|
||||
return obj.TaskActivityID, nil
|
||||
}
|
||||
|
||||
func (r *taskActivityResolver) Type(ctx context.Context, obj *db.TaskActivity) (ActivityType, error) {
|
||||
switch obj.ActivityTypeID {
|
||||
case 1:
|
||||
return ActivityTypeTaskAdded, nil
|
||||
case 2:
|
||||
return ActivityTypeTaskMoved, nil
|
||||
case 3:
|
||||
return ActivityTypeTaskMarkedComplete, nil
|
||||
case 4:
|
||||
return ActivityTypeTaskMarkedIncomplete, nil
|
||||
case 5:
|
||||
return ActivityTypeTaskDueDateChanged, nil
|
||||
case 6:
|
||||
return ActivityTypeTaskDueDateAdded, nil
|
||||
case 7:
|
||||
return ActivityTypeTaskDueDateRemoved, nil
|
||||
case 8:
|
||||
return ActivityTypeTaskChecklistChanged, nil
|
||||
case 9:
|
||||
return ActivityTypeTaskChecklistAdded, nil
|
||||
case 10:
|
||||
return ActivityTypeTaskChecklistRemoved, nil
|
||||
default:
|
||||
return ActivityTypeTaskAdded, errors.New("unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *taskActivityResolver) Data(ctx context.Context, obj *db.TaskActivity) ([]TaskActivityData, error) {
|
||||
var data map[string]string
|
||||
_ = json.Unmarshal(obj.Data, &data)
|
||||
activity := []TaskActivityData{}
|
||||
for name, value := range data {
|
||||
activity = append(activity, TaskActivityData{
|
||||
Name: name,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
return activity, nil
|
||||
}
|
||||
|
||||
func (r *taskActivityResolver) CausedBy(ctx context.Context, obj *db.TaskActivity) (*CausedBy, error) {
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, obj.CausedBy)
|
||||
var url *string
|
||||
if user.ProfileAvatarUrl.Valid {
|
||||
url = &user.ProfileAvatarUrl.String
|
||||
}
|
||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||
return &CausedBy{
|
||||
ID: obj.CausedBy,
|
||||
FullName: user.FullName,
|
||||
ProfileIcon: profileIcon,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (r *taskChecklistResolver) ID(ctx context.Context, obj *db.TaskChecklist) (uuid.UUID, error) {
|
||||
return obj.TaskChecklistID, nil
|
||||
}
|
||||
@ -1574,6 +1769,31 @@ func (r *taskChecklistItemResolver) DueDate(ctx context.Context, obj *db.TaskChe
|
||||
panic(fmt.Errorf("not implemented"))
|
||||
}
|
||||
|
||||
func (r *taskCommentResolver) ID(ctx context.Context, obj *db.TaskComment) (uuid.UUID, error) {
|
||||
return obj.TaskCommentID, nil
|
||||
}
|
||||
|
||||
func (r *taskCommentResolver) UpdatedAt(ctx context.Context, obj *db.TaskComment) (*time.Time, error) {
|
||||
if obj.UpdatedAt.Valid {
|
||||
return &obj.UpdatedAt.Time, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *taskCommentResolver) CreatedBy(ctx context.Context, obj *db.TaskComment) (*CreatedBy, error) {
|
||||
user, err := r.Repository.GetUserAccountByID(ctx, obj.CreatedBy)
|
||||
var url *string
|
||||
if user.ProfileAvatarUrl.Valid {
|
||||
url = &user.ProfileAvatarUrl.String
|
||||
}
|
||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||
return &CreatedBy{
|
||||
ID: obj.CreatedBy,
|
||||
FullName: user.FullName,
|
||||
ProfileIcon: profileIcon,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (r *taskGroupResolver) ID(ctx context.Context, obj *db.TaskGroup) (uuid.UUID, error) {
|
||||
return obj.TaskGroupID, nil
|
||||
}
|
||||
@ -1619,6 +1839,7 @@ func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, err
|
||||
if user.ProfileAvatarUrl.Valid {
|
||||
url = &user.ProfileAvatarUrl.String
|
||||
}
|
||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||
role, err := r.Repository.GetRoleForTeamMember(ctx, db.GetRoleForTeamMemberParams{UserID: user.UserID, TeamID: obj.TeamID})
|
||||
if err != nil {
|
||||
logger.New(ctx).WithError(err).Error("get role for projet member by user ID")
|
||||
@ -1634,7 +1855,6 @@ func (r *teamResolver) Members(ctx context.Context, obj *db.Team) ([]Member, err
|
||||
return members, err
|
||||
}
|
||||
|
||||
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||
members = append(members, Member{ID: user.UserID, FullName: user.FullName, ProfileIcon: profileIcon,
|
||||
Username: user.Username, Owned: ownedList, Member: memberList, Role: &db.Role{Code: role.Code, Name: role.Name},
|
||||
})
|
||||
@ -1724,6 +1944,9 @@ func (r *Resolver) RefreshToken() RefreshTokenResolver { return &refreshTokenRes
|
||||
// Task returns TaskResolver implementation.
|
||||
func (r *Resolver) Task() TaskResolver { return &taskResolver{r} }
|
||||
|
||||
// TaskActivity returns TaskActivityResolver implementation.
|
||||
func (r *Resolver) TaskActivity() TaskActivityResolver { return &taskActivityResolver{r} }
|
||||
|
||||
// TaskChecklist returns TaskChecklistResolver implementation.
|
||||
func (r *Resolver) TaskChecklist() TaskChecklistResolver { return &taskChecklistResolver{r} }
|
||||
|
||||
@ -1732,6 +1955,9 @@ func (r *Resolver) TaskChecklistItem() TaskChecklistItemResolver {
|
||||
return &taskChecklistItemResolver{r}
|
||||
}
|
||||
|
||||
// TaskComment returns TaskCommentResolver implementation.
|
||||
func (r *Resolver) TaskComment() TaskCommentResolver { return &taskCommentResolver{r} }
|
||||
|
||||
// TaskGroup returns TaskGroupResolver implementation.
|
||||
func (r *Resolver) TaskGroup() TaskGroupResolver { return &taskGroupResolver{r} }
|
||||
|
||||
@ -1753,27 +1979,11 @@ type projectLabelResolver struct{ *Resolver }
|
||||
type queryResolver struct{ *Resolver }
|
||||
type refreshTokenResolver struct{ *Resolver }
|
||||
type taskResolver struct{ *Resolver }
|
||||
type taskActivityResolver struct{ *Resolver }
|
||||
type taskChecklistResolver struct{ *Resolver }
|
||||
type taskChecklistItemResolver struct{ *Resolver }
|
||||
type taskCommentResolver struct{ *Resolver }
|
||||
type taskGroupResolver struct{ *Resolver }
|
||||
type taskLabelResolver struct{ *Resolver }
|
||||
type teamResolver struct{ *Resolver }
|
||||
type userAccountResolver struct{ *Resolver }
|
||||
|
||||
// !!! WARNING !!!
|
||||
// The code below was going to be deleted when updating resolvers. It has been copied here so you have
|
||||
// one last chance to move it out of harms way if you want. There are two reasons this happens:
|
||||
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
|
||||
// it when you're done.
|
||||
// - You have helper methods in this file. Move them out to keep these resolver files clean.
|
||||
type MemberType string
|
||||
|
||||
const (
|
||||
MemberTypeInvited MemberType = "INVITED"
|
||||
MemberTypeJoined MemberType = "JOINED"
|
||||
)
|
||||
|
||||
type MasterEntry struct {
|
||||
MemberType MemberType
|
||||
ID uuid.UUID
|
||||
}
|
||||
|
@ -135,6 +135,38 @@ type TaskBadges {
|
||||
checklist: ChecklistBadge
|
||||
}
|
||||
|
||||
type CausedBy {
|
||||
id: ID!
|
||||
fullName: String!
|
||||
profileIcon: ProfileIcon
|
||||
}
|
||||
|
||||
type TaskActivityData {
|
||||
name: String!
|
||||
value: String!
|
||||
}
|
||||
|
||||
enum ActivityType {
|
||||
TASK_ADDED
|
||||
TASK_MOVED
|
||||
TASK_MARKED_COMPLETE
|
||||
TASK_MARKED_INCOMPLETE
|
||||
TASK_DUE_DATE_CHANGED
|
||||
TASK_DUE_DATE_ADDED
|
||||
TASK_DUE_DATE_REMOVED
|
||||
TASK_CHECKLIST_CHANGED
|
||||
TASK_CHECKLIST_ADDED
|
||||
TASK_CHECKLIST_REMOVED
|
||||
}
|
||||
|
||||
type TaskActivity {
|
||||
id: ID!
|
||||
type: ActivityType!
|
||||
data: [TaskActivityData!]!
|
||||
causedBy: CausedBy!
|
||||
createdAt: Time!
|
||||
}
|
||||
|
||||
type Task {
|
||||
id: ID!
|
||||
taskGroup: TaskGroup!
|
||||
@ -149,6 +181,23 @@ type Task {
|
||||
labels: [TaskLabel!]!
|
||||
checklists: [TaskChecklist!]!
|
||||
badges: TaskBadges!
|
||||
activity: [TaskActivity!]!
|
||||
comments: [TaskComment!]!
|
||||
}
|
||||
|
||||
type CreatedBy {
|
||||
id: ID!
|
||||
fullName: String!
|
||||
profileIcon: ProfileIcon!
|
||||
}
|
||||
|
||||
type TaskComment {
|
||||
id: ID!
|
||||
createdAt: Time!
|
||||
updatedAt: Time
|
||||
message: String!
|
||||
createdBy: CreatedBy!
|
||||
pinned: Boolean!
|
||||
}
|
||||
|
||||
type Organization {
|
||||
|
37
internal/graph/schema/task_comment.gql
Normal file
37
internal/graph/schema/task_comment.gql
Normal file
@ -0,0 +1,37 @@
|
||||
extend type Mutation {
|
||||
createTaskComment(input: CreateTaskComment):
|
||||
CreateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
|
||||
deleteTaskComment(input: DeleteTaskComment):
|
||||
DeleteTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
|
||||
updateTaskComment(input: UpdateTaskComment):
|
||||
UpdateTaskCommentPayload! @hasRole(roles: [ADMIN, MEMBER], level: PROJECT, type: TASK)
|
||||
}
|
||||
|
||||
input CreateTaskComment {
|
||||
taskID: UUID!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type CreateTaskCommentPayload {
|
||||
taskID: UUID!
|
||||
comment: TaskComment!
|
||||
}
|
||||
|
||||
input UpdateTaskComment {
|
||||
commentID: UUID!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type UpdateTaskCommentPayload {
|
||||
taskID: UUID!
|
||||
comment: TaskComment!
|
||||
}
|
||||
|
||||
input DeleteTaskComment {
|
||||
commentID: UUID!
|
||||
}
|
||||
|
||||
type DeleteTaskCommentPayload {
|
||||
taskID: UUID!
|
||||
commentID: UUID!
|
||||
}
|
Reference in New Issue
Block a user