feature: add team creation & project deletion
This commit is contained in:
parent
5c3afaba7c
commit
fd7c006b73
File diff suppressed because it is too large
Load Diff
@ -36,10 +36,29 @@ type CreateTaskChecklistItem struct {
|
|||||||
Position float64 `json:"position"`
|
Position float64 `json:"position"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CreateTeamMember struct {
|
||||||
|
UserID uuid.UUID `json:"userID"`
|
||||||
|
TeamID uuid.UUID `json:"teamID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateTeamMemberPayload struct {
|
||||||
|
Team *pg.Team `json:"team"`
|
||||||
|
TeamMember *ProjectMember `json:"teamMember"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteProject struct {
|
||||||
|
ProjectID uuid.UUID `json:"projectID"`
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteProjectLabel struct {
|
type DeleteProjectLabel struct {
|
||||||
ProjectLabelID uuid.UUID `json:"projectLabelID"`
|
ProjectLabelID uuid.UUID `json:"projectLabelID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeleteProjectPayload struct {
|
||||||
|
Ok bool `json:"ok"`
|
||||||
|
Project *pg.Project `json:"project"`
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteTaskChecklistItem struct {
|
type DeleteTaskChecklistItem struct {
|
||||||
TaskChecklistItemID uuid.UUID `json:"taskChecklistItemID"`
|
TaskChecklistItemID uuid.UUID `json:"taskChecklistItemID"`
|
||||||
}
|
}
|
||||||
@ -124,7 +143,7 @@ type NewTaskLocation struct {
|
|||||||
|
|
||||||
type NewTeam struct {
|
type NewTeam struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
OrganizationID string `json:"organizationID"`
|
OrganizationID uuid.UUID `json:"organizationID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NewUserAccount struct {
|
type NewUserAccount struct {
|
||||||
|
@ -55,6 +55,7 @@ type Team {
|
|||||||
id: ID!
|
id: ID!
|
||||||
createdAt: Time!
|
createdAt: Time!
|
||||||
name: String!
|
name: String!
|
||||||
|
members: [ProjectMember!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Project {
|
type Project {
|
||||||
@ -117,7 +118,13 @@ input FindTask {
|
|||||||
taskID: UUID!
|
taskID: UUID!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Organization {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
}
|
||||||
|
|
||||||
type Query {
|
type Query {
|
||||||
|
organizations: [Organization!]!
|
||||||
users: [UserAccount!]!
|
users: [UserAccount!]!
|
||||||
findUser(input: FindUser!): UserAccount!
|
findUser(input: FindUser!): UserAccount!
|
||||||
findProject(input: FindProject!): Project!
|
findProject(input: FindProject!): Project!
|
||||||
@ -143,7 +150,7 @@ input NewUserAccount {
|
|||||||
|
|
||||||
input NewTeam {
|
input NewTeam {
|
||||||
name: String!
|
name: String!
|
||||||
organizationID: String!
|
organizationID: UUID!
|
||||||
}
|
}
|
||||||
|
|
||||||
input NewProject {
|
input NewProject {
|
||||||
@ -330,6 +337,25 @@ input UpdateTaskChecklistItemName {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input CreateTeamMember {
|
||||||
|
userID: UUID!
|
||||||
|
teamID: UUID!
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateTeamMemberPayload {
|
||||||
|
team: Team!
|
||||||
|
teamMember: ProjectMember!
|
||||||
|
}
|
||||||
|
|
||||||
|
input DeleteProject {
|
||||||
|
projectID: UUID!
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteProjectPayload {
|
||||||
|
ok: Boolean!
|
||||||
|
project: Project!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
createRefreshToken(input: NewRefreshToken!): RefreshToken!
|
||||||
|
|
||||||
@ -338,7 +364,10 @@ type Mutation {
|
|||||||
createTeam(input: NewTeam!): Team!
|
createTeam(input: NewTeam!): Team!
|
||||||
clearProfileAvatar: UserAccount!
|
clearProfileAvatar: UserAccount!
|
||||||
|
|
||||||
|
createTeamMember(input: CreateTeamMember!): CreateTeamMemberPayload!
|
||||||
|
|
||||||
createProject(input: NewProject!): Project!
|
createProject(input: NewProject!): Project!
|
||||||
|
deleteProject(input: DeleteProject!): DeleteProjectPayload!
|
||||||
updateProjectName(input: UpdateProjectName): Project!
|
updateProjectName(input: UpdateProjectName): Project!
|
||||||
|
|
||||||
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
|
createProjectLabel(input: NewProjectLabel!): ProjectLabel!
|
||||||
|
@ -41,12 +41,8 @@ func (r *mutationResolver) CreateUserAccount(ctx context.Context, input NewUserA
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*pg.Team, error) {
|
func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*pg.Team, error) {
|
||||||
organizationID, err := uuid.Parse(input.OrganizationID)
|
|
||||||
if err != nil {
|
|
||||||
return &pg.Team{}, err
|
|
||||||
}
|
|
||||||
createdAt := time.Now().UTC()
|
createdAt := time.Now().UTC()
|
||||||
team, err := r.Repository.CreateTeam(ctx, pg.CreateTeamParams{organizationID, createdAt, input.Name})
|
team, err := r.Repository.CreateTeam(ctx, pg.CreateTeamParams{input.OrganizationID, createdAt, input.Name})
|
||||||
return &team, err
|
return &team, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +61,49 @@ func (r *mutationResolver) ClearProfileAvatar(ctx context.Context) (*pg.UserAcco
|
|||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) CreateTeamMember(ctx context.Context, input CreateTeamMember) (*CreateTeamMemberPayload, error) {
|
||||||
|
addedDate := time.Now().UTC()
|
||||||
|
team, err := r.Repository.GetTeamByID(ctx, input.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
return &CreateTeamMemberPayload{}, err
|
||||||
|
}
|
||||||
|
_, err = r.Repository.CreateTeamMember(ctx, pg.CreateTeamMemberParams{TeamID: input.TeamID, UserID: input.UserID, Addeddate: addedDate})
|
||||||
|
user, err := r.Repository.GetUserAccountByID(ctx, input.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return &CreateTeamMemberPayload{}, err
|
||||||
|
}
|
||||||
|
var url *string
|
||||||
|
if user.ProfileAvatarUrl.Valid {
|
||||||
|
url = &user.ProfileAvatarUrl.String
|
||||||
|
}
|
||||||
|
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||||
|
return &CreateTeamMemberPayload{
|
||||||
|
Team: &team,
|
||||||
|
TeamMember: &ProjectMember{
|
||||||
|
ID: user.UserID,
|
||||||
|
FullName: user.FullName,
|
||||||
|
ProfileIcon: profileIcon,
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject) (*pg.Project, error) {
|
func (r *mutationResolver) CreateProject(ctx context.Context, input NewProject) (*pg.Project, error) {
|
||||||
createdAt := time.Now().UTC()
|
createdAt := time.Now().UTC()
|
||||||
project, err := r.Repository.CreateProject(ctx, pg.CreateProjectParams{input.UserID, input.TeamID, createdAt, input.Name})
|
project, err := r.Repository.CreateProject(ctx, pg.CreateProjectParams{input.UserID, input.TeamID, createdAt, input.Name})
|
||||||
return &project, err
|
return &project, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *mutationResolver) DeleteProject(ctx context.Context, input DeleteProject) (*DeleteProjectPayload, error) {
|
||||||
|
project, err := r.Repository.GetProjectByID(ctx, input.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
return &DeleteProjectPayload{Ok: false}, err
|
||||||
|
}
|
||||||
|
err = r.Repository.DeleteProjectByID(ctx, input.ProjectID)
|
||||||
|
if err != nil {
|
||||||
|
return &DeleteProjectPayload{Ok: false}, err
|
||||||
|
}
|
||||||
|
return &DeleteProjectPayload{Project: &project, Ok: true}, err
|
||||||
|
}
|
||||||
|
|
||||||
func (r *mutationResolver) UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*pg.Project, error) {
|
func (r *mutationResolver) UpdateProjectName(ctx context.Context, input *UpdateProjectName) (*pg.Project, error) {
|
||||||
project, err := r.Repository.UpdateProjectNameByID(ctx, pg.UpdateProjectNameByIDParams{ProjectID: input.ProjectID, Name: input.Name})
|
project, err := r.Repository.UpdateProjectNameByID(ctx, pg.UpdateProjectNameByIDParams{ProjectID: input.ProjectID, Name: input.Name})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -409,6 +442,10 @@ func (r *mutationResolver) LogoutUser(ctx context.Context, input LogoutUser) (bo
|
|||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *organizationResolver) ID(ctx context.Context, obj *pg.Organization) (uuid.UUID, error) {
|
||||||
|
return obj.OrganizationID, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *projectResolver) ID(ctx context.Context, obj *pg.Project) (uuid.UUID, error) {
|
func (r *projectResolver) ID(ctx context.Context, obj *pg.Project) (uuid.UUID, error) {
|
||||||
return obj.ProjectID, nil
|
return obj.ProjectID, nil
|
||||||
}
|
}
|
||||||
@ -475,6 +512,10 @@ func (r *projectLabelResolver) Name(ctx context.Context, obj *pg.ProjectLabel) (
|
|||||||
return name, nil
|
return name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *queryResolver) Organizations(ctx context.Context) ([]pg.Organization, error) {
|
||||||
|
return r.Repository.GetAllOrganizations(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -683,6 +724,31 @@ func (r *teamResolver) ID(ctx context.Context, obj *pg.Team) (uuid.UUID, error)
|
|||||||
return obj.TeamID, nil
|
return obj.TeamID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *teamResolver) Members(ctx context.Context, obj *pg.Team) ([]ProjectMember, error) {
|
||||||
|
teamMembers, err := r.Repository.GetTeamMembersForTeamID(ctx, obj.TeamID)
|
||||||
|
var projectMembers []ProjectMember
|
||||||
|
if err != nil {
|
||||||
|
return projectMembers, err
|
||||||
|
}
|
||||||
|
for _, teamMember := range teamMembers {
|
||||||
|
user, err := r.Repository.GetUserAccountByID(ctx, teamMember.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return projectMembers, err
|
||||||
|
}
|
||||||
|
var url *string
|
||||||
|
if user.ProfileAvatarUrl.Valid {
|
||||||
|
url = &user.ProfileAvatarUrl.String
|
||||||
|
}
|
||||||
|
profileIcon := &ProfileIcon{url, &user.Initials, &user.ProfileBgColor}
|
||||||
|
projectMembers = append(projectMembers, ProjectMember{
|
||||||
|
ID: user.UserID,
|
||||||
|
FullName: user.FullName,
|
||||||
|
ProfileIcon: profileIcon,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return projectMembers, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *userAccountResolver) ID(ctx context.Context, obj *pg.UserAccount) (uuid.UUID, error) {
|
func (r *userAccountResolver) ID(ctx context.Context, obj *pg.UserAccount) (uuid.UUID, error) {
|
||||||
return obj.UserID, nil
|
return obj.UserID, nil
|
||||||
}
|
}
|
||||||
@ -702,6 +768,9 @@ func (r *Resolver) LabelColor() LabelColorResolver { return &labelColorResolver{
|
|||||||
// Mutation returns MutationResolver implementation.
|
// Mutation returns MutationResolver implementation.
|
||||||
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} }
|
||||||
|
|
||||||
|
// Organization returns OrganizationResolver implementation.
|
||||||
|
func (r *Resolver) Organization() OrganizationResolver { return &organizationResolver{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} }
|
||||||
|
|
||||||
@ -737,6 +806,7 @@ func (r *Resolver) UserAccount() UserAccountResolver { return &userAccountResolv
|
|||||||
|
|
||||||
type labelColorResolver struct{ *Resolver }
|
type labelColorResolver struct{ *Resolver }
|
||||||
type mutationResolver struct{ *Resolver }
|
type mutationResolver struct{ *Resolver }
|
||||||
|
type organizationResolver struct{ *Resolver }
|
||||||
type projectResolver struct{ *Resolver }
|
type projectResolver struct{ *Resolver }
|
||||||
type projectLabelResolver struct{ *Resolver }
|
type projectLabelResolver struct{ *Resolver }
|
||||||
type queryResolver struct{ *Resolver }
|
type queryResolver struct{ *Resolver }
|
||||||
|
8
api/migrations/0031_add-team-member-table.up.sql
Normal file
8
api/migrations/0031_add-team-member-table.up.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE team_member (
|
||||||
|
team_member_id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||||
|
team_id uuid NOT NULL REFERENCES team(team_id) ON DELETE CASCADE,
|
||||||
|
user_id uuid NOT NULL REFERENCES user_account(user_id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(team_id, user_id),
|
||||||
|
addedDate timestamptz NOT NULL
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
ALTER TABLE project_label DROP CONSTRAINT project_label_project_id_fkey;
|
||||||
|
ALTER TABLE project_label
|
||||||
|
ADD CONSTRAINT project_label_project_id_fkey
|
||||||
|
FOREIGN KEY (project_id)
|
||||||
|
REFERENCES project(project_id)
|
||||||
|
ON DELETE CASCADE;
|
@ -0,0 +1,6 @@
|
|||||||
|
ALTER TABLE task_label DROP CONSTRAINT task_label_project_label_id_fkey;
|
||||||
|
ALTER TABLE task_label
|
||||||
|
ADD CONSTRAINT task_label_project_label_id_fkey
|
||||||
|
FOREIGN KEY (project_label_id)
|
||||||
|
REFERENCES project_label(project_label_id)
|
||||||
|
ON DELETE CASCADE;
|
@ -103,6 +103,13 @@ type Team struct {
|
|||||||
OrganizationID uuid.UUID `json:"organization_id"`
|
OrganizationID uuid.UUID `json:"organization_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TeamMember struct {
|
||||||
|
TeamMemberID uuid.UUID `json:"team_member_id"`
|
||||||
|
TeamID uuid.UUID `json:"team_id"`
|
||||||
|
UserID uuid.UUID `json:"user_id"`
|
||||||
|
Addeddate time.Time `json:"addeddate"`
|
||||||
|
}
|
||||||
|
|
||||||
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"`
|
||||||
|
@ -7,11 +7,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
|
CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error)
|
||||||
|
DeleteTeamMemberByUserID(ctx context.Context, userID uuid.UUID) error
|
||||||
|
GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error)
|
||||||
|
|
||||||
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
||||||
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
||||||
GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error)
|
GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error)
|
||||||
GetAllTeams(ctx context.Context) ([]Team, error)
|
GetAllTeams(ctx context.Context) ([]Team, error)
|
||||||
|
|
||||||
|
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error
|
||||||
|
|
||||||
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
|
CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error)
|
||||||
GetAllProjects(ctx context.Context) ([]Project, error)
|
GetAllProjects(ctx context.Context) ([]Project, error)
|
||||||
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
||||||
|
@ -39,6 +39,15 @@ func (q *Queries) CreateProject(ctx context.Context, arg CreateProjectParams) (P
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteProjectByID = `-- name: DeleteProjectByID :exec
|
||||||
|
DELETE FROM project WHERE project_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteProjectByID, projectID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const getAllProjects = `-- name: GetAllProjects :many
|
const getAllProjects = `-- name: GetAllProjects :many
|
||||||
SELECT project_id, team_id, created_at, name, owner FROM project
|
SELECT project_id, team_id, created_at, name, owner FROM project
|
||||||
`
|
`
|
||||||
|
@ -21,8 +21,10 @@ type Querier interface {
|
|||||||
CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error)
|
CreateTaskGroup(ctx context.Context, arg CreateTaskGroupParams) (TaskGroup, error)
|
||||||
CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error)
|
CreateTaskLabelForTask(ctx context.Context, arg CreateTaskLabelForTaskParams) (TaskLabel, error)
|
||||||
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
CreateTeam(ctx context.Context, arg CreateTeamParams) (Team, error)
|
||||||
|
CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error)
|
||||||
CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error)
|
CreateUserAccount(ctx context.Context, arg CreateUserAccountParams) (UserAccount, error)
|
||||||
DeleteExpiredTokens(ctx context.Context) error
|
DeleteExpiredTokens(ctx context.Context) error
|
||||||
|
DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error
|
||||||
DeleteProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) error
|
DeleteProjectLabelByID(ctx context.Context, projectLabelID uuid.UUID) 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
|
||||||
@ -34,6 +36,7 @@ type Querier interface {
|
|||||||
DeleteTaskLabelForTaskByProjectLabelID(ctx context.Context, arg DeleteTaskLabelForTaskByProjectLabelIDParams) error
|
DeleteTaskLabelForTaskByProjectLabelID(ctx context.Context, arg DeleteTaskLabelForTaskByProjectLabelIDParams) error
|
||||||
DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error)
|
||||||
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error
|
||||||
|
DeleteTeamMemberByUserID(ctx context.Context, userID uuid.UUID) error
|
||||||
GetAllOrganizations(ctx context.Context) ([]Organization, error)
|
GetAllOrganizations(ctx context.Context) ([]Organization, error)
|
||||||
GetAllProjects(ctx context.Context) ([]Project, error)
|
GetAllProjects(ctx context.Context) ([]Project, error)
|
||||||
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error)
|
||||||
@ -59,6 +62,7 @@ type Querier interface {
|
|||||||
GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error)
|
GetTaskLabelsForTaskID(ctx context.Context, taskID uuid.UUID) ([]TaskLabel, error)
|
||||||
GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error)
|
GetTasksForTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) ([]Task, error)
|
||||||
GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error)
|
GetTeamByID(ctx context.Context, teamID uuid.UUID) (Team, error)
|
||||||
|
GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error)
|
||||||
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
GetTeamsForOrganization(ctx context.Context, organizationID uuid.UUID) ([]Team, error)
|
||||||
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
GetUserAccountByID(ctx context.Context, userID uuid.UUID) (UserAccount, error)
|
||||||
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
GetUserAccountByUsername(ctx context.Context, username string) (UserAccount, error)
|
||||||
|
75
api/pg/team_member.sql.go
Normal file
75
api/pg/team_member.sql.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// source: team_member.sql
|
||||||
|
|
||||||
|
package pg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
const createTeamMember = `-- name: CreateTeamMember :one
|
||||||
|
INSERT INTO team_member (team_id, user_id, addedDate) VALUES ($1, $2, $3)
|
||||||
|
RETURNING team_member_id, team_id, user_id, addeddate
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateTeamMemberParams struct {
|
||||||
|
TeamID uuid.UUID `json:"team_id"`
|
||||||
|
UserID uuid.UUID `json:"user_id"`
|
||||||
|
Addeddate time.Time `json:"addeddate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateTeamMember(ctx context.Context, arg CreateTeamMemberParams) (TeamMember, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createTeamMember, arg.TeamID, arg.UserID, arg.Addeddate)
|
||||||
|
var i TeamMember
|
||||||
|
err := row.Scan(
|
||||||
|
&i.TeamMemberID,
|
||||||
|
&i.TeamID,
|
||||||
|
&i.UserID,
|
||||||
|
&i.Addeddate,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTeamMemberByUserID = `-- name: DeleteTeamMemberByUserID :exec
|
||||||
|
DELETE FROM team_member WHERE user_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteTeamMemberByUserID(ctx context.Context, userID uuid.UUID) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteTeamMemberByUserID, userID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTeamMembersForTeamID = `-- name: GetTeamMembersForTeamID :many
|
||||||
|
SELECT team_member_id, team_id, user_id, addeddate FROM team_member WHERE team_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetTeamMembersForTeamID(ctx context.Context, teamID uuid.UUID) ([]TeamMember, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getTeamMembersForTeamID, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []TeamMember
|
||||||
|
for rows.Next() {
|
||||||
|
var i TeamMember
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.TeamMemberID,
|
||||||
|
&i.TeamID,
|
||||||
|
&i.UserID,
|
||||||
|
&i.Addeddate,
|
||||||
|
); 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
|
||||||
|
}
|
@ -12,3 +12,6 @@ INSERT INTO project(owner, team_id, created_at, name) VALUES ($1, $2, $3, $4) RE
|
|||||||
|
|
||||||
-- name: UpdateProjectNameByID :one
|
-- name: UpdateProjectNameByID :one
|
||||||
UPDATE project SET name = $2 WHERE project_id = $1 RETURNING *;
|
UPDATE project SET name = $2 WHERE project_id = $1 RETURNING *;
|
||||||
|
|
||||||
|
-- name: DeleteProjectByID :exec
|
||||||
|
DELETE FROM project WHERE project_id = $1;
|
||||||
|
9
api/query/team_member.sql
Normal file
9
api/query/team_member.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- name: CreateTeamMember :one
|
||||||
|
INSERT INTO team_member (team_id, user_id, addedDate) VALUES ($1, $2, $3)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetTeamMembersForTeamID :many
|
||||||
|
SELECT * FROM team_member WHERE team_id = $1;
|
||||||
|
|
||||||
|
-- name: DeleteTeamMemberByUserID :exec
|
||||||
|
DELETE FROM team_member WHERE user_id = $1;
|
@ -1,22 +1,47 @@
|
|||||||
import React, { useState, useContext } from 'react';
|
import React, { useState, useContext } from 'react';
|
||||||
import TopNavbar from 'shared/components/TopNavbar';
|
import TopNavbar from 'shared/components/TopNavbar';
|
||||||
import DropdownMenu, { ProfileMenu } from 'shared/components/DropdownMenu';
|
import DropdownMenu, { ProfileMenu } from 'shared/components/DropdownMenu';
|
||||||
import ProjectSettings from 'shared/components/ProjectSettings';
|
import ProjectSettings, { DeleteProject } from 'shared/components/ProjectSettings';
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import UserIDContext from 'App/context';
|
import UserIDContext from 'App/context';
|
||||||
import { useMeQuery } from 'shared/generated/graphql';
|
import { useMeQuery, useDeleteProjectMutation, GetProjectsDocument } from 'shared/generated/graphql';
|
||||||
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||||
|
import produce from 'immer';
|
||||||
|
|
||||||
type GlobalTopNavbarProps = {
|
type GlobalTopNavbarProps = {
|
||||||
|
projectID: string | null;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
projectMembers?: null | Array<TaskUser>;
|
projectMembers?: null | Array<TaskUser>;
|
||||||
onSaveProjectName?: (projectName: string) => void;
|
onSaveProjectName?: (projectName: string) => void;
|
||||||
};
|
};
|
||||||
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ name, projectMembers, onSaveProjectName }) => {
|
const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ projectID, name, projectMembers, onSaveProjectName }) => {
|
||||||
const { loading, data } = useMeQuery();
|
const { loading, data } = useMeQuery();
|
||||||
const { showPopup, hidePopup } = usePopup();
|
const { showPopup, hidePopup, setTab } = usePopup();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { userID, setUserID } = useContext(UserIDContext);
|
const { userID, setUserID } = useContext(UserIDContext);
|
||||||
|
const [deleteProject] = useDeleteProjectMutation({
|
||||||
|
update: (client, deleteData) => {
|
||||||
|
const cacheData: any = client.readQuery({
|
||||||
|
query: GetProjectsDocument,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(cacheData);
|
||||||
|
console.log(deleteData);
|
||||||
|
|
||||||
|
const newData = produce(cacheData, (draftState: any) => {
|
||||||
|
draftState.projects = draftState.projects.filter(
|
||||||
|
(project: any) => project.id !== deleteData.data.deleteProject.project.id,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.writeQuery({
|
||||||
|
query: GetProjectsDocument,
|
||||||
|
data: {
|
||||||
|
...newData,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
const onLogout = () => {
|
const onLogout = () => {
|
||||||
fetch('http://localhost:3333/auth/logout', {
|
fetch('http://localhost:3333/auth/logout', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -49,9 +74,27 @@ const GlobalTopNavbar: React.FC<GlobalTopNavbarProps> = ({ name, projectMembers,
|
|||||||
const onOpenSettings = ($target: React.RefObject<HTMLElement>) => {
|
const onOpenSettings = ($target: React.RefObject<HTMLElement>) => {
|
||||||
showPopup(
|
showPopup(
|
||||||
$target,
|
$target,
|
||||||
|
<>
|
||||||
<Popup title={null} tab={0}>
|
<Popup title={null} tab={0}>
|
||||||
<ProjectSettings />
|
<ProjectSettings
|
||||||
</Popup>,
|
onDeleteProject={() => {
|
||||||
|
setTab(1, 325);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popup>
|
||||||
|
<Popup title={`Delete the "${name}" project?`} tab={1}>
|
||||||
|
<DeleteProject
|
||||||
|
name={name ?? ''}
|
||||||
|
onDeleteProject={() => {
|
||||||
|
if (projectID) {
|
||||||
|
deleteProject({ variables: { projectID } });
|
||||||
|
hidePopup();
|
||||||
|
history.push('/projects');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popup>
|
||||||
|
</>,
|
||||||
185,
|
185,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,7 @@ const Projects = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<GlobalTopNavbar onSaveProjectName={() => {}} name={null} />
|
<GlobalTopNavbar projectID={null} onSaveProjectName={() => {}} name={null} />
|
||||||
{!loading && data && (
|
{!loading && data && (
|
||||||
<Settings
|
<Settings
|
||||||
profile={data.me.profileIcon}
|
profile={data.me.profileIcon}
|
||||||
|
@ -472,7 +472,7 @@ const Project = () => {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GlobalTopNavbar onSaveProjectName={projectName => {}} name="" />
|
<GlobalTopNavbar onSaveProjectName={projectName => {}} name="" projectID={null} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -510,6 +510,7 @@ const Project = () => {
|
|||||||
updateProjectName({ variables: { projectID, name: projectName } });
|
updateProjectName({ variables: { projectID, name: projectName } });
|
||||||
}}
|
}}
|
||||||
projectMembers={data.findProject.members}
|
projectMembers={data.findProject.members}
|
||||||
|
projectID={projectID}
|
||||||
name={data.findProject.name}
|
name={data.findProject.name}
|
||||||
/>
|
/>
|
||||||
<ProjectBar>
|
<ProjectBar>
|
||||||
|
@ -1,32 +1,183 @@
|
|||||||
import React, { useState, useContext, useEffect } from 'react';
|
import React, { useState, useContext, useEffect } from 'react';
|
||||||
import styled from 'styled-components/macro';
|
import styled from 'styled-components/macro';
|
||||||
import GlobalTopNavbar from 'App/TopNavbar';
|
import GlobalTopNavbar from 'App/TopNavbar';
|
||||||
import { useGetProjectsQuery, useCreateProjectMutation, GetProjectsDocument } from 'shared/generated/graphql';
|
import {
|
||||||
|
useCreateTeamMutation,
|
||||||
|
useGetProjectsQuery,
|
||||||
|
useCreateProjectMutation,
|
||||||
|
GetProjectsDocument,
|
||||||
|
} from 'shared/generated/graphql';
|
||||||
|
|
||||||
import ProjectGridItem, { AddProjectItem } from 'shared/components/ProjectGridItem';
|
import ProjectGridItem, { AddProjectItem } from 'shared/components/ProjectGridItem';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import Navbar from 'App/Navbar';
|
import Navbar from 'App/Navbar';
|
||||||
import NewProject from 'shared/components/NewProject';
|
import NewProject from 'shared/components/NewProject';
|
||||||
import UserIDContext from 'App/context';
|
import UserIDContext from 'App/context';
|
||||||
|
import Button from 'shared/components/Button';
|
||||||
|
import { usePopup, Popup } from 'shared/components/PopupMenu';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import Input from 'shared/components/Input';
|
||||||
|
|
||||||
const MainContent = styled.div`
|
const CreateTeamButton = styled(Button)`
|
||||||
padding: 0 0 50px 80px;
|
width: 100%;
|
||||||
height: 100%;
|
`;
|
||||||
background: #262c49;
|
type CreateTeamData = { teamName: string };
|
||||||
|
type CreateTeamFormProps = {
|
||||||
|
onCreateTeam: (teamName: string) => void;
|
||||||
|
};
|
||||||
|
const CreateTeamFormContainer = styled.form``;
|
||||||
|
|
||||||
|
const CreateTeamForm: React.FC<CreateTeamFormProps> = ({ onCreateTeam }) => {
|
||||||
|
const { register, handleSubmit, errors } = useForm<CreateTeamData>();
|
||||||
|
const createTeam = (data: CreateTeamData) => {
|
||||||
|
onCreateTeam(data.teamName);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<CreateTeamFormContainer onSubmit={handleSubmit(createTeam)}>
|
||||||
|
<Input
|
||||||
|
width="100%"
|
||||||
|
label="Team name"
|
||||||
|
id="teamName"
|
||||||
|
name="teamName"
|
||||||
|
variant="alternate"
|
||||||
|
ref={register({ required: 'Team name is required' })}
|
||||||
|
/>
|
||||||
|
<CreateTeamButton type="submit">Create</CreateTeamButton>
|
||||||
|
</CreateTeamFormContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProjectAddTile = styled.div`
|
||||||
|
background-color: rgba(${props => props.theme.colors.bg.primary}, 0.4);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: 50%;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 8px;
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
border-radius: 3px;
|
||||||
|
display: block;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ProjectTile = styled(Link)<{ color: string }>`
|
||||||
|
background-color: ${props => props.color};
|
||||||
|
background-size: cover;
|
||||||
|
background-position: 50%;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 8px;
|
||||||
|
position: relative;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
border-radius: 3px;
|
||||||
|
display: block;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectTileFade = styled.div`
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectListItem = styled.li`
|
||||||
|
width: 23.5%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 2% 2% 0;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover ${ProjectTileFade} {
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectList = styled.ul`
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
& ${ProjectListItem}:nth-of-type(4n) {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectTileDetails = styled.div`
|
||||||
|
display: flex;
|
||||||
|
height: 80px;
|
||||||
|
position: relative;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectAddTileDetails = styled.div`
|
||||||
|
display: flex;
|
||||||
|
height: 80px;
|
||||||
|
position: relative;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectTileName = styled.div<{ centered?: boolean }>`
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 40px;
|
||||||
|
width: 100%;
|
||||||
|
word-wrap: break-word;
|
||||||
|
${props => props.centered && 'text-align: center;'}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectSectionTitleWrapper = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
padding: 8px 0;
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectSectionTitle = styled.h3`
|
||||||
|
font-size: 16px;
|
||||||
|
color: rgba(${props => props.theme.colors.text.primary});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ProjectsContainer = styled.div`
|
||||||
|
margin: 40px 16px 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 825px;
|
||||||
|
min-width: 288px;
|
||||||
|
`;
|
||||||
const ProjectGrid = styled.div`
|
const ProjectGrid = styled.div`
|
||||||
width: 60%;
|
|
||||||
max-width: 780px;
|
max-width: 780px;
|
||||||
margin: 25px auto;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 240px 240px 240px;
|
grid-template-columns: 240px 240px 240px;
|
||||||
gap: 20px 10px;
|
gap: 20px 10px;
|
||||||
`;
|
`;
|
||||||
|
const AddTeamButton = styled(Button)`
|
||||||
|
padding: 6px 12px;
|
||||||
|
float: right;
|
||||||
|
`;
|
||||||
|
|
||||||
const ProjectLink = styled(Link)``;
|
const ProjectLink = styled(Link)``;
|
||||||
|
|
||||||
const Projects = () => {
|
const Projects = () => {
|
||||||
|
const { showPopup } = usePopup();
|
||||||
const { loading, data } = useGetProjectsQuery();
|
const { loading, data } = useGetProjectsQuery();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'Citadel';
|
document.title = 'Citadel';
|
||||||
@ -53,6 +204,7 @@ const Projects = () => {
|
|||||||
});
|
});
|
||||||
const [showNewProject, setShowNewProject] = useState(false);
|
const [showNewProject, setShowNewProject] = useState(false);
|
||||||
const { userID, setUserID } = useContext(UserIDContext);
|
const { userID, setUserID } = useContext(UserIDContext);
|
||||||
|
const [createTeam] = useCreateTeamMutation();
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -60,25 +212,75 @@ const Projects = () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f'];
|
||||||
if (data) {
|
if (data) {
|
||||||
const { projects, teams } = data;
|
const { projects, teams, organizations } = data;
|
||||||
|
const organizationID = organizations[0].id ?? null;
|
||||||
|
const projectTeams = teams.map(team => {
|
||||||
|
return {
|
||||||
|
id: team.id,
|
||||||
|
name: team.name,
|
||||||
|
projects: projects.filter(project => project.team.id === team.id),
|
||||||
|
};
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<GlobalTopNavbar onSaveProjectName={() => {}} name={null} />
|
<GlobalTopNavbar onSaveProjectName={() => {}} projectID={null} name={null} />
|
||||||
<ProjectGrid>
|
<Wrapper>
|
||||||
{projects.map(project => (
|
<ProjectsContainer>
|
||||||
<ProjectLink key={project.id} to={`/projects/${project.id}`}>
|
<AddTeamButton
|
||||||
<ProjectGridItem
|
variant="outline"
|
||||||
project={{ ...project, projectID: project.id, teamTitle: project.team.name, taskGroups: [] }}
|
onClick={$target => {
|
||||||
/>
|
showPopup(
|
||||||
</ProjectLink>
|
$target,
|
||||||
))}
|
<Popup title="Create team" tab={0}>
|
||||||
<AddProjectItem
|
<CreateTeamForm
|
||||||
onAddProject={() => {
|
onCreateTeam={teamName => {
|
||||||
setShowNewProject(true);
|
if (organizationID) {
|
||||||
|
createTeam({ variables: { name: teamName, organizationID } });
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ProjectGrid>
|
</Popup>,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Team
|
||||||
|
</AddTeamButton>
|
||||||
|
{projectTeams.map(team => {
|
||||||
|
return (
|
||||||
|
<div key={team.id}>
|
||||||
|
<ProjectSectionTitleWrapper>
|
||||||
|
<ProjectSectionTitle>{team.name}</ProjectSectionTitle>
|
||||||
|
</ProjectSectionTitleWrapper>
|
||||||
|
<ProjectList>
|
||||||
|
{team.projects.map((project, idx) => (
|
||||||
|
<ProjectListItem key={project.id}>
|
||||||
|
<ProjectTile color={colors[idx % 5]} to={`/projects/${project.id}`}>
|
||||||
|
<ProjectTileFade />
|
||||||
|
<ProjectTileDetails>
|
||||||
|
<ProjectTileName>{project.name}</ProjectTileName>
|
||||||
|
</ProjectTileDetails>
|
||||||
|
</ProjectTile>
|
||||||
|
</ProjectListItem>
|
||||||
|
))}
|
||||||
|
<ProjectListItem>
|
||||||
|
<ProjectAddTile
|
||||||
|
onClick={() => {
|
||||||
|
setShowNewProject(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProjectTileFade />
|
||||||
|
<ProjectAddTileDetails>
|
||||||
|
<ProjectTileName centered>Create new project</ProjectTileName>
|
||||||
|
</ProjectAddTileDetails>
|
||||||
|
</ProjectAddTile>
|
||||||
|
</ProjectListItem>
|
||||||
|
</ProjectList>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{showNewProject && (
|
{showNewProject && (
|
||||||
<NewProject
|
<NewProject
|
||||||
onCreateProject={(name, teamID) => {
|
onCreateProject={(name, teamID) => {
|
||||||
@ -93,6 +295,8 @@ const Projects = () => {
|
|||||||
teams={teams}
|
teams={teams}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</ProjectsContainer>
|
||||||
|
</Wrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ const InputWrapper = styled.div<{ width: string }>`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
margin-bottom: 2.2rem;
|
margin-bottom: 2.2rem;
|
||||||
margin-top: 17px;
|
margin-top: 24px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InputLabel = styled.span<{ width: string }>`
|
const InputLabel = styled.span<{ width: string }>`
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import Button from 'shared/components/Button';
|
||||||
|
|
||||||
export const Wrapper = styled.div`
|
export const Wrapper = styled.div`
|
||||||
background: #eff2f7;
|
background: #eff2f7;
|
||||||
@ -70,21 +71,7 @@ export const FormError = styled.span`
|
|||||||
color: rgb(234, 84, 85);
|
color: rgb(234, 84, 85);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const LoginButton = styled.input`
|
export const LoginButton = styled(Button)``;
|
||||||
padding: 0.75rem 2rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: var(--color-button-background);
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: var(--color-button-text-hover);
|
|
||||||
&:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: default;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const ActionButtons = styled.div`
|
export const ActionButtons = styled.div`
|
||||||
margin-top: 17.5px;
|
margin-top: 17.5px;
|
||||||
@ -92,15 +79,7 @@ export const ActionButtons = styled.div`
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RegisterButton = styled.button`
|
export const RegisterButton = styled(Button)``;
|
||||||
padding: 0.679rem 2rem;
|
|
||||||
border-radius: 6px;
|
|
||||||
border: 1px solid rgb(115, 103, 240);
|
|
||||||
background: transparent;
|
|
||||||
font-size: 1rem;
|
|
||||||
color: var(--color-primary);
|
|
||||||
cursor: pointer;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const LogoTitle = styled.div`
|
export const LogoTitle = styled.div`
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
|
@ -72,8 +72,10 @@ const Login = ({ onSubmit }: LoginProps) => {
|
|||||||
{errors.password && <FormError>{errors.password.message}</FormError>}
|
{errors.password && <FormError>{errors.password.message}</FormError>}
|
||||||
|
|
||||||
<ActionButtons>
|
<ActionButtons>
|
||||||
<RegisterButton>Register</RegisterButton>
|
<RegisterButton variant="outline">Register</RegisterButton>
|
||||||
<LoginButton type="submit" value="Login" disabled={!isComplete} />
|
<LoginButton type="submit" disabled={!isComplete}>
|
||||||
|
Login
|
||||||
|
</LoginButton>
|
||||||
</ActionButtons>
|
</ActionButtons>
|
||||||
</Form>
|
</Form>
|
||||||
</LoginFormContainer>
|
</LoginFormContainer>
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
|
|
||||||
type PopupContextState = {
|
type PopupContextState = {
|
||||||
show: (target: RefObject<HTMLElement>, content: JSX.Element, width?: string | number) => void;
|
show: (target: RefObject<HTMLElement>, content: JSX.Element, width?: string | number) => void;
|
||||||
setTab: (newTab: number) => void;
|
setTab: (newTab: number, width?: number | string) => void;
|
||||||
getCurrentTab: () => number;
|
getCurrentTab: () => number;
|
||||||
hide: () => void;
|
hide: () => void;
|
||||||
};
|
};
|
||||||
@ -139,12 +139,14 @@ export const PopupProvider: React.FC = ({ children }) => {
|
|||||||
};
|
};
|
||||||
const portalTarget = canUseDOM ? document.body : null; // appease flow
|
const portalTarget = canUseDOM ? document.body : null; // appease flow
|
||||||
|
|
||||||
const setTab = (newTab: number) => {
|
const setTab = (newTab: number, width?: number | string) => {
|
||||||
|
let newWidth = width ?? currentState.width;
|
||||||
setState((prevState: PopupState) => {
|
setState((prevState: PopupState) => {
|
||||||
return {
|
return {
|
||||||
...prevState,
|
...prevState,
|
||||||
previousTab: currentState.currentTab,
|
previousTab: currentState.currentTab,
|
||||||
currentTab: newTab,
|
currentTab: newTab,
|
||||||
|
width: newWidth,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import Button from 'shared/components/Button';
|
||||||
|
|
||||||
export const ListActionsWrapper = styled.ul`
|
export const ListActionsWrapper = styled.ul`
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
@ -36,16 +37,64 @@ export const ListSeparator = styled.hr`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type Props = {};
|
type Props = {
|
||||||
const ProjectSettings: React.FC<Props> = () => {
|
onDeleteProject: () => void;
|
||||||
|
};
|
||||||
|
const ProjectSettings: React.FC<Props> = ({ onDeleteProject }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ListActionsWrapper>
|
<ListActionsWrapper>
|
||||||
<ListActionItemWrapper onClick={() => {}}>
|
<ListActionItemWrapper onClick={() => onDeleteProject()}>
|
||||||
<ListActionItem>Delete Project</ListActionItem>
|
<ListActionItem>Delete Project</ListActionItem>
|
||||||
</ListActionItemWrapper>
|
</ListActionItemWrapper>
|
||||||
</ListActionsWrapper>
|
</ListActionsWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ConfirmWrapper = styled.div``;
|
||||||
|
|
||||||
|
const ConfirmSubTitle = styled.h3`
|
||||||
|
font-size: 14px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ConfirmDescription = styled.div`
|
||||||
|
font-size: 14px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DeleteList = styled.ul`
|
||||||
|
margin-bottom: 12px;
|
||||||
|
`;
|
||||||
|
const DeleteListItem = styled.li`
|
||||||
|
padding: 6px 0;
|
||||||
|
list-style: disc;
|
||||||
|
margin-left: 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ConfirmDeleteButton = styled(Button)`
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 12px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type DeleteProjectProps = {
|
||||||
|
name: string;
|
||||||
|
onDeleteProject: () => void;
|
||||||
|
};
|
||||||
|
const DeleteProject: React.FC<DeleteProjectProps> = ({ name, onDeleteProject }) => {
|
||||||
|
return (
|
||||||
|
<ConfirmWrapper>
|
||||||
|
<ConfirmDescription>
|
||||||
|
Deleting the project will also delete the following:
|
||||||
|
<DeleteList>
|
||||||
|
<DeleteListItem>Task groups and tasks</DeleteListItem>
|
||||||
|
</DeleteList>
|
||||||
|
</ConfirmDescription>
|
||||||
|
<ConfirmDeleteButton onClick={() => onDeleteProject()} color="danger">
|
||||||
|
Delete
|
||||||
|
</ConfirmDeleteButton>
|
||||||
|
</ConfirmWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { DeleteProject };
|
||||||
export default ProjectSettings;
|
export default ProjectSettings;
|
||||||
|
@ -78,6 +78,7 @@ export type Team = {
|
|||||||
id: Scalars['ID'];
|
id: Scalars['ID'];
|
||||||
createdAt: Scalars['Time'];
|
createdAt: Scalars['Time'];
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
|
members: Array<ProjectMember>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Project = {
|
export type Project = {
|
||||||
@ -145,8 +146,15 @@ export type FindTask = {
|
|||||||
taskID: Scalars['UUID'];
|
taskID: Scalars['UUID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Organization = {
|
||||||
|
__typename?: 'Organization';
|
||||||
|
id: Scalars['ID'];
|
||||||
|
name: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type Query = {
|
export type Query = {
|
||||||
__typename?: 'Query';
|
__typename?: 'Query';
|
||||||
|
organizations: Array<Organization>;
|
||||||
users: Array<UserAccount>;
|
users: Array<UserAccount>;
|
||||||
findUser: UserAccount;
|
findUser: UserAccount;
|
||||||
findProject: Project;
|
findProject: Project;
|
||||||
@ -192,7 +200,7 @@ export type NewUserAccount = {
|
|||||||
|
|
||||||
export type NewTeam = {
|
export type NewTeam = {
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
organizationID: Scalars['String'];
|
organizationID: Scalars['UUID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NewProject = {
|
export type NewProject = {
|
||||||
@ -390,13 +398,36 @@ export type UpdateTaskChecklistItemName = {
|
|||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CreateTeamMember = {
|
||||||
|
userID: Scalars['UUID'];
|
||||||
|
teamID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateTeamMemberPayload = {
|
||||||
|
__typename?: 'CreateTeamMemberPayload';
|
||||||
|
team: Team;
|
||||||
|
teamMember: ProjectMember;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeleteProject = {
|
||||||
|
projectID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DeleteProjectPayload = {
|
||||||
|
__typename?: 'DeleteProjectPayload';
|
||||||
|
ok: Scalars['Boolean'];
|
||||||
|
project: Project;
|
||||||
|
};
|
||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
__typename?: 'Mutation';
|
__typename?: 'Mutation';
|
||||||
createRefreshToken: RefreshToken;
|
createRefreshToken: RefreshToken;
|
||||||
createUserAccount: UserAccount;
|
createUserAccount: UserAccount;
|
||||||
createTeam: Team;
|
createTeam: Team;
|
||||||
clearProfileAvatar: UserAccount;
|
clearProfileAvatar: UserAccount;
|
||||||
|
createTeamMember: CreateTeamMemberPayload;
|
||||||
createProject: Project;
|
createProject: Project;
|
||||||
|
deleteProject: DeleteProjectPayload;
|
||||||
updateProjectName: Project;
|
updateProjectName: Project;
|
||||||
createProjectLabel: ProjectLabel;
|
createProjectLabel: ProjectLabel;
|
||||||
deleteProjectLabel: ProjectLabel;
|
deleteProjectLabel: ProjectLabel;
|
||||||
@ -443,11 +474,21 @@ export type MutationCreateTeamArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationCreateTeamMemberArgs = {
|
||||||
|
input: CreateTeamMember;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationCreateProjectArgs = {
|
export type MutationCreateProjectArgs = {
|
||||||
input: NewProject;
|
input: NewProject;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationDeleteProjectArgs = {
|
||||||
|
input: DeleteProject;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationUpdateProjectNameArgs = {
|
export type MutationUpdateProjectNameArgs = {
|
||||||
input?: Maybe<UpdateProjectName>;
|
input?: Maybe<UpdateProjectName>;
|
||||||
};
|
};
|
||||||
@ -869,7 +910,10 @@ export type GetProjectsQueryVariables = {};
|
|||||||
|
|
||||||
export type GetProjectsQuery = (
|
export type GetProjectsQuery = (
|
||||||
{ __typename?: 'Query' }
|
{ __typename?: 'Query' }
|
||||||
& { teams: Array<(
|
& { organizations: Array<(
|
||||||
|
{ __typename?: 'Organization' }
|
||||||
|
& Pick<Organization, 'id' | 'name'>
|
||||||
|
)>, teams: Array<(
|
||||||
{ __typename?: 'Team' }
|
{ __typename?: 'Team' }
|
||||||
& Pick<Team, 'id' | 'name' | 'createdAt'>
|
& Pick<Team, 'id' | 'name' | 'createdAt'>
|
||||||
)>, projects: Array<(
|
)>, projects: Array<(
|
||||||
@ -897,6 +941,23 @@ export type MeQuery = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type DeleteProjectMutationVariables = {
|
||||||
|
projectID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type DeleteProjectMutation = (
|
||||||
|
{ __typename?: 'Mutation' }
|
||||||
|
& { deleteProject: (
|
||||||
|
{ __typename?: 'DeleteProjectPayload' }
|
||||||
|
& Pick<DeleteProjectPayload, 'ok'>
|
||||||
|
& { project: (
|
||||||
|
{ __typename?: 'Project' }
|
||||||
|
& Pick<Project, 'id'>
|
||||||
|
) }
|
||||||
|
) }
|
||||||
|
);
|
||||||
|
|
||||||
export type CreateTaskChecklistItemMutationVariables = {
|
export type CreateTaskChecklistItemMutationVariables = {
|
||||||
taskChecklistID: Scalars['UUID'];
|
taskChecklistID: Scalars['UUID'];
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
@ -985,6 +1046,20 @@ export type UpdateTaskGroupNameMutation = (
|
|||||||
) }
|
) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export type CreateTeamMutationVariables = {
|
||||||
|
name: Scalars['String'];
|
||||||
|
organizationID: Scalars['UUID'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type CreateTeamMutation = (
|
||||||
|
{ __typename?: 'Mutation' }
|
||||||
|
& { createTeam: (
|
||||||
|
{ __typename?: 'Team' }
|
||||||
|
& Pick<Team, 'id' | 'createdAt' | 'name'>
|
||||||
|
) }
|
||||||
|
);
|
||||||
|
|
||||||
export type ToggleTaskLabelMutationVariables = {
|
export type ToggleTaskLabelMutationVariables = {
|
||||||
taskID: Scalars['UUID'];
|
taskID: Scalars['UUID'];
|
||||||
projectLabelID: Scalars['UUID'];
|
projectLabelID: Scalars['UUID'];
|
||||||
@ -1690,6 +1765,10 @@ export type FindTaskLazyQueryHookResult = ReturnType<typeof useFindTaskLazyQuery
|
|||||||
export type FindTaskQueryResult = ApolloReactCommon.QueryResult<FindTaskQuery, FindTaskQueryVariables>;
|
export type FindTaskQueryResult = ApolloReactCommon.QueryResult<FindTaskQuery, FindTaskQueryVariables>;
|
||||||
export const GetProjectsDocument = gql`
|
export const GetProjectsDocument = gql`
|
||||||
query getProjects {
|
query getProjects {
|
||||||
|
organizations {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
teams {
|
teams {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@ -1768,6 +1847,41 @@ 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 DeleteProjectDocument = gql`
|
||||||
|
mutation deleteProject($projectID: UUID!) {
|
||||||
|
deleteProject(input: {projectID: $projectID}) {
|
||||||
|
ok
|
||||||
|
project {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type DeleteProjectMutationFn = ApolloReactCommon.MutationFunction<DeleteProjectMutation, DeleteProjectMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useDeleteProjectMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useDeleteProjectMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useDeleteProjectMutation` 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 [deleteProjectMutation, { data, loading, error }] = useDeleteProjectMutation({
|
||||||
|
* variables: {
|
||||||
|
* projectID: // value for 'projectID'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useDeleteProjectMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<DeleteProjectMutation, DeleteProjectMutationVariables>) {
|
||||||
|
return ApolloReactHooks.useMutation<DeleteProjectMutation, DeleteProjectMutationVariables>(DeleteProjectDocument, baseOptions);
|
||||||
|
}
|
||||||
|
export type DeleteProjectMutationHookResult = ReturnType<typeof useDeleteProjectMutation>;
|
||||||
|
export type DeleteProjectMutationResult = ApolloReactCommon.MutationResult<DeleteProjectMutation>;
|
||||||
|
export type DeleteProjectMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteProjectMutation, DeleteProjectMutationVariables>;
|
||||||
export const CreateTaskChecklistItemDocument = gql`
|
export const CreateTaskChecklistItemDocument = gql`
|
||||||
mutation createTaskChecklistItem($taskChecklistID: UUID!, $name: String!, $position: Float!) {
|
mutation createTaskChecklistItem($taskChecklistID: UUID!, $name: String!, $position: Float!) {
|
||||||
createTaskChecklistItem(input: {taskChecklistID: $taskChecklistID, name: $name, position: $position}) {
|
createTaskChecklistItem(input: {taskChecklistID: $taskChecklistID, name: $name, position: $position}) {
|
||||||
@ -1980,6 +2094,41 @@ export function useUpdateTaskGroupNameMutation(baseOptions?: ApolloReactHooks.Mu
|
|||||||
export type UpdateTaskGroupNameMutationHookResult = ReturnType<typeof useUpdateTaskGroupNameMutation>;
|
export type UpdateTaskGroupNameMutationHookResult = ReturnType<typeof useUpdateTaskGroupNameMutation>;
|
||||||
export type UpdateTaskGroupNameMutationResult = ApolloReactCommon.MutationResult<UpdateTaskGroupNameMutation>;
|
export type UpdateTaskGroupNameMutationResult = ApolloReactCommon.MutationResult<UpdateTaskGroupNameMutation>;
|
||||||
export type UpdateTaskGroupNameMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskGroupNameMutation, UpdateTaskGroupNameMutationVariables>;
|
export type UpdateTaskGroupNameMutationOptions = ApolloReactCommon.BaseMutationOptions<UpdateTaskGroupNameMutation, UpdateTaskGroupNameMutationVariables>;
|
||||||
|
export const CreateTeamDocument = gql`
|
||||||
|
mutation createTeam($name: String!, $organizationID: UUID!) {
|
||||||
|
createTeam(input: {name: $name, organizationID: $organizationID}) {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type CreateTeamMutationFn = ApolloReactCommon.MutationFunction<CreateTeamMutation, CreateTeamMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useCreateTeamMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useCreateTeamMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useCreateTeamMutation` 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 [createTeamMutation, { data, loading, error }] = useCreateTeamMutation({
|
||||||
|
* variables: {
|
||||||
|
* name: // value for 'name'
|
||||||
|
* organizationID: // value for 'organizationID'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useCreateTeamMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<CreateTeamMutation, CreateTeamMutationVariables>) {
|
||||||
|
return ApolloReactHooks.useMutation<CreateTeamMutation, CreateTeamMutationVariables>(CreateTeamDocument, baseOptions);
|
||||||
|
}
|
||||||
|
export type CreateTeamMutationHookResult = ReturnType<typeof useCreateTeamMutation>;
|
||||||
|
export type CreateTeamMutationResult = ApolloReactCommon.MutationResult<CreateTeamMutation>;
|
||||||
|
export type CreateTeamMutationOptions = ApolloReactCommon.BaseMutationOptions<CreateTeamMutation, CreateTeamMutationVariables>;
|
||||||
export const ToggleTaskLabelDocument = gql`
|
export const ToggleTaskLabelDocument = gql`
|
||||||
mutation toggleTaskLabel($taskID: UUID!, $projectLabelID: UUID!) {
|
mutation toggleTaskLabel($taskID: UUID!, $projectLabelID: UUID!) {
|
||||||
toggleTaskLabel(input: {taskID: $taskID, projectLabelID: $projectLabelID}) {
|
toggleTaskLabel(input: {taskID: $taskID, projectLabelID: $projectLabelID}) {
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
query getProjects {
|
query getProjects {
|
||||||
|
organizations {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
teams {
|
teams {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
14
web/src/shared/graphql/project/deleteProject.ts
Normal file
14
web/src/shared/graphql/project/deleteProject.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export const DELETE_PROJECT_MUTATION = gql`
|
||||||
|
mutation deleteProject($projectID: UUID!) {
|
||||||
|
deleteProject(input: { projectID: $projectID }) {
|
||||||
|
ok
|
||||||
|
project {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default DELETE_PROJECT_MUTATION;
|
13
web/src/shared/graphql/team/createTeam.ts
Normal file
13
web/src/shared/graphql/team/createTeam.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
export const CREATE_TEAM_MUTATION = gql`
|
||||||
|
mutation createTeam($name: String!, $organizationID: UUID!) {
|
||||||
|
createTeam(input: { name: $name, organizationID: $organizationID }) {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default CREATE_TEAM_MUTATION;
|
Loading…
Reference in New Issue
Block a user