feature: UI changes
This commit is contained in:
parent
c38024e692
commit
7e78ee36b4
File diff suppressed because it is too large
Load Diff
@ -57,6 +57,12 @@ type NewProject struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NewProjectLabel struct {
|
||||||
|
ProjectID uuid.UUID `json:"projectID"`
|
||||||
|
LabelColorID uuid.UUID `json:"labelColorID"`
|
||||||
|
Name *string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
type NewRefreshToken struct {
|
type NewRefreshToken struct {
|
||||||
UserID string `json:"userId"`
|
UserID string `json:"userId"`
|
||||||
}
|
}
|
||||||
@ -100,6 +106,7 @@ type NewUserAccount struct {
|
|||||||
type ProfileIcon struct {
|
type ProfileIcon struct {
|
||||||
URL *string `json:"url"`
|
URL *string `json:"url"`
|
||||||
Initials *string `json:"initials"`
|
Initials *string `json:"initials"`
|
||||||
|
BgColor *string `json:"bgColor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectMember struct {
|
type ProjectMember struct {
|
||||||
@ -118,6 +125,11 @@ type RemoveTaskLabelInput struct {
|
|||||||
TaskLabelID uuid.UUID `json:"taskLabelID"`
|
TaskLabelID uuid.UUID `json:"taskLabelID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UnassignTaskInput struct {
|
||||||
|
TaskID uuid.UUID `json:"taskID"`
|
||||||
|
UserID uuid.UUID `json:"userID"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdateTaskDescriptionInput struct {
|
type UpdateTaskDescriptionInput struct {
|
||||||
TaskID uuid.UUID `json:"taskID"`
|
TaskID uuid.UUID `json:"taskID"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
scalar Time
|
scalar Time
|
||||||
scalar UUID
|
scalar UUID
|
||||||
|
|
||||||
|
type ProjectLabel {
|
||||||
|
projectLabelID: ID!
|
||||||
|
createdDate: Time!
|
||||||
|
colorHex: String!
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
|
||||||
type TaskLabel {
|
type TaskLabel {
|
||||||
taskLabelID: ID!
|
taskLabelID: ID!
|
||||||
labelColorID: UUID!
|
projectLabelID: UUID!
|
||||||
|
assignedDate: Time!
|
||||||
colorHex: String!
|
colorHex: String!
|
||||||
|
name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProfileIcon {
|
type ProfileIcon {
|
||||||
url: String
|
url: String
|
||||||
initials: String
|
initials: String
|
||||||
|
bgColor: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProjectMember {
|
type ProjectMember {
|
||||||
@ -50,6 +60,7 @@ type Project {
|
|||||||
owner: ProjectMember!
|
owner: ProjectMember!
|
||||||
taskGroups: [TaskGroup!]!
|
taskGroups: [TaskGroup!]!
|
||||||
members: [ProjectMember!]!
|
members: [ProjectMember!]!
|
||||||
|
labels: [ProjectLabel!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type TaskGroup {
|
type TaskGroup {
|
||||||
@ -174,6 +185,10 @@ input AssignTaskInput {
|
|||||||
userID: UUID!
|
userID: UUID!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input UnassignTaskInput {
|
||||||
|
taskID: UUID!
|
||||||
|
userID: UUID!
|
||||||
|
}
|
||||||
input UpdateTaskDescriptionInput {
|
input UpdateTaskDescriptionInput {
|
||||||
taskID: UUID!
|
taskID: UUID!
|
||||||
description: String!
|
description: String!
|
||||||
@ -189,6 +204,12 @@ input RemoveTaskLabelInput {
|
|||||||
taskLabelID: UUID!
|
taskLabelID: UUID!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input NewProjectLabel {
|
||||||
|
projectID: UUID!
|
||||||
|
labelColorID: UUID!
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||||
|
|
||||||
@ -197,6 +218,7 @@ type Mutation {
|
|||||||
createTeam(input: NewTeam!): Team!
|
createTeam(input: NewTeam!): Team!
|
||||||
|
|
||||||
createProject(input: NewProject!): Project!
|
createProject(input: NewProject!): Project!
|
||||||
|
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
|
||||||
|
|
||||||
createTaskGroup(input: NewTaskGroup!): TaskGroup!
|
createTaskGroup(input: NewTaskGroup!): TaskGroup!
|
||||||
updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup!
|
updateTaskGroupLocation(input: NewTaskGroupLocation!): TaskGroup!
|
||||||
@ -211,6 +233,7 @@ type Mutation {
|
|||||||
updateTaskName(input: UpdateTaskName!): Task!
|
updateTaskName(input: UpdateTaskName!): Task!
|
||||||
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
||||||
assignTask(input: AssignTaskInput): Task!
|
assignTask(input: AssignTaskInput): Task!
|
||||||
|
unassignTask(input: UnassignTaskInput): Task!
|
||||||
|
|
||||||
logoutUser(input: LogoutUser!): Boolean!
|
logoutUser(input: LogoutUser!): Boolean!
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,10 @@ func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject)
|
|||||||
return &project, err
|
return &project, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) CreateProjectLabel(ctx context.Context, input NewProjectLabel) (*pg.ProjectLabel, error) {
|
||||||
|
panic(fmt.Errorf("not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) CreateTaskGroup(ctx context.Context, input NewTaskGroup) (*pg.TaskGroup, error) {
|
func (r *mutationResolver) CreateTaskGroup(ctx context.Context, input NewTaskGroup) (*pg.TaskGroup, error) {
|
||||||
createdAt := time.Now().UTC()
|
createdAt := time.Now().UTC()
|
||||||
projectID, err := uuid.Parse(input.ProjectID)
|
projectID, err := uuid.Parse(input.ProjectID)
|
||||||
@ -164,6 +168,18 @@ func (r *mutationResolver) AssignTask(ctx context.Context, input *AssignTaskInpu
|
|||||||
return &task, err
|
return &task, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) UnassignTask(ctx context.Context, input *UnassignTaskInput) (*pg.Task, error) {
|
||||||
|
task, err := r.Repository.GetTaskByID(ctx, input.TaskID)
|
||||||
|
if err != nil {
|
||||||
|
return &pg.Task{}, err
|
||||||
|
}
|
||||||
|
_, err = r.Repository.DeleteTaskAssignedByID(ctx, pg.DeleteTaskAssignedByIDParams{input.TaskID, input.UserID})
|
||||||
|
if err != nil {
|
||||||
|
return &pg.Task{}, err
|
||||||
|
}
|
||||||
|
return &task, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) {
|
func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bool, error) {
|
||||||
userID, err := uuid.Parse(input.UserID)
|
userID, err := uuid.Parse(input.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -185,7 +201,7 @@ func (r *projectResolver) Owner(ctx context.Context, obj *pg.Project) (*ProjectM
|
|||||||
return &ProjectMember{}, err
|
return &ProjectMember{}, err
|
||||||
}
|
}
|
||||||
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
||||||
profileIcon := &ProfileIcon{nil, &initials}
|
profileIcon := &ProfileIcon{nil, &initials, &user.ProfileBgColor}
|
||||||
return &ProjectMember{obj.Owner, user.FirstName, user.LastName, profileIcon}, nil
|
return &ProjectMember{obj.Owner, user.FirstName, user.LastName, profileIcon}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,11 +216,28 @@ func (r *projectResolver) Members(ctx context.Context, obj *pg.Project) ([]Proje
|
|||||||
return members, err
|
return members, err
|
||||||
}
|
}
|
||||||
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
||||||
profileIcon := &ProfileIcon{nil, &initials}
|
profileIcon := &ProfileIcon{nil, &initials, &user.ProfileBgColor}
|
||||||
members = append(members, ProjectMember{obj.Owner, user.FirstName, user.LastName, profileIcon})
|
members = append(members, ProjectMember{obj.Owner, user.FirstName, user.LastName, profileIcon})
|
||||||
return members, nil
|
return members, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *projectResolver) Labels(ctx context.Context, obj *pg.Project) ([]pg.ProjectLabel, error) {
|
||||||
|
labels, err := r.Repository.GetProjectLabelsForProject(ctx, obj.ProjectID)
|
||||||
|
return labels, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *projectLabelResolver) ColorHex(ctx context.Context, obj *pg.ProjectLabel) (string, error) {
|
||||||
|
labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return labelColor.ColorHex, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *projectLabelResolver) Name(ctx context.Context, obj *pg.ProjectLabel) (*string, error) {
|
||||||
|
panic(fmt.Errorf("not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Users(ctx context.Context) ([]pg.UserAccount, error) {
|
func (r *queryResolver) Users(ctx context.Context) ([]pg.UserAccount, error) {
|
||||||
return r.Repository.GetAllUserAccounts(ctx)
|
return r.Repository.GetAllUserAccounts(ctx)
|
||||||
}
|
}
|
||||||
@ -306,7 +339,7 @@ func (r *taskResolver) Assigned(ctx context.Context, obj *pg.Task) ([]ProjectMem
|
|||||||
return taskMembers, err
|
return taskMembers, err
|
||||||
}
|
}
|
||||||
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
initials := string([]rune(user.FirstName)[0]) + string([]rune(user.LastName)[0])
|
||||||
profileIcon := &ProfileIcon{nil, &initials}
|
profileIcon := &ProfileIcon{nil, &initials, &user.ProfileBgColor}
|
||||||
taskMembers = append(taskMembers, ProjectMember{taskMemberLink.UserID, user.FirstName, user.LastName, profileIcon})
|
taskMembers = append(taskMembers, ProjectMember{taskMemberLink.UserID, user.FirstName, user.LastName, profileIcon})
|
||||||
}
|
}
|
||||||
return taskMembers, nil
|
return taskMembers, nil
|
||||||
@ -326,16 +359,32 @@ func (r *taskGroupResolver) Tasks(ctx context.Context, obj *pg.TaskGroup) ([]pg.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *taskLabelResolver) ColorHex(ctx context.Context, obj *pg.TaskLabel) (string, error) {
|
func (r *taskLabelResolver) ColorHex(ctx context.Context, obj *pg.TaskLabel) (string, error) {
|
||||||
labelColor, err := r.Repository.GetLabelColorByID(ctx, obj.LabelColorID)
|
projectLabel, err := r.Repository.GetProjectLabelByID(ctx, obj.ProjectLabelID)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
labelColor, err := r.Repository.GetLabelColorByID(ctx, projectLabel.LabelColorID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return labelColor.ColorHex, nil
|
return labelColor.ColorHex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *taskLabelResolver) Name(ctx context.Context, obj *pg.TaskLabel) (*string, error) {
|
||||||
|
projectLabel, err := r.Repository.GetProjectLabelByID(ctx, obj.ProjectLabelID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
name := projectLabel.Name
|
||||||
|
if !name.Valid {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &name.String, err
|
||||||
|
}
|
||||||
|
|
||||||
func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *pg.UserAccount) (*ProfileIcon, error) {
|
func (r *userAccountResolver) ProfileIcon(ctx context.Context, obj *pg.UserAccount) (*ProfileIcon, error) {
|
||||||
initials := string([]rune(obj.FirstName)[0]) + string([]rune(obj.LastName)[0])
|
initials := string([]rune(obj.FirstName)[0]) + string([]rune(obj.LastName)[0])
|
||||||
profileIcon := &ProfileIcon{nil, &initials}
|
profileIcon := &ProfileIcon{nil, &initials, &obj.ProfileBgColor}
|
||||||
return profileIcon, nil
|
return profileIcon, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,6 +394,9 @@ func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
|||||||
// Project returns ProjectResolver implementation.
|
// Project returns ProjectResolver implementation.
|
||||||
func (r *Resolver) Project() ProjectResolver { return &projectResolver{r} }
|
func (r *Resolver) Project() ProjectResolver { return &projectResolver{r} }
|
||||||
|
|
||||||
|
// ProjectLabel returns ProjectLabelResolver implementation.
|
||||||
|
func (r *Resolver) ProjectLabel() ProjectLabelResolver { return &projectLabelResolver{r} }
|
||||||
|
|
||||||
// Query returns QueryResolver implementation.
|
// Query returns QueryResolver implementation.
|
||||||
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
|
||||||
|
|
||||||
@ -362,6 +414,7 @@ func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolv
|
|||||||
|
|
||||||
type mutationResolver struct{ *Resolver }
|
type mutationResolver struct{ *Resolver }
|
||||||
type projectResolver struct{ *Resolver }
|
type projectResolver struct{ *Resolver }
|
||||||
|
type projectLabelResolver struct{ *Resolver }
|
||||||
type queryResolver struct{ *Resolver }
|
type queryResolver struct{ *Resolver }
|
||||||
type taskResolver struct{ *Resolver }
|
type taskResolver struct{ *Resolver }
|
||||||
type taskGroupResolver struct{ *Resolver }
|
type taskGroupResolver struct{ *Resolver }
|
||||||
@ -374,6 +427,9 @@ type userAccountResolver struct{ *Resolver }
|
|||||||
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
|
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
|
||||||
// it when you're done.
|
// it when you're done.
|
||||||
// - You have helper methods in this file. Move them out to keep these resolver files clean.
|
// - You have helper methods in this file. Move them out to keep these resolver files clean.
|
||||||
|
func (r *taskLabelResolver) ProjectLabelID(ctx context.Context, obj *pg.TaskLabel) (uuid.UUID, error) {
|
||||||
|
panic(fmt.Errorf("not implemented"))
|
||||||
|
}
|
||||||
func (r *userAccountResolver) DisplayName(ctx context.Context, obj *pg.UserAccount) (string, error) {
|
func (r *userAccountResolver) DisplayName(ctx context.Context, obj *pg.UserAccount) (string, error) {
|
||||||
return obj.FirstName + " " + obj.LastName, nil
|
return obj.FirstName + " " + obj.LastName, nil
|
||||||
}
|
}
|
||||||
|
7
api/migrations/0012-add-project-label-table.up.sql
Normal file
7
api/migrations/0012-add-project-label-table.up.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE project_label (
|
||||||
|
project_label_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
project_id uuid NOT NULL REFERENCES project(project_id),
|
||||||
|
label_color_id uuid NOT NULL REFERENCES label_color(label_color_id),
|
||||||
|
created_date timestamptz NOT NULL,
|
||||||
|
name text
|
||||||
|
);
|
@ -1,6 +1,6 @@
|
|||||||
CREATE TABLE task_label (
|
CREATE TABLE task_label (
|
||||||
task_label_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
task_label_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
task_id uuid NOT NULL REFERENCES task(task_id),
|
task_id uuid NOT NULL REFERENCES task(task_id),
|
||||||
label_color_id uuid NOT NULL REFERENCES label_color(label_color_id),
|
project_label_id uuid NOT NULL REFERENCES project_label(project_label_id),
|
||||||
assigned_date timestamptz NOT NULL
|
assigned_date timestamptz NOT NULL
|
||||||
);
|
);
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE user_account ADD COLUMN profile_bg_color text NOT NULL DEFAULT '#7367F0';
|
@ -29,6 +29,14 @@ type Project struct {
|
|||||||
Owner uuid.UUID `json:"owner"`
|
Owner uuid.UUID `json:"owner"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProjectLabel struct {
|
||||||
|
ProjectLabelID uuid.UUID `json:"project_label_id"`
|
||||||
|
ProjectID uuid.UUID `json:"project_id"`
|
||||||
|
LabelColorID uuid.UUID `json:"label_color_id"`
|
||||||
|
CreatedDate time.Time `json:"created_date"`
|
||||||
|
Name sql.NullString `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
type RefreshToken struct {
|
type RefreshToken struct {
|
||||||
TokenID uuid.UUID `json:"token_id"`
|
TokenID uuid.UUID `json:"token_id"`
|
||||||
UserID uuid.UUID `json:"user_id"`
|
UserID uuid.UUID `json:"user_id"`
|
||||||
@ -62,10 +70,10 @@ type TaskGroup struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TaskLabel struct {
|
type TaskLabel struct {
|
||||||
TaskLabelID uuid.UUID `json:"task_label_id"`
|
TaskLabelID uuid.UUID `json:"task_label_id"`
|
||||||
TaskID uuid.UUID `json:"task_id"`
|
TaskID uuid.UUID `json:"task_id"`
|
||||||
LabelColorID uuid.UUID `json:"label_color_id"`
|
ProjectLabelID uuid.UUID `json:"project_label_id"`
|
||||||
AssignedDate time.Time `json:"assigned_date"`
|
AssignedDate time.Time `json:"assigned_date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Team struct {
|
type Team struct {
|
||||||
@ -76,11 +84,12 @@ type Team struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserAccount struct {
|
type UserAccount struct {
|
||||||
UserID uuid.UUID `json:"user_id"`
|
UserID uuid.UUID `json:"user_id"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
PasswordHash string `json:"password_hash"`
|
PasswordHash string `json:"password_hash"`
|
||||||
|
ProfileBgColor string `json:"profile_bg_color"`
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,10 @@ type Repository interface {
|
|||||||
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
||||||
GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
||||||
|
|
||||||
|
CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error)
|
||||||
|
GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error)
|
||||||
|
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)
|
||||||
|
|
||||||
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error)
|
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error)
|
||||||
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
|
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
|
||||||
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
|
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
|
||||||
@ -55,6 +59,7 @@ type Repository interface {
|
|||||||
|
|
||||||
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
|
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
|
||||||
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
||||||
|
DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type repoSvc struct {
|
type repoSvc struct {
|
||||||
|
92
api/pg/project_label.sql.go
Normal file
92
api/pg/project_label.sql.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// source: project_label.sql
|
||||||
|
|
||||||
|
package pg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const createProjectLabel = `-- name: CreateProjectLabel :one
|
||||||
|
INSERT INTO project_label (project_id, label_color_id, created_date, name)
|
||||||
|
VALUES ($1, $2, $3, $4) RETURNING project_label_id, project_id, label_color_id, created_date, name
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateProjectLabelParams struct {
|
||||||
|
ProjectID uuid.UUID `json:"project_id"`
|
||||||
|
LabelColorID uuid.UUID `json:"label_color_id"`
|
||||||
|
CreatedDate time.Time `json:"created_date"`
|
||||||
|
Name sql.NullString `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createProjectLabel,
|
||||||
|
arg.ProjectID,
|
||||||
|
arg.LabelColorID,
|
||||||
|
arg.CreatedDate,
|
||||||
|
arg.Name,
|
||||||
|
)
|
||||||
|
var i ProjectLabel
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ProjectLabelID,
|
||||||
|
&i.ProjectID,
|
||||||
|
&i.LabelColorID,
|
||||||
|
&i.CreatedDate,
|
||||||
|
&i.Name,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProjectLabelByID = `-- name: GetProjectLabelByID :one
|
||||||
|
SELECT project_label_id, project_id, label_color_id, created_date, name FROM project_label WHERE project_label_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getProjectLabelByID, projectLabelID)
|
||||||
|
var i ProjectLabel
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ProjectLabelID,
|
||||||
|
&i.ProjectID,
|
||||||
|
&i.LabelColorID,
|
||||||
|
&i.CreatedDate,
|
||||||
|
&i.Name,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProjectLabelsForProject = `-- name: GetProjectLabelsForProject :many
|
||||||
|
SELECT project_label_id, project_id, label_color_id, created_date, name FROM project_label WHERE project_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getProjectLabelsForProject, projectID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []ProjectLabel
|
||||||
|
for rows.Next() {
|
||||||
|
var i ProjectLabel
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ProjectLabelID,
|
||||||
|
&i.ProjectID,
|
||||||
|
&i.LabelColorID,
|
||||||
|
&i.CreatedDate,
|
||||||
|
&i.Name,
|
||||||
|
); 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
|
||||||
|
}
|
@ -11,6 +11,7 @@ import (
|
|||||||
type Querier interface {
|
type Querier interface {
|
||||||
CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (Organization, error)
|
CreateOrganization(ctx context.Context, arg CreateOrganizationParams) (Organization, error)
|
||||||
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
|
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
|
||||||
|
CreateProjectLabel(ctx context.Context, arg CreateProjectLabelParams) (ProjectLabel, error)
|
||||||
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error)
|
CreateRefreshToken(ctx context.Context, arg CreateRefreshTokenParams) (RefreshToken, error)
|
||||||
CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error)
|
CreateTask(ctx context.Context, arg CreateTaskParams) (Task, error)
|
||||||
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
|
CreateTaskAssigned(ctx context.Context, arg CreateTaskAssignedParams) (TaskAssigned, error)
|
||||||
@ -21,6 +22,7 @@ type Querier interface {
|
|||||||
DeleteExpiredTokens(ctx context.Context) error
|
DeleteExpiredTokens(ctx context.Context) error
|
||||||
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
|
DeleteRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) error
|
||||||
DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error
|
DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error
|
||||||
|
DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error)
|
||||||
DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error
|
DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error
|
||||||
DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
||||||
DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
||||||
@ -35,6 +37,8 @@ type Querier interface {
|
|||||||
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
GetAssignedMembersForTask(ctx context.Context, taskID uuid.UUID) ([]TaskAssigned, error)
|
||||||
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
GetLabelColorByID(ctx context.Context, labelColorID uuid.UUID) (LabelColor, error)
|
||||||
GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error)
|
GetProjectByID(ctx context.Context, projectID uuid.UUID) (Project, error)
|
||||||
|
GetProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) (ProjectLabel, error)
|
||||||
|
GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error)
|
||||||
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
|
GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error)
|
||||||
GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error)
|
GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error)
|
||||||
GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error)
|
GetTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (TaskGroup, error)
|
||||||
|
@ -33,6 +33,27 @@ func (q *Queries) CreateTaskAssigned(ctx context.Context, arg CreateTaskAssigned
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteTaskAssignedByID = `-- name: DeleteTaskAssignedByID :one
|
||||||
|
DELETE FROM task_assigned WHERE task_id = $1 AND user_id = $2 RETURNING task_assigned_id, task_id, user_id, assigned_date
|
||||||
|
`
|
||||||
|
|
||||||
|
type DeleteTaskAssignedByIDParams struct {
|
||||||
|
TaskID uuid.UUID `json:"task_id"`
|
||||||
|
UserID uuid.UUID `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, deleteTaskAssignedByID, arg.TaskID, arg.UserID)
|
||||||
|
var i TaskAssigned
|
||||||
|
err := row.Scan(
|
||||||
|
&i.TaskAssignedID,
|
||||||
|
&i.TaskID,
|
||||||
|
&i.UserID,
|
||||||
|
&i.AssignedDate,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const getAssignedMembersForTask = `-- name: GetAssignedMembersForTask :many
|
const getAssignedMembersForTask = `-- name: GetAssignedMembersForTask :many
|
||||||
SELECT task_assigned_id, task_id, user_id, assigned_date FROM task_assigned WHERE task_id = $1
|
SELECT task_assigned_id, task_id, user_id, assigned_date FROM task_assigned WHERE task_id = $1
|
||||||
`
|
`
|
||||||
|
@ -11,30 +11,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const createTaskLabelForTask = `-- name: CreateTaskLabelForTask :one
|
const createTaskLabelForTask = `-- name: CreateTaskLabelForTask :one
|
||||||
INSERT INTO task_label (task_id, label_color_id, assigned_date)
|
INSERT INTO task_label (task_id, project_label_id, assigned_date)
|
||||||
VALUES ($1, $2, $3) RETURNING task_label_id, task_id, label_color_id, assigned_date
|
VALUES ($1, $2, $3) RETURNING task_label_id, task_id, project_label_id, assigned_date
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateTaskLabelForTaskParams struct {
|
type CreateTaskLabelForTaskParams struct {
|
||||||
TaskID uuid.UUID `json:"task_id"`
|
TaskID uuid.UUID `json:"task_id"`
|
||||||
LabelColorID uuid.UUID `json:"label_color_id"`
|
ProjectLabelID uuid.UUID `json:"project_label_id"`
|
||||||
AssignedDate time.Time `json:"assigned_date"`
|
AssignedDate time.Time `json:"assigned_date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error) {
|
func (q *Queries) CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error) {
|
||||||
row := q.db.QueryRowContext(ctx, createTaskLabelForTask, arg.TaskID, arg.LabelColorID, arg.AssignedDate)
|
row := q.db.QueryRowContext(ctx, createTaskLabelForTask, arg.TaskID, arg.ProjectLabelID, arg.AssignedDate)
|
||||||
var i TaskLabel
|
var i TaskLabel
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.TaskLabelID,
|
&i.TaskLabelID,
|
||||||
&i.TaskID,
|
&i.TaskID,
|
||||||
&i.LabelColorID,
|
&i.ProjectLabelID,
|
||||||
&i.AssignedDate,
|
&i.AssignedDate,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTaskLabelsForTaskID = `-- name: GetTaskLabelsForTaskID :many
|
const getTaskLabelsForTaskID = `-- name: GetTaskLabelsForTaskID :many
|
||||||
SELECT task_label_id, task_id, label_color_id, assigned_date FROM task_label WHERE task_id = $1
|
SELECT task_label_id, task_id, project_label_id, assigned_date FROM task_label WHERE task_id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error) {
|
func (q *Queries) GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error) {
|
||||||
@ -49,7 +49,7 @@ func (q *Queries) GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID)
|
|||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.TaskLabelID,
|
&i.TaskLabelID,
|
||||||
&i.TaskID,
|
&i.TaskID,
|
||||||
&i.LabelColorID,
|
&i.ProjectLabelID,
|
||||||
&i.AssignedDate,
|
&i.AssignedDate,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
const createUserAccount = `-- name: CreateUserAccount :one
|
const createUserAccount = `-- name: CreateUserAccount :one
|
||||||
INSERT INTO user_account(first_name, last_name, email, username, created_at, password_hash)
|
INSERT INTO user_account(first_name, last_name, email, username, created_at, password_hash)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
VALUES ($1, $2, $3, $4, $5, $6)
|
||||||
RETURNING user_id, created_at, first_name, last_name, email, username, password_hash
|
RETURNING user_id, created_at, first_name, last_name, email, username, password_hash, profile_bg_color
|
||||||
`
|
`
|
||||||
|
|
||||||
type CreateUserAccountParams struct {
|
type CreateUserAccountParams struct {
|
||||||
@ -43,12 +43,13 @@ func (q *Queries) CreateUserAccount(ctx context.Context, arg CreateUserAccountPa
|
|||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.PasswordHash,
|
&i.PasswordHash,
|
||||||
|
&i.ProfileBgColor,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllUserAccounts = `-- name: GetAllUserAccounts :many
|
const getAllUserAccounts = `-- name: GetAllUserAccounts :many
|
||||||
SELECT user_id, created_at, first_name, last_name, email, username, password_hash FROM user_account
|
SELECT user_id, created_at, first_name, last_name, email, username, password_hash, profile_bg_color FROM user_account
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) {
|
func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error) {
|
||||||
@ -68,6 +69,7 @@ func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
|||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.PasswordHash,
|
&i.PasswordHash,
|
||||||
|
&i.ProfileBgColor,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -83,7 +85,7 @@ func (q *Queries) GetAllUserAccounts(ctx context.Context) ([]UserAccount, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getUserAccountByID = `-- name: GetUserAccountByID :one
|
const getUserAccountByID = `-- name: GetUserAccountByID :one
|
||||||
SELECT user_id, created_at, first_name, last_name, email, username, password_hash FROM user_account WHERE user_id = $1
|
SELECT user_id, created_at, first_name, last_name, email, username, password_hash, profile_bg_color FROM user_account WHERE user_id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error) {
|
func (q *Queries) GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error) {
|
||||||
@ -97,12 +99,13 @@ func (q *Queries) GetUserAccountByID(ctx context.Context, userID uuid.UUID) (Use
|
|||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.PasswordHash,
|
&i.PasswordHash,
|
||||||
|
&i.ProfileBgColor,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserAccountByUsername = `-- name: GetUserAccountByUsername :one
|
const getUserAccountByUsername = `-- name: GetUserAccountByUsername :one
|
||||||
SELECT user_id, created_at, first_name, last_name, email, username, password_hash FROM user_account WHERE username = $1
|
SELECT user_id, created_at, first_name, last_name, email, username, password_hash, profile_bg_color FROM user_account WHERE username = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error) {
|
func (q *Queries) GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error) {
|
||||||
@ -116,6 +119,7 @@ func (q *Queries) GetUserAccountByUsername(ctx context.Context, username string)
|
|||||||
&i.Email,
|
&i.Email,
|
||||||
&i.Username,
|
&i.Username,
|
||||||
&i.PasswordHash,
|
&i.PasswordHash,
|
||||||
|
&i.ProfileBgColor,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
9
api/query/project_label.sql
Normal file
9
api/query/project_label.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- name: CreateProjectLabel :one
|
||||||
|
INSERT INTO project_label (project_id, label_color_id, created_date, name)
|
||||||
|
VALUES ($1, $2, $3, $4) RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetProjectLabelsForProject :many
|
||||||
|
SELECT * FROM project_label WHERE project_id = $1;
|
||||||
|
|
||||||
|
-- name: GetProjectLabelByID :one
|
||||||
|
SELECT * FROM project_label WHERE project_label_id = $1;
|
@ -4,3 +4,6 @@ INSERT INTO task_assigned (task_id, user_id, assigned_date)
|
|||||||
|
|
||||||
-- name: GetAssignedMembersForTask :many
|
-- name: GetAssignedMembersForTask :many
|
||||||
SELECT * FROM task_assigned WHERE task_id = $1;
|
SELECT * FROM task_assigned WHERE task_id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteTaskAssignedByID :one
|
||||||
|
DELETE FROM task_assigned WHERE task_id = $1 AND user_id = $2 RETURNING *;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-- name: CreateTaskLabelForTask :one
|
-- name: CreateTaskLabelForTask :one
|
||||||
INSERT INTO task_label (task_id, label_color_id, assigned_date)
|
INSERT INTO task_label (task_id, project_label_id, assigned_date)
|
||||||
VALUES ($1, $2, $3) RETURNING *;
|
VALUES ($1, $2, $3) RETURNING *;
|
||||||
|
|
||||||
-- name: GetTaskLabelsForTaskID :many
|
-- name: GetTaskLabelsForTaskID :many
|
||||||
|
@ -41,13 +41,27 @@ const GlobalTopNavbar: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TopNavbar
|
<TopNavbar
|
||||||
|
bgColor={data ? data.me.profileIcon.bgColor ?? '#7367F0' : '#7367F0'}
|
||||||
firstName={data ? data.me.firstName : ''}
|
firstName={data ? data.me.firstName : ''}
|
||||||
lastName={data ? data.me.lastName : ''}
|
lastName={data ? data.me.lastName : ''}
|
||||||
initials={!data ? '' : data.me.profileIcon.initials ?? ''}
|
initials={!data ? '' : data.me.profileIcon.initials ?? ''}
|
||||||
onNotificationClick={() => console.log('beep')}
|
onNotificationClick={() => console.log('beep')}
|
||||||
onProfileClick={onProfileClick}
|
onProfileClick={onProfileClick}
|
||||||
/>
|
/>
|
||||||
{menu.isOpen && <DropdownMenu onLogout={onLogout} left={menu.left} top={menu.top} />}
|
{menu.isOpen && (
|
||||||
|
<DropdownMenu
|
||||||
|
onCloseDropdown={() => {
|
||||||
|
setMenu({
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
isOpen: false,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onLogout={onLogout}
|
||||||
|
left={menu.left}
|
||||||
|
top={menu.top}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import TaskDetails from 'shared/components/TaskDetails';
|
|||||||
import PopupMenu from 'shared/components/PopupMenu';
|
import PopupMenu 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 } from 'shared/generated/graphql';
|
import { useFindTaskQuery, useAssignTaskMutation, useUnassignTaskMutation } from 'shared/generated/graphql';
|
||||||
import UserIDContext from 'App/context';
|
import UserIDContext from 'App/context';
|
||||||
|
|
||||||
type DetailsProps = {
|
type DetailsProps = {
|
||||||
@ -15,6 +15,7 @@ type DetailsProps = {
|
|||||||
onDeleteTask: (task: Task) => void;
|
onDeleteTask: (task: Task) => void;
|
||||||
onOpenAddLabelPopup: (task: Task, bounds: ElementBounds) => void;
|
onOpenAddLabelPopup: (task: Task, bounds: ElementBounds) => void;
|
||||||
availableMembers: Array<TaskUser>;
|
availableMembers: Array<TaskUser>;
|
||||||
|
refreshCache: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialMemberPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
|
const initialMemberPopupState = { taskID: '', isOpen: false, top: 0, left: 0 };
|
||||||
@ -27,13 +28,25 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
onDeleteTask,
|
onDeleteTask,
|
||||||
onOpenAddLabelPopup,
|
onOpenAddLabelPopup,
|
||||||
availableMembers,
|
availableMembers,
|
||||||
|
refreshCache,
|
||||||
}) => {
|
}) => {
|
||||||
const { userID } = useContext(UserIDContext);
|
const { userID } = useContext(UserIDContext);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState);
|
||||||
const { loading, data } = useFindTaskQuery({ variables: { taskID } });
|
const { loading, data, refetch } = useFindTaskQuery({ variables: { taskID } });
|
||||||
const [assignTask] = useAssignTaskMutation();
|
const [assignTask] = useAssignTaskMutation({
|
||||||
|
onCompleted: () => {
|
||||||
|
refetch();
|
||||||
|
refreshCache();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const [unassignTask] = useUnassignTaskMutation({
|
||||||
|
onCompleted: () => {
|
||||||
|
refetch();
|
||||||
|
refreshCache();
|
||||||
|
},
|
||||||
|
});
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div>loading</div>;
|
return <div>loading</div>;
|
||||||
}
|
}
|
||||||
@ -47,6 +60,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
profileIcon: {
|
profileIcon: {
|
||||||
url: null,
|
url: null,
|
||||||
initials: assigned.profileIcon.initials ?? null,
|
initials: assigned.profileIcon.initials ?? null,
|
||||||
|
bgColor: assigned.profileIcon.bgColor ?? null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -93,10 +107,13 @@ const Details: React.FC<DetailsProps> = ({
|
|||||||
>
|
>
|
||||||
<MemberManager
|
<MemberManager
|
||||||
availableMembers={availableMembers}
|
availableMembers={availableMembers}
|
||||||
activeMembers={[]}
|
activeMembers={taskMembers}
|
||||||
onMemberChange={(member, isActive) => {
|
onMemberChange={(member, isActive) => {
|
||||||
|
console.log(`is active ${member.userID} - ${isActive}`);
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
assignTask({ variables: { taskID: data.findTask.taskID, userID: userID ?? '' } });
|
assignTask({ variables: { taskID: data.findTask.taskID, userID: userID ?? '' } });
|
||||||
|
} else {
|
||||||
|
unassignTask({ variables: { taskID: data.findTask.taskID, userID: userID ?? '' } });
|
||||||
}
|
}
|
||||||
console.log(member, isActive);
|
console.log(member, isActive);
|
||||||
}}
|
}}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const Board = styled.div`
|
export const Board = styled.div`
|
||||||
margin-left: 36px;
|
margin-top: 12px;
|
||||||
|
margin-left: 8px;
|
||||||
`;
|
`;
|
||||||
|
@ -109,9 +109,10 @@ const Project = () => {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const { loading, data } = useFindProjectQuery({
|
const { loading, data, refetch } = useFindProjectQuery({
|
||||||
variables: { projectId },
|
variables: { projectId },
|
||||||
onCompleted: newData => {
|
onCompleted: newData => {
|
||||||
|
console.log('beep!');
|
||||||
const newListsData: BoardState = { tasks: {}, columns: {} };
|
const newListsData: BoardState = { tasks: {}, columns: {} };
|
||||||
newData.findProject.taskGroups.forEach(taskGroup => {
|
newData.findProject.taskGroups.forEach(taskGroup => {
|
||||||
newListsData.columns[taskGroup.taskGroupID] = {
|
newListsData.columns[taskGroup.taskGroupID] = {
|
||||||
@ -121,15 +122,27 @@ const Project = () => {
|
|||||||
tasks: [],
|
tasks: [],
|
||||||
};
|
};
|
||||||
taskGroup.tasks.forEach(task => {
|
taskGroup.tasks.forEach(task => {
|
||||||
|
const taskMembers = task.assigned.map(assigned => {
|
||||||
|
return {
|
||||||
|
userID: assigned.userID,
|
||||||
|
displayName: `${assigned.firstName} ${assigned.lastName}`,
|
||||||
|
profileIcon: {
|
||||||
|
url: null,
|
||||||
|
initials: assigned.profileIcon.initials ?? '',
|
||||||
|
bgColor: assigned.profileIcon.bgColor ?? '#7367F0',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
newListsData.tasks[task.taskID] = {
|
newListsData.tasks[task.taskID] = {
|
||||||
taskID: task.taskID,
|
taskID: task.taskID,
|
||||||
taskGroup: {
|
taskGroup: {
|
||||||
taskGroupID: taskGroup.taskGroupID,
|
taskGroupID: taskGroup.taskGroupID,
|
||||||
},
|
},
|
||||||
name: task.name,
|
name: task.name,
|
||||||
position: task.position,
|
|
||||||
labels: [],
|
labels: [],
|
||||||
|
position: task.position,
|
||||||
description: task.description ?? undefined,
|
description: task.description ?? undefined,
|
||||||
|
members: taskMembers,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -196,15 +209,16 @@ const Project = () => {
|
|||||||
const availableMembers = data.findProject.members.map(member => {
|
const availableMembers = data.findProject.members.map(member => {
|
||||||
return {
|
return {
|
||||||
displayName: `${member.firstName} ${member.lastName}`,
|
displayName: `${member.firstName} ${member.lastName}`,
|
||||||
profileIcon: { url: null, initials: member.profileIcon.initials ?? null },
|
profileIcon: {
|
||||||
|
url: null,
|
||||||
|
initials: member.profileIcon.initials ?? null,
|
||||||
|
bgColor: member.profileIcon.bgColor ?? null,
|
||||||
|
},
|
||||||
userID: member.userID,
|
userID: member.userID,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TitleWrapper>
|
|
||||||
<Title>{data.findProject.name}</Title>
|
|
||||||
</TitleWrapper>
|
|
||||||
<KanbanBoard
|
<KanbanBoard
|
||||||
listsData={listsData}
|
listsData={listsData}
|
||||||
onCardDrop={onCardDrop}
|
onCardDrop={onCardDrop}
|
||||||
@ -253,6 +267,10 @@ const Project = () => {
|
|||||||
path={`${match.path}/c/:taskID`}
|
path={`${match.path}/c/:taskID`}
|
||||||
render={(routeProps: RouteComponentProps<TaskRouteProps>) => (
|
render={(routeProps: RouteComponentProps<TaskRouteProps>) => (
|
||||||
<Details
|
<Details
|
||||||
|
refreshCache={() => {
|
||||||
|
console.log('beep 2!');
|
||||||
|
refetch();
|
||||||
|
}}
|
||||||
availableMembers={availableMembers}
|
availableMembers={availableMembers}
|
||||||
projectURL={match.url}
|
projectURL={match.url}
|
||||||
taskID={routeProps.match.params.taskID}
|
taskID={routeProps.match.params.taskID}
|
||||||
|
1
web/src/citadel.d.ts
vendored
1
web/src/citadel.d.ts
vendored
@ -37,6 +37,7 @@ type InnerTaskGroup = {
|
|||||||
type ProfileIcon = {
|
type ProfileIcon = {
|
||||||
url: string | null;
|
url: string | null;
|
||||||
initials: string | null;
|
initials: string | null;
|
||||||
|
bgColor: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TaskUser = {
|
type TaskUser = {
|
||||||
|
@ -98,9 +98,6 @@ export const ListCardOperation = styled.span`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: ${props => mixin.darken('#262c49', 0.15)};
|
|
||||||
background-clip: padding-box;
|
|
||||||
background-origin: padding-box;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
@ -108,6 +105,10 @@ export const ListCardOperation = styled.span`
|
|||||||
right: 2px;
|
right: 2px;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: ${props => mixin.darken('#262c49', 0.45)};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CardTitle = styled.span`
|
export const CardTitle = styled.span`
|
||||||
@ -120,3 +121,34 @@ export const CardTitle = styled.span`
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const CardMembers = styled.div`
|
||||||
|
float: right;
|
||||||
|
margin: 0 -2px 0 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CardMember = styled.div<{ bgColor: string }>`
|
||||||
|
height: 28px;
|
||||||
|
width: 28px;
|
||||||
|
float: right;
|
||||||
|
margin: 0 0 4px 4px;
|
||||||
|
|
||||||
|
background-color: ${props => props.bgColor};
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 25em;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none;
|
||||||
|
z-index: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CardMemberInitials = styled.div`
|
||||||
|
height: 28px;
|
||||||
|
width: 28px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
`;
|
||||||
|
@ -18,6 +18,9 @@ import {
|
|||||||
ListCardLabel,
|
ListCardLabel,
|
||||||
ListCardOperation,
|
ListCardOperation,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
|
CardMembers,
|
||||||
|
CardMember,
|
||||||
|
CardMemberInitials,
|
||||||
} from './Styles';
|
} from './Styles';
|
||||||
|
|
||||||
type DueDate = {
|
type DueDate = {
|
||||||
@ -42,6 +45,7 @@ type Props = {
|
|||||||
watched?: boolean;
|
watched?: boolean;
|
||||||
labels?: Label[];
|
labels?: Label[];
|
||||||
wrapperProps?: any;
|
wrapperProps?: any;
|
||||||
|
members?: Array<TaskUser> | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Card = React.forwardRef(
|
const Card = React.forwardRef(
|
||||||
@ -58,6 +62,7 @@ const Card = React.forwardRef(
|
|||||||
description,
|
description,
|
||||||
checklists,
|
checklists,
|
||||||
watched,
|
watched,
|
||||||
|
members,
|
||||||
}: Props,
|
}: Props,
|
||||||
$cardRef: any,
|
$cardRef: any,
|
||||||
) => {
|
) => {
|
||||||
@ -95,9 +100,11 @@ const Card = React.forwardRef(
|
|||||||
{...wrapperProps}
|
{...wrapperProps}
|
||||||
>
|
>
|
||||||
<ListCardInnerContainer ref={$innerCardRef}>
|
<ListCardInnerContainer ref={$innerCardRef}>
|
||||||
<ListCardOperation>
|
{isActive && (
|
||||||
<FontAwesomeIcon onClick={onOperationClick} color="#c2c6dc" size="xs" icon={faPencilAlt} />
|
<ListCardOperation>
|
||||||
</ListCardOperation>
|
<FontAwesomeIcon onClick={onOperationClick} color="#c2c6dc" size="xs" icon={faPencilAlt} />
|
||||||
|
</ListCardOperation>
|
||||||
|
)}
|
||||||
<ListCardDetails>
|
<ListCardDetails>
|
||||||
<ListCardLabels>
|
<ListCardLabels>
|
||||||
{labels &&
|
{labels &&
|
||||||
@ -132,6 +139,14 @@ const Card = React.forwardRef(
|
|||||||
</ListCardBadge>
|
</ListCardBadge>
|
||||||
)}
|
)}
|
||||||
</ListCardBadges>
|
</ListCardBadges>
|
||||||
|
<CardMembers>
|
||||||
|
{members &&
|
||||||
|
members.map(member => (
|
||||||
|
<CardMember key={member.userID} bgColor={member.profileIcon.bgColor ?? '#7367F0'}>
|
||||||
|
<CardMemberInitials>{member.profileIcon.initials}</CardMemberInitials>
|
||||||
|
</CardMember>
|
||||||
|
))}
|
||||||
|
</CardMembers>
|
||||||
</ListCardDetails>
|
</ListCardDetails>
|
||||||
</ListCardInnerContainer>
|
</ListCardInnerContainer>
|
||||||
</ListCardContainer>
|
</ListCardContainer>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React, { createRef, useState } from 'react';
|
import React, { createRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import DropdownMenu from '.';
|
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import DropdownMenu from '.';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: DropdownMenu,
|
component: DropdownMenu,
|
||||||
@ -50,7 +49,16 @@ export const Default = () => {
|
|||||||
Click me
|
Click me
|
||||||
</Button>
|
</Button>
|
||||||
</Container>
|
</Container>
|
||||||
{menu.isOpen && <DropdownMenu onLogout={action('on logout')} left={menu.left} top={menu.top} />}
|
{menu.isOpen && (
|
||||||
|
<DropdownMenu
|
||||||
|
onCloseDropdown={() => {
|
||||||
|
setMenu({ top: 0, left: 0, isOpen: false });
|
||||||
|
}}
|
||||||
|
onLogout={action('on logout')}
|
||||||
|
left={menu.left}
|
||||||
|
top={menu.top}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useRef } from 'react';
|
||||||
|
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||||
import { Exit, User } from 'shared/icons';
|
import { Exit, User } from 'shared/icons';
|
||||||
import { Separator, Container, WrapperDiamond, Wrapper, ActionsList, ActionItem, ActionTitle } from './Styles';
|
import { Separator, Container, WrapperDiamond, Wrapper, ActionsList, ActionItem, ActionTitle } from './Styles';
|
||||||
|
|
||||||
@ -7,11 +7,14 @@ type DropdownMenuProps = {
|
|||||||
left: number;
|
left: number;
|
||||||
top: number;
|
top: number;
|
||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
|
onCloseDropdown: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DropdownMenu: React.FC<DropdownMenuProps> = ({ left, top, onLogout }) => {
|
const DropdownMenu: React.FC<DropdownMenuProps> = ({ left, top, onLogout, onCloseDropdown }) => {
|
||||||
|
const $containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
useOnOutsideClick($containerRef, true, onCloseDropdown, null);
|
||||||
return (
|
return (
|
||||||
<Container left={left} top={top}>
|
<Container ref={$containerRef} left={left} top={top}>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<ActionItem>
|
<ActionItem>
|
||||||
<User size={16} color="#c2c6dc" />
|
<User size={16} color="#c2c6dc" />
|
||||||
|
@ -23,7 +23,9 @@ export const Default = () => {
|
|||||||
position: 1,
|
position: 1,
|
||||||
labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
|
labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
|
||||||
description: 'hello!',
|
description: 'hello!',
|
||||||
members: [{ userID: '1', profileIcon: { url: null, initials: null }, displayName: 'Jordan Knott' }],
|
members: [
|
||||||
|
{ userID: '1', profileIcon: { url: null, initials: null, bgColor: null }, displayName: 'Jordan Knott' },
|
||||||
|
],
|
||||||
}}
|
}}
|
||||||
onCancel={action('cancel')}
|
onCancel={action('cancel')}
|
||||||
onDueDateChange={action('due date change')}
|
onDueDateChange={action('due date change')}
|
||||||
|
@ -159,6 +159,7 @@ const Lists: React.FC<Props> = ({
|
|||||||
description=""
|
description=""
|
||||||
title={task.name}
|
title={task.name}
|
||||||
labels={task.labels}
|
labels={task.labels}
|
||||||
|
members={task.members}
|
||||||
onClick={() => onCardClick(task)}
|
onClick={() => onCardClick(task)}
|
||||||
onContextMenu={onQuickEditorOpen}
|
onContextMenu={onQuickEditorOpen}
|
||||||
/>
|
/>
|
||||||
|
50
web/src/shared/components/MiniProfile/Styles.ts
Normal file
50
web/src/shared/components/MiniProfile/Styles.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Profile = styled.div`
|
||||||
|
margin: 8px 0;
|
||||||
|
min-height: 56px;
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ProfileIcon = styled.div<{ bgColor: string }>`
|
||||||
|
float: left;
|
||||||
|
margin: 2px;
|
||||||
|
background-color: ${props => props.bgColor};
|
||||||
|
border-radius: 25em;
|
||||||
|
display: block;
|
||||||
|
height: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
width: 50px;
|
||||||
|
z-index: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ProfileInfo = styled.div`
|
||||||
|
margin: 0 0 0 64px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const InfoTitle = styled.h3`
|
||||||
|
margin: 0 40px 0 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #172b4d;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const InfoUsername = styled.p`
|
||||||
|
color: #5e6c84;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const InfoBio = styled.p`
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #5e6c84;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
`;
|
26
web/src/shared/components/MiniProfile/index.tsx
Normal file
26
web/src/shared/components/MiniProfile/index.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Profile, ProfileIcon, ProfileInfo, InfoTitle, InfoUsername, InfoBio } from './Styles';
|
||||||
|
|
||||||
|
type MiniProfileProps = {
|
||||||
|
displayName: string;
|
||||||
|
username: string;
|
||||||
|
bio: string;
|
||||||
|
profileIcon: ProfileIcon;
|
||||||
|
};
|
||||||
|
const MiniProfile: React.FC<MiniProfileProps> = ({ displayName, username, bio, profileIcon }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Profile>
|
||||||
|
<ProfileIcon bgColor={profileIcon.bgColor ?? ''}>{profileIcon.initials}</ProfileIcon>
|
||||||
|
<ProfileInfo>
|
||||||
|
<InfoTitle>{displayName}</InfoTitle>
|
||||||
|
<InfoUsername>{username}</InfoUsername>
|
||||||
|
<InfoBio>{bio}</InfoBio>
|
||||||
|
</ProfileInfo>
|
||||||
|
</Profile>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MiniProfile;
|
@ -17,13 +17,12 @@ export const ClickableOverlay = styled.div`
|
|||||||
background: rgba(0, 0, 0, 0.4);
|
background: rgba(0, 0, 0, 0.4);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
|
||||||
padding: 50px;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const StyledModal = styled.div<{ width: number }>`
|
export const StyledModal = styled.div<{ width: number }>`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
margin: 48px 0 80px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #262c49;
|
background: #262c49;
|
||||||
max-width: ${props => props.width}px;
|
max-width: ${props => props.width}px;
|
||||||
|
@ -1,32 +1,14 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
|
|
||||||
export const LogoWrapper = styled.div`
|
export const Logo = styled.div``;
|
||||||
margin: 20px 0px 20px;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 42px;
|
|
||||||
line-height: 42px;
|
|
||||||
padding-left: 64px;
|
|
||||||
color: rgb(222, 235, 255);
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
transition: color 0.1s ease 0s;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Logo = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
left: 19px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const LogoTitle = styled.div`
|
export const LogoTitle = styled.div`
|
||||||
position: relative;
|
position: absolute;
|
||||||
right: 12px;
|
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: right 0.1s ease 0s, visibility, opacity, transform 0.25s ease;
|
transition: visibility, opacity, transform 0.25s ease;
|
||||||
color: #7367f0;
|
color: #7367f0;
|
||||||
`;
|
`;
|
||||||
export const ActionContainer = styled.div`
|
export const ActionContainer = styled.div`
|
||||||
@ -54,6 +36,10 @@ export const IconWrapper = styled.div`
|
|||||||
export const ActionButtonContainer = styled.div`
|
export const ActionButtonContainer = styled.div`
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
& > a:first-child > div {
|
||||||
|
padding-top: 48px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ActionButtonWrapper = styled.div<{ active?: boolean }>`
|
export const ActionButtonWrapper = styled.div<{ active?: boolean }>`
|
||||||
@ -65,7 +51,7 @@ export const ActionButtonWrapper = styled.div<{ active?: boolean }>`
|
|||||||
`}
|
`}
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 10px 15px;
|
padding: 24px 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
&:hover ${ActionButtonTitle} {
|
&:hover ${ActionButtonTitle} {
|
||||||
@ -76,6 +62,20 @@ export const ActionButtonWrapper = styled.div<{ active?: boolean }>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const LogoWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
color: rgb(222, 235, 255);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.1s ease 0s, border 0.1s ease 0s;
|
||||||
|
border-bottom: 1px solid rgba(65, 69, 97, 0.65);
|
||||||
|
`;
|
||||||
|
|
||||||
export const Container = styled.aside`
|
export const Container = styled.aside`
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -87,13 +87,15 @@ export const Container = styled.aside`
|
|||||||
transform: translateZ(0px);
|
transform: translateZ(0px);
|
||||||
background: #10163a;
|
background: #10163a;
|
||||||
transition: all 0.1s ease 0s;
|
transition: all 0.1s ease 0s;
|
||||||
|
border-right: 1px solid rgba(65, 69, 97, 0.65);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
width: 260px;
|
width: 260px;
|
||||||
box-shadow: rgba(0, 0, 0, 0.6) 0px 0px 50px 0px;
|
box-shadow: rgba(0, 0, 0, 0.6) 0px 0px 50px 0px;
|
||||||
|
border-right: 1px solid rgba(65, 69, 97, 0);
|
||||||
}
|
}
|
||||||
&:hover ${LogoTitle} {
|
&:hover ${LogoTitle} {
|
||||||
right: 0px;
|
bottom: -12px;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
@ -102,4 +104,8 @@ export const Container = styled.aside`
|
|||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover ${LogoWrapper} {
|
||||||
|
border-bottom: 1px solid rgba(65, 69, 97, 0);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -35,9 +35,7 @@ export const ButtonContainer: React.FC = ({ children }) => (
|
|||||||
export const PrimaryLogo = () => {
|
export const PrimaryLogo = () => {
|
||||||
return (
|
return (
|
||||||
<LogoWrapper>
|
<LogoWrapper>
|
||||||
<Logo>
|
<Citadel size={42} />
|
||||||
<Citadel size={42} />
|
|
||||||
</Logo>
|
|
||||||
<LogoTitle>Citadel</LogoTitle>
|
<LogoTitle>Citadel</LogoTitle>
|
||||||
</LogoWrapper>
|
</LogoWrapper>
|
||||||
);
|
);
|
||||||
|
@ -6,6 +6,7 @@ import LabelEditor from 'shared/components/PopupMenu/LabelEditor';
|
|||||||
import ListActions from 'shared/components/ListActions';
|
import ListActions from 'shared/components/ListActions';
|
||||||
import MemberManager from 'shared/components/MemberManager';
|
import MemberManager from 'shared/components/MemberManager';
|
||||||
import DueDateManager from 'shared/components/DueDateManager';
|
import DueDateManager from 'shared/components/DueDateManager';
|
||||||
|
import MiniProfile from 'shared/components/MiniProfile';
|
||||||
|
|
||||||
import PopupMenu from '.';
|
import PopupMenu from '.';
|
||||||
import NormalizeStyles from 'App/NormalizeStyles';
|
import NormalizeStyles from 'App/NormalizeStyles';
|
||||||
@ -115,7 +116,7 @@ export const MemberManagerPopup = () => {
|
|||||||
<PopupMenu title="Members" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}>
|
<PopupMenu title="Members" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}>
|
||||||
<MemberManager
|
<MemberManager
|
||||||
availableMembers={[
|
availableMembers={[
|
||||||
{ userID: '1', displayName: 'Jordan Knott', profileIcon: { url: null, initials: null } },
|
{ userID: '1', displayName: 'Jordan Knott', profileIcon: { bgColor: null, url: null, initials: null } },
|
||||||
]}
|
]}
|
||||||
activeMembers={[]}
|
activeMembers={[]}
|
||||||
onMemberChange={action('member change')}
|
onMemberChange={action('member change')}
|
||||||
@ -158,7 +159,9 @@ export const DueDateManagerPopup = () => {
|
|||||||
position: 1,
|
position: 1,
|
||||||
labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
|
labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
|
||||||
description: 'hello!',
|
description: 'hello!',
|
||||||
members: [{ userID: '1', profileIcon: { url: null, initials: null }, displayName: 'Jordan Knott' }],
|
members: [
|
||||||
|
{ userID: '1', profileIcon: { bgColor: null, url: null, initials: null }, displayName: 'Jordan Knott' },
|
||||||
|
],
|
||||||
}}
|
}}
|
||||||
onCancel={action('cancel')}
|
onCancel={action('cancel')}
|
||||||
onDueDateChange={action('due date change')}
|
onDueDateChange={action('due date change')}
|
||||||
@ -189,3 +192,47 @@ export const DueDateManagerPopup = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MiniProfilePopup = () => {
|
||||||
|
const $buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
const [popupData, setPopupData] = useState(initalState);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NormalizeStyles />
|
||||||
|
<BaseStyles />
|
||||||
|
{popupData.isOpen && (
|
||||||
|
<PopupMenu title="Due Date" top={popupData.top} onClose={() => setPopupData(initalState)} left={popupData.left}>
|
||||||
|
<MiniProfile
|
||||||
|
displayName="Jordan Knott"
|
||||||
|
profileIcon={{ url: null, bgColor: '#000', initials: 'JK' }}
|
||||||
|
username="@jordanthedev"
|
||||||
|
bio="Stuff and things"
|
||||||
|
/>
|
||||||
|
</PopupMenu>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
width: '60px',
|
||||||
|
textAlign: 'center',
|
||||||
|
margin: '25px auto',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: '#fff',
|
||||||
|
background: '#f00',
|
||||||
|
}}
|
||||||
|
ref={$buttonRef}
|
||||||
|
onClick={() => {
|
||||||
|
if ($buttonRef && $buttonRef.current) {
|
||||||
|
const pos = $buttonRef.current.getBoundingClientRect();
|
||||||
|
setPopupData({
|
||||||
|
isOpen: true,
|
||||||
|
left: pos.left,
|
||||||
|
top: pos.top + pos.height + 10,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Open
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -281,3 +281,9 @@ export const NoDueDateLabel = styled.span`
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const UnassignedLabel = styled.div`
|
||||||
|
color: rgb(137, 147, 164);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
@ -35,7 +35,13 @@ export const Default = () => {
|
|||||||
position: 1,
|
position: 1,
|
||||||
labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
|
labels: [{ labelId: 'soft-skills', color: '#fff', active: true, name: 'Soft Skills' }],
|
||||||
description,
|
description,
|
||||||
members: [{ userID: '1', profileIcon: { url: null, initials: null }, displayName: 'Jordan Knott' }],
|
members: [
|
||||||
|
{
|
||||||
|
userID: '1',
|
||||||
|
profileIcon: { bgColor: null, url: null, initials: null },
|
||||||
|
displayName: 'Jordan Knott',
|
||||||
|
},
|
||||||
|
],
|
||||||
}}
|
}}
|
||||||
onTaskNameChange={action('task name change')}
|
onTaskNameChange={action('task name change')}
|
||||||
onTaskDescriptionChange={(_task, desc) => setDescription(desc)}
|
onTaskDescriptionChange={(_task, desc) => setDescription(desc)}
|
||||||
|
@ -4,6 +4,7 @@ import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
NoDueDateLabel,
|
NoDueDateLabel,
|
||||||
|
UnassignedLabel,
|
||||||
TaskDetailsAddMember,
|
TaskDetailsAddMember,
|
||||||
TaskGroupLabel,
|
TaskGroupLabel,
|
||||||
TaskGroupLabelName,
|
TaskGroupLabelName,
|
||||||
@ -126,11 +127,16 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
onTaskNameChange(task, taskName);
|
onTaskNameChange(task, taskName);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const $unassignedRef = useRef<HTMLDivElement>(null);
|
||||||
const $addMemberRef = useRef<HTMLDivElement>(null);
|
const $addMemberRef = useRef<HTMLDivElement>(null);
|
||||||
|
const onUnassignedClick = () => {
|
||||||
|
const bounds = convertDivElementRefToBounds($unassignedRef);
|
||||||
|
if (bounds) {
|
||||||
|
onOpenAddMemberPopup(task, bounds);
|
||||||
|
}
|
||||||
|
};
|
||||||
const onAddMember = () => {
|
const onAddMember = () => {
|
||||||
console.log('beep!');
|
|
||||||
const bounds = convertDivElementRefToBounds($addMemberRef);
|
const bounds = convertDivElementRefToBounds($addMemberRef);
|
||||||
console.log(bounds);
|
|
||||||
if (bounds) {
|
if (bounds) {
|
||||||
onOpenAddMemberPopup(task, bounds);
|
onOpenAddMemberPopup(task, bounds);
|
||||||
}
|
}
|
||||||
@ -191,20 +197,28 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
|
|||||||
<TaskDetailsSidebar>
|
<TaskDetailsSidebar>
|
||||||
<TaskDetailSectionTitle>Assignees</TaskDetailSectionTitle>
|
<TaskDetailSectionTitle>Assignees</TaskDetailSectionTitle>
|
||||||
<TaskDetailAssignees>
|
<TaskDetailAssignees>
|
||||||
{task.members &&
|
{task.members && task.members.length === 0 ? (
|
||||||
task.members.map(member => {
|
<UnassignedLabel ref={$unassignedRef} onClick={onUnassignedClick}>
|
||||||
console.log(member);
|
Unassigned
|
||||||
return (
|
</UnassignedLabel>
|
||||||
<TaskDetailAssignee key={member.userID}>
|
) : (
|
||||||
<ProfileIcon>{member.profileIcon.initials ?? ''}</ProfileIcon>
|
<>
|
||||||
</TaskDetailAssignee>
|
{task.members &&
|
||||||
);
|
task.members.map(member => {
|
||||||
})}
|
console.log(member);
|
||||||
<TaskDetailsAddMember ref={$addMemberRef} onClick={onAddMember}>
|
return (
|
||||||
<TaskDetailsAddMemberIcon>
|
<TaskDetailAssignee key={member.userID}>
|
||||||
<Plus size={16} color="#c2c6dc" />
|
<ProfileIcon>{member.profileIcon.initials ?? ''}</ProfileIcon>
|
||||||
</TaskDetailsAddMemberIcon>
|
</TaskDetailAssignee>
|
||||||
</TaskDetailsAddMember>
|
);
|
||||||
|
})}
|
||||||
|
<TaskDetailsAddMember ref={$addMemberRef} onClick={onAddMember}>
|
||||||
|
<TaskDetailsAddMemberIcon>
|
||||||
|
<Plus size={16} color="#c2c6dc" />
|
||||||
|
</TaskDetailsAddMemberIcon>
|
||||||
|
</TaskDetailsAddMember>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</TaskDetailAssignees>
|
</TaskDetailAssignees>
|
||||||
<TaskDetailSectionTitle>Labels</TaskDetailSectionTitle>
|
<TaskDetailSectionTitle>Labels</TaskDetailSectionTitle>
|
||||||
<TaskDetailLabels>
|
<TaskDetailLabels>
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const NavbarWrapper = styled.div`
|
export const NavbarWrapper = styled.div`
|
||||||
height: 103px;
|
|
||||||
padding: 1.3rem 2.2rem 2.2rem;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const NavbarHeader = styled.header`
|
export const NavbarHeader = styled.header`
|
||||||
border-radius: 0.5rem;
|
height: 80px;
|
||||||
padding: 0.8rem 1rem;
|
padding: 0 1.75rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background: rgb(16, 22, 58);
|
background: rgb(16, 22, 58);
|
||||||
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.05);
|
||||||
|
border-bottom: 1px solid rgba(65, 69, 97, 0.65);
|
||||||
`;
|
`;
|
||||||
export const Breadcrumbs = styled.div`
|
export const Breadcrumbs = styled.div`
|
||||||
color: rgb(94, 108, 132);
|
color: rgb(94, 108, 132);
|
||||||
@ -26,7 +25,14 @@ export const BreadcrumpSeparator = styled.span`
|
|||||||
margin: 0px 10px;
|
margin: 0px 10px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ProjectActions = styled.div``;
|
export const ProjectActions = styled.div`
|
||||||
|
align-items: flex-start;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 1px;
|
||||||
|
`;
|
||||||
|
|
||||||
export const GlobalActions = styled.div`
|
export const GlobalActions = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -55,7 +61,7 @@ export const ProfileNameSecondary = styled.small`
|
|||||||
color: #c2c6dc;
|
color: #c2c6dc;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ProfileIcon = styled.div`
|
export const ProfileIcon = styled.div<{ bgColor: string }>`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@ -65,6 +71,48 @@ export const ProfileIcon = styled.div`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
background: rgb(115, 103, 240);
|
background: ${props => props.bgColor};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ProjectMeta = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 51px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ProjectTabs = styled.div`
|
||||||
|
align-items: flex-end;
|
||||||
|
align-self: stretch;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
justify-content: flex-start;
|
||||||
|
max-width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ProjectTab = styled.span`
|
||||||
|
font-size: 80%;
|
||||||
|
color: #c2c6dc;
|
||||||
|
font-size: 15px;
|
||||||
|
cursor: default;
|
||||||
|
display: flex;
|
||||||
|
line-height: normal;
|
||||||
|
min-width: 1px;
|
||||||
|
transition-duration: 0.2s;
|
||||||
|
transition-property: box-shadow, color;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex: 0 1 auto;
|
||||||
|
|
||||||
|
padding-bottom: 12px;
|
||||||
|
|
||||||
|
box-shadow: inset 0 -2px #d85dd8;
|
||||||
|
color: #d85dd8;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ProjectName = styled.h1`
|
||||||
|
color: #c2c6dc;
|
||||||
|
margin-top: 9px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
`;
|
||||||
|
@ -38,13 +38,23 @@ export const Default = () => {
|
|||||||
<NormalizeStyles />
|
<NormalizeStyles />
|
||||||
<BaseStyles />
|
<BaseStyles />
|
||||||
<TopNavbar
|
<TopNavbar
|
||||||
|
bgColor="#7367F0"
|
||||||
firstName="Jordan"
|
firstName="Jordan"
|
||||||
lastName="Knott"
|
lastName="Knott"
|
||||||
initials="JK"
|
initials="JK"
|
||||||
onNotificationClick={action('notifications click')}
|
onNotificationClick={action('notifications click')}
|
||||||
onProfileClick={onClick}
|
onProfileClick={onClick}
|
||||||
/>
|
/>
|
||||||
{menu.isOpen && <DropdownMenu onLogout={action('on logout')} left={menu.left} top={menu.top} />}
|
{menu.isOpen && (
|
||||||
|
<DropdownMenu
|
||||||
|
onCloseDropdown={() => {
|
||||||
|
setMenu({ left: 0, top: 0, isOpen: false });
|
||||||
|
}}
|
||||||
|
onLogout={action('on logout')}
|
||||||
|
left={menu.left}
|
||||||
|
top={menu.top}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,10 @@ import {
|
|||||||
NotificationContainer,
|
NotificationContainer,
|
||||||
GlobalActions,
|
GlobalActions,
|
||||||
ProjectActions,
|
ProjectActions,
|
||||||
|
ProjectMeta,
|
||||||
|
ProjectName,
|
||||||
|
ProjectTabs,
|
||||||
|
ProjectTab,
|
||||||
NavbarWrapper,
|
NavbarWrapper,
|
||||||
NavbarHeader,
|
NavbarHeader,
|
||||||
Breadcrumbs,
|
Breadcrumbs,
|
||||||
@ -19,11 +23,19 @@ import {
|
|||||||
type NavBarProps = {
|
type NavBarProps = {
|
||||||
onProfileClick: (bottom: number, right: number) => void;
|
onProfileClick: (bottom: number, right: number) => void;
|
||||||
onNotificationClick: () => void;
|
onNotificationClick: () => void;
|
||||||
|
bgColor: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
initials: string;
|
initials: string;
|
||||||
};
|
};
|
||||||
const NavBar: React.FC<NavBarProps> = ({ onProfileClick, onNotificationClick, firstName, lastName, initials }) => {
|
const NavBar: React.FC<NavBarProps> = ({
|
||||||
|
onProfileClick,
|
||||||
|
onNotificationClick,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
initials,
|
||||||
|
bgColor,
|
||||||
|
}) => {
|
||||||
const $profileRef: any = useRef(null);
|
const $profileRef: any = useRef(null);
|
||||||
const handleProfileClick = () => {
|
const handleProfileClick = () => {
|
||||||
console.log('click');
|
console.log('click');
|
||||||
@ -34,13 +46,12 @@ const NavBar: React.FC<NavBarProps> = ({ onProfileClick, onNotificationClick, fi
|
|||||||
<NavbarWrapper>
|
<NavbarWrapper>
|
||||||
<NavbarHeader>
|
<NavbarHeader>
|
||||||
<ProjectActions>
|
<ProjectActions>
|
||||||
<Breadcrumbs>
|
<ProjectMeta>
|
||||||
Projects
|
<ProjectName>Production Team</ProjectName>
|
||||||
<BreadcrumpSeparator>/</BreadcrumpSeparator>
|
</ProjectMeta>
|
||||||
project name
|
<ProjectTabs>
|
||||||
<BreadcrumpSeparator>/</BreadcrumpSeparator>
|
<ProjectTab>Board</ProjectTab>
|
||||||
Board
|
</ProjectTabs>
|
||||||
</Breadcrumbs>
|
|
||||||
</ProjectActions>
|
</ProjectActions>
|
||||||
<GlobalActions>
|
<GlobalActions>
|
||||||
<NotificationContainer onClick={onNotificationClick}>
|
<NotificationContainer onClick={onNotificationClick}>
|
||||||
@ -53,7 +64,7 @@ const NavBar: React.FC<NavBarProps> = ({ onProfileClick, onNotificationClick, fi
|
|||||||
</ProfileNamePrimary>
|
</ProfileNamePrimary>
|
||||||
<ProfileNameSecondary>Manager</ProfileNameSecondary>
|
<ProfileNameSecondary>Manager</ProfileNameSecondary>
|
||||||
</ProfileNameWrapper>
|
</ProfileNameWrapper>
|
||||||
<ProfileIcon ref={$profileRef} onClick={handleProfileClick}>
|
<ProfileIcon ref={$profileRef} onClick={handleProfileClick} bgColor={bgColor}>
|
||||||
{initials}
|
{initials}
|
||||||
</ProfileIcon>
|
</ProfileIcon>
|
||||||
</ProfileContainer>
|
</ProfileContainer>
|
||||||
|
@ -15,17 +15,28 @@ export type Scalars = {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export type ProjectLabel = {
|
||||||
|
__typename?: 'ProjectLabel';
|
||||||
|
projectLabelID: Scalars['ID'];
|
||||||
|
createdDate: Scalars['Time'];
|
||||||
|
colorHex: Scalars['String'];
|
||||||
|
name?: Maybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type TaskLabel = {
|
export type TaskLabel = {
|
||||||
__typename?: 'TaskLabel';
|
__typename?: 'TaskLabel';
|
||||||
taskLabelID: Scalars['ID'];
|
taskLabelID: Scalars['ID'];
|
||||||
labelColorID: Scalars['UUID'];
|
projectLabelID: Scalars['UUID'];
|
||||||
|
assignedDate: Scalars['Time'];
|
||||||
colorHex: Scalars['String'];
|
colorHex: Scalars['String'];
|
||||||
|
name?: Maybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProfileIcon = {
|
export type ProfileIcon = {
|
||||||
__typename?: 'ProfileIcon';
|
__typename?: 'ProfileIcon';
|
||||||
url?: Maybe<Scalars['String']>;
|
url?: Maybe<Scalars['String']>;
|
||||||
initials?: Maybe<Scalars['String']>;
|
initials?: Maybe<Scalars['String']>;
|
||||||
|
bgColor?: Maybe<Scalars['String']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProjectMember = {
|
export type ProjectMember = {
|
||||||
@ -71,6 +82,7 @@ export type Project = {
|
|||||||
owner: ProjectMember;
|
owner: ProjectMember;
|
||||||
taskGroups: Array<TaskGroup>;
|
taskGroups: Array<TaskGroup>;
|
||||||
members: Array<ProjectMember>;
|
members: Array<ProjectMember>;
|
||||||
|
labels: Array<ProjectLabel>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TaskGroup = {
|
export type TaskGroup = {
|
||||||
@ -222,6 +234,11 @@ export type AssignTaskInput = {
|
|||||||
userID: Scalars['UUID'];
|
userID: Scalars['UUID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UnassignTaskInput = {
|
||||||
|
taskID: Scalars['UUID'];
|
||||||
|
userID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
export type UpdateTaskDescriptionInput = {
|
export type UpdateTaskDescriptionInput = {
|
||||||
taskID: Scalars['UUID'];
|
taskID: Scalars['UUID'];
|
||||||
description: Scalars['String'];
|
description: Scalars['String'];
|
||||||
@ -237,12 +254,19 @@ export type RemoveTaskLabelInput = {
|
|||||||
taskLabelID: Scalars['UUID'];
|
taskLabelID: Scalars['UUID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NewProjectLabel = {
|
||||||
|
projectID: Scalars['UUID'];
|
||||||
|
labelColorID: Scalars['UUID'];
|
||||||
|
name?: Maybe<Scalars['String']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
createRefreshToken: RefreshToken;
|
createRefreshToken: RefreshToken;
|
||||||
createUserAccount: UserAccount;
|
createUserAccount: UserAccount;
|
||||||
createTeam: Team;
|
createTeam: Team;
|
||||||
createProject: Project;
|
createProject: Project;
|
||||||
|
createProjectLabel: ProjectLabel;
|
||||||
createTaskGroup: TaskGroup;
|
createTaskGroup: TaskGroup;
|
||||||
updateTaskGroupLocation: TaskGroup;
|
updateTaskGroupLocation: TaskGroup;
|
||||||
deleteTaskGroup: DeleteTaskGroupPayload;
|
deleteTaskGroup: DeleteTaskGroupPayload;
|
||||||
@ -254,6 +278,7 @@ export type Mutation = {
|
|||||||
updateTaskName: Task;
|
updateTaskName: Task;
|
||||||
deleteTask: DeleteTaskPayload;
|
deleteTask: DeleteTaskPayload;
|
||||||
assignTask: Task;
|
assignTask: Task;
|
||||||
|
unassignTask: Task;
|
||||||
logoutUser: Scalars['Boolean'];
|
logoutUser: Scalars['Boolean'];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -278,6 +303,11 @@ export type MutationCreateProjectArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationCreateProjectLabelArgs = {
|
||||||
|
input: NewProjectLabel;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateTaskGroupArgs = {
|
export type MutationCreateTaskGroupArgs = {
|
||||||
input: NewTaskGroup;
|
input: NewTaskGroup;
|
||||||
};
|
};
|
||||||
@ -333,6 +363,11 @@ export type MutationAssignTaskArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationUnassignTaskArgs = {
|
||||||
|
input?: Maybe<UnassignTaskInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationLogoutUserArgs = {
|
export type MutationLogoutUserArgs = {
|
||||||
input: LogoutUser;
|
input: LogoutUser;
|
||||||
};
|
};
|
||||||
@ -438,7 +473,7 @@ export type FindProjectQuery = (
|
|||||||
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||||
& { profileIcon: (
|
& { profileIcon: (
|
||||||
{ __typename?: 'ProfileIcon' }
|
{ __typename?: 'ProfileIcon' }
|
||||||
& Pick<ProfileIcon, 'url' | 'initials'>
|
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||||
) }
|
) }
|
||||||
)>, taskGroups: Array<(
|
)>, taskGroups: Array<(
|
||||||
{ __typename?: 'TaskGroup' }
|
{ __typename?: 'TaskGroup' }
|
||||||
@ -446,6 +481,14 @@ export type FindProjectQuery = (
|
|||||||
& { tasks: Array<(
|
& { tasks: Array<(
|
||||||
{ __typename?: 'Task' }
|
{ __typename?: 'Task' }
|
||||||
& Pick<Task, 'taskID' | 'name' | 'position' | 'description'>
|
& Pick<Task, 'taskID' | 'name' | 'position' | 'description'>
|
||||||
|
& { assigned: Array<(
|
||||||
|
{ __typename?: 'ProjectMember' }
|
||||||
|
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||||
|
& { profileIcon: (
|
||||||
|
{ __typename?: 'ProfileIcon' }
|
||||||
|
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||||
|
) }
|
||||||
|
)> }
|
||||||
)> }
|
)> }
|
||||||
)> }
|
)> }
|
||||||
) }
|
) }
|
||||||
@ -469,7 +512,7 @@ export type FindTaskQuery = (
|
|||||||
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||||
& { profileIcon: (
|
& { profileIcon: (
|
||||||
{ __typename?: 'ProfileIcon' }
|
{ __typename?: 'ProfileIcon' }
|
||||||
& Pick<ProfileIcon, 'url' | 'initials'>
|
& Pick<ProfileIcon, 'url' | 'initials' | 'bgColor'>
|
||||||
) }
|
) }
|
||||||
)> }
|
)> }
|
||||||
) }
|
) }
|
||||||
@ -500,11 +543,29 @@ export type MeQuery = (
|
|||||||
& Pick<UserAccount, 'firstName' | 'lastName'>
|
& Pick<UserAccount, 'firstName' | 'lastName'>
|
||||||
& { profileIcon: (
|
& { profileIcon: (
|
||||||
{ __typename?: 'ProfileIcon' }
|
{ __typename?: 'ProfileIcon' }
|
||||||
& Pick<ProfileIcon, 'initials'>
|
& Pick<ProfileIcon, 'initials' | 'bgColor'>
|
||||||
) }
|
) }
|
||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type UnassignTaskMutationVariables = {
|
||||||
|
taskID: Scalars['UUID'];
|
||||||
|
userID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type UnassignTaskMutation = (
|
||||||
|
{ __typename?: 'Mutation' }
|
||||||
|
& { unassignTask: (
|
||||||
|
{ __typename?: 'Task' }
|
||||||
|
& Pick<Task, 'taskID'>
|
||||||
|
& { assigned: Array<(
|
||||||
|
{ __typename?: 'ProjectMember' }
|
||||||
|
& Pick<ProjectMember, 'userID' | 'firstName' | 'lastName'>
|
||||||
|
)> }
|
||||||
|
) }
|
||||||
|
);
|
||||||
|
|
||||||
export type UpdateTaskDescriptionMutationVariables = {
|
export type UpdateTaskDescriptionMutationVariables = {
|
||||||
taskID: Scalars['UUID'];
|
taskID: Scalars['UUID'];
|
||||||
description: Scalars['String'];
|
description: Scalars['String'];
|
||||||
@ -759,6 +820,7 @@ export const FindProjectDocument = gql`
|
|||||||
profileIcon {
|
profileIcon {
|
||||||
url
|
url
|
||||||
initials
|
initials
|
||||||
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
taskGroups {
|
taskGroups {
|
||||||
@ -770,6 +832,16 @@ export const FindProjectDocument = gql`
|
|||||||
name
|
name
|
||||||
position
|
position
|
||||||
description
|
description
|
||||||
|
assigned {
|
||||||
|
userID
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
profileIcon {
|
||||||
|
url
|
||||||
|
initials
|
||||||
|
bgColor
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -818,6 +890,7 @@ export const FindTaskDocument = gql`
|
|||||||
profileIcon {
|
profileIcon {
|
||||||
url
|
url
|
||||||
initials
|
initials
|
||||||
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -893,6 +966,7 @@ export const MeDocument = gql`
|
|||||||
lastName
|
lastName
|
||||||
profileIcon {
|
profileIcon {
|
||||||
initials
|
initials
|
||||||
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -922,6 +996,44 @@ export function useMeLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptio
|
|||||||
export type MeQueryHookResult = ReturnType<typeof useMeQuery>;
|
export type MeQueryHookResult = ReturnType<typeof useMeQuery>;
|
||||||
export type MeLazyQueryHookResult = ReturnType<typeof useMeLazyQuery>;
|
export type MeLazyQueryHookResult = ReturnType<typeof useMeLazyQuery>;
|
||||||
export type MeQueryResult = ApolloReactCommon.QueryResult<MeQuery, MeQueryVariables>;
|
export type MeQueryResult = ApolloReactCommon.QueryResult<MeQuery, MeQueryVariables>;
|
||||||
|
export const UnassignTaskDocument = gql`
|
||||||
|
mutation unassignTask($taskID: UUID!, $userID: UUID!) {
|
||||||
|
unassignTask(input: {taskID: $taskID, userID: $userID}) {
|
||||||
|
assigned {
|
||||||
|
userID
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
}
|
||||||
|
taskID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type UnassignTaskMutationFn = ApolloReactCommon.MutationFunction<UnassignTaskMutation, UnassignTaskMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useUnassignTaskMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useUnassignTaskMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useUnassignTaskMutation` 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 [unassignTaskMutation, { data, loading, error }] = useUnassignTaskMutation({
|
||||||
|
* variables: {
|
||||||
|
* taskID: // value for 'taskID'
|
||||||
|
* userID: // value for 'userID'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useUnassignTaskMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<UnassignTaskMutation, UnassignTaskMutationVariables>) {
|
||||||
|
return ApolloReactHooks.useMutation<UnassignTaskMutation, UnassignTaskMutationVariables>(UnassignTaskDocument, baseOptions);
|
||||||
|
}
|
||||||
|
export type UnassignTaskMutationHookResult = ReturnType<typeof useUnassignTaskMutation>;
|
||||||
|
export type UnassignTaskMutationResult = ApolloReactCommon.MutationResult<UnassignTaskMutation>;
|
||||||
|
export type UnassignTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<UnassignTaskMutation, UnassignTaskMutationVariables>;
|
||||||
export const UpdateTaskDescriptionDocument = gql`
|
export const UpdateTaskDescriptionDocument = gql`
|
||||||
mutation updateTaskDescription($taskID: UUID!, $description: String!) {
|
mutation updateTaskDescription($taskID: UUID!, $description: String!) {
|
||||||
updateTaskDescription(input: {taskID: $taskID, description: $description}) {
|
updateTaskDescription(input: {taskID: $taskID, description: $description}) {
|
||||||
|
@ -8,6 +8,7 @@ query findProject($projectId: String!) {
|
|||||||
profileIcon {
|
profileIcon {
|
||||||
url
|
url
|
||||||
initials
|
initials
|
||||||
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
taskGroups {
|
taskGroups {
|
||||||
@ -19,6 +20,16 @@ query findProject($projectId: String!) {
|
|||||||
name
|
name
|
||||||
position
|
position
|
||||||
description
|
description
|
||||||
|
assigned {
|
||||||
|
userID
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
profileIcon {
|
||||||
|
url
|
||||||
|
initials
|
||||||
|
bgColor
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ query findTask($taskID: UUID!) {
|
|||||||
profileIcon {
|
profileIcon {
|
||||||
url
|
url
|
||||||
initials
|
initials
|
||||||
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ query me {
|
|||||||
lastName
|
lastName
|
||||||
profileIcon {
|
profileIcon {
|
||||||
initials
|
initials
|
||||||
|
bgColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
web/src/shared/graphql/unassignTask.graphqls
Normal file
10
web/src/shared/graphql/unassignTask.graphqls
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
mutation unassignTask($taskID: UUID!, $userID: UUID!) {
|
||||||
|
unassignTask(input: {taskID: $taskID, userID: $userID}) {
|
||||||
|
assigned {
|
||||||
|
userID
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
}
|
||||||
|
taskID
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user