From 57382de9d0c33232bd5db33d5a7b9d860ef4e9ae Mon Sep 17 00:00:00 2001 From: Jordan Knott Date: Tue, 23 Jun 2020 15:20:53 -0500 Subject: [PATCH] feature: remove sidebar & redesign top navbar --- .tmuxinator.yml | 27 + api/cmd/citadel/main.go | 8 +- api/cmd/citadelctl/main.go | 56 +- api/docker-compose.yml | 8 + api/go.mod | 8 + api/go.sum | 129 +++ api/graph/generated.go | 1007 ++++++++++++++++- api/graph/models_gen.go | 39 +- api/graph/schema.graphqls | 41 +- api/graph/schema.resolvers.go | 80 +- ...cade-delete-to_project_team_id_fkey.up.sql | 6 + ...elete-to-task_assigned_user_id_fkey.up.sql | 6 + ...y-to-user_id-on-refresh_token-table.up.sql | 5 + api/pg/pg.go | 4 + api/pg/querier.go | 4 + api/pg/task_checklist.sql.go | 49 + api/pg/user_accounts.sql.go | 9 + api/query/task_checklist.sql | 10 + api/query/user_accounts.sql | 3 + web/package.json | 1 + web/report.20200621.183857.68808.0.001.json | 471 ++++++++ web/src/Admin/index.tsx | 158 +++ web/src/App/Navbar.tsx | 2 +- web/src/App/Routes.tsx | 6 +- web/src/App/TopNavbar.tsx | 283 ++++- web/src/App/index.tsx | 13 +- web/src/Projects/Project/Details/index.tsx | 258 ++++- web/src/Projects/Project/index.tsx | 80 +- web/src/Projects/index.tsx | 73 +- web/src/Teams/index.tsx | 227 ++++ web/src/citadel.d.ts | 9 + web/src/projects.d.ts | 10 + .../shared/components/Admin/Admin.stories.tsx | 25 +- web/src/shared/components/Admin/index.tsx | 118 +- web/src/shared/components/Card/index.tsx | 2 +- web/src/shared/components/Checklist/index.tsx | 6 +- .../DropdownMenu/DropdownMenu.stories.tsx | 1 + .../shared/components/DropdownMenu/index.tsx | 16 +- web/src/shared/components/Input/index.tsx | 7 +- web/src/shared/components/Lists/Styles.ts | 1 + web/src/shared/components/Lists/index.tsx | 1 + web/src/shared/components/Login/index.tsx | 4 +- .../components/Navbar/Navbar.stories.tsx | 4 +- web/src/shared/components/Navbar/index.tsx | 2 +- .../NewProject/NewProject.stories.tsx | 1 + .../shared/components/NewProject/index.tsx | 5 +- .../components/PopupMenu/LabelManager.tsx | 2 +- web/src/shared/components/PopupMenu/Styles.ts | 12 +- .../shared/components/ProfileIcon/index.tsx | 1 - .../components/ProjectSettings/index.tsx | 46 +- .../TaskDetails/TaskDetails.stories.tsx | 3 + .../shared/components/TaskDetails/index.tsx | 23 +- web/src/shared/components/TopNavbar/Styles.ts | 48 +- .../TopNavbar/TopNavbar.stories.tsx | 4 +- web/src/shared/components/TopNavbar/index.tsx | 134 ++- web/src/shared/generated/graphql.tsx | 672 +++++++++-- web/src/shared/graphql/createTask.graphqls | 38 - web/src/shared/graphql/findTask.graphqls | 6 + web/src/shared/graphql/fragments/task.ts | 8 + web/src/shared/graphql/task/createTask.ts | 13 + .../graphql/task/createTaskChecklist.ts | 20 + .../graphql/task/deleteTaskChecklist.ts | 14 + .../task/setTaskChecklistItemComplete.ts | 3 - .../graphql/task/updateTaskChecklistName.ts | 19 + web/src/shared/graphql/team/deleteTeam.ts | 14 + web/src/shared/graphql/team/getTeam.ts | 21 + web/src/shared/graphql/user/createUser.ts | 28 + web/src/shared/graphql/users.graphqls | 14 + web/src/shared/icons/BarChart.tsx | 12 + web/src/shared/icons/Citadel.tsx | 26 +- web/src/shared/icons/Filter.tsx | 12 + web/src/shared/icons/Home.tsx | 21 +- web/src/shared/icons/Icon.tsx | 1 + web/src/shared/icons/Lock.tsx | 19 +- web/src/shared/icons/Pencil.tsx | 21 +- web/src/shared/icons/Sort.tsx | 12 + web/src/shared/icons/index.ts | 6 + web/yarn.lock | 33 +- 78 files changed, 4124 insertions(+), 465 deletions(-) create mode 100644 .tmuxinator.yml create mode 100644 api/docker-compose.yml create mode 100644 api/migrations/0034_add-cascade-delete-to_project_team_id_fkey.up.sql create mode 100644 api/migrations/0035_add-cascade-delete-to-task_assigned_user_id_fkey.up.sql create mode 100644 api/migrations/0036-add-fkey-to-user_id-on-refresh_token-table.up.sql create mode 100644 web/report.20200621.183857.68808.0.001.json create mode 100644 web/src/Admin/index.tsx create mode 100644 web/src/Teams/index.tsx delete mode 100644 web/src/shared/graphql/createTask.graphqls create mode 100644 web/src/shared/graphql/task/createTask.ts create mode 100644 web/src/shared/graphql/task/createTaskChecklist.ts create mode 100644 web/src/shared/graphql/task/deleteTaskChecklist.ts create mode 100644 web/src/shared/graphql/task/updateTaskChecklistName.ts create mode 100644 web/src/shared/graphql/team/deleteTeam.ts create mode 100644 web/src/shared/graphql/team/getTeam.ts create mode 100644 web/src/shared/graphql/user/createUser.ts create mode 100644 web/src/shared/graphql/users.graphqls create mode 100644 web/src/shared/icons/BarChart.tsx create mode 100644 web/src/shared/icons/Filter.tsx create mode 100644 web/src/shared/icons/Sort.tsx diff --git a/.tmuxinator.yml b/.tmuxinator.yml new file mode 100644 index 0000000..b023a6a --- /dev/null +++ b/.tmuxinator.yml @@ -0,0 +1,27 @@ +name: citadel +root: . + +on_project_start: docker start test-db + +windows: + - services: + root: ./ + panes: + - api: + - cd api + - go run cmd/citadel/main.go + - yarn: + - cd web + - yarn start + - web/editor: + root: ./web + panes: + - vim src/index.tsx + - api/editor: + root: ./api + panes: + - vim cmd/citadel/main.go + - database: + root: ./api + panes: + - docker container exec -it test-db psql -U postgres citadel diff --git a/api/cmd/citadel/main.go b/api/cmd/citadel/main.go index 2b3e63d..fe9fc67 100644 --- a/api/cmd/citadel/main.go +++ b/api/cmd/citadel/main.go @@ -1,10 +1,10 @@ package main import ( - _ "github.com/lib/pq" - "fmt" + _ "github.com/lib/pq" "net/http" + "time" "github.com/jmoiron/sqlx" "github.com/jordanknott/project-citadel/api/router" @@ -20,6 +20,10 @@ func main() { if err != nil { log.Panic(err) } + db.SetMaxOpenConns(25) + db.SetMaxIdleConns(25) + db.SetConnMaxLifetime(5 * time.Minute) + defer db.Close() fmt.Println("starting graphql server on http://localhost:3333") fmt.Println("starting graphql playground on http://localhost:3333/__graphql") diff --git a/api/cmd/citadelctl/main.go b/api/cmd/citadelctl/main.go index 2c88681..be5cbf5 100644 --- a/api/cmd/citadelctl/main.go +++ b/api/cmd/citadelctl/main.go @@ -1,16 +1,16 @@ package main import ( - "context" - "fmt" - "io/ioutil" + // "context" + // "fmt" + // "io/ioutil" + "github.com/jordan-wright/email" _ "github.com/lib/pq" - - "github.com/jmoiron/sqlx" - "github.com/jordanknott/project-citadel/api/pg" - - "github.com/BurntSushi/toml" + "net/smtp" + // "github.com/jmoiron/sqlx" + // "github.com/jordanknott/project-citadel/api/pg" + // "github.com/BurntSushi/toml" // "github.com/jordanknott/project-citadel/api/router" // "time" ) @@ -26,6 +26,14 @@ type colors struct { } func main() { + + e := email.NewEmail() + e.From = "Jordan Knott " + e.To = []string{"jordan@jordanthedev.com"} + e.Subject = "Jordan Knott (@jordanthedev) invited you to join the team \"Paradox\" on Citadel" + e.Text = []byte("Text Body is, of course, supported!") + e.HTML = []byte("

Fancy HTML is supported, too!

") + e.Send("localhost:1025", smtp.PlainAuth("", "test@gmail.com", "password123", "localhost")) // dur := time.Hour * 24 * 7 * 30 // token, err := router.NewAccessTokenCustomExpiration("21345076-6423-4a00-a6bd-cd9f830e2764", dur) // if err != nil { @@ -33,23 +41,23 @@ func main() { // } // fmt.Println(token) - fmt.Println("seeding database...") + // fmt.Println("seeding database...") - dat, err := ioutil.ReadFile("data/colors.toml") - if err != nil { - panic(err) - } + // dat, err := ioutil.ReadFile("data/colors.toml") + // if err != nil { + // panic(err) + // } - var labelColors colors - _, err = toml.Decode(string(dat), &labelColors) - if err != nil { - panic(err) - } - db, err := sqlx.Connect("postgres", "user=postgres password=test host=0.0.0.0 dbname=citadel sslmode=disable") - repository := pg.NewRepository(db) - for _, color := range labelColors.Color { - fmt.Printf("%v\n", color) - repository.CreateLabelColor(context.Background(), pg.CreateLabelColorParams{color.Name, color.Color, float64(color.Position)}) - } + // var labelColors colors + // _, err = toml.Decode(string(dat), &labelColors) + // if err != nil { + // panic(err) + // } + // db, err := sqlx.Connect("postgres", "user=postgres password=test host=0.0.0.0 dbname=citadel sslmode=disable") + // repository := pg.NewRepository(db) + // for _, color := range labelColors.Color { + // fmt.Printf("%v\n", color) + // repository.CreateLabelColor(context.Background(), pg.CreateLabelColorParams{color.Name, color.Color, float64(color.Position)}) + // } } diff --git a/api/docker-compose.yml b/api/docker-compose.yml new file mode 100644 index 0000000..ff5f467 --- /dev/null +++ b/api/docker-compose.yml @@ -0,0 +1,8 @@ +version: "3" +services: + mailhog: + image: mailhog/mailhog:latest + restart: always + ports: + - 1025:1025 + - 8025:8025 diff --git a/api/go.mod b/api/go.mod index d54cc3c..3fa59eb 100644 --- a/api/go.mod +++ b/api/go.mod @@ -5,17 +5,25 @@ go 1.13 require ( github.com/99designs/gqlgen v0.11.3 github.com/BurntSushi/toml v0.3.1 + github.com/boyter/scc v2.12.0+incompatible // indirect + github.com/dbaggerman/cuba v0.3.2 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-chi/chi v3.3.2+incompatible github.com/go-chi/cors v1.0.0 github.com/google/martian v2.1.0+incompatible github.com/google/uuid v1.1.1 github.com/jmoiron/sqlx v1.2.0 + github.com/jordan-wright/email v0.0.0-20200602115436-fd8a7622303e github.com/lib/pq v1.0.0 + github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200525100937-58356a36e03f // indirect github.com/pelletier/go-toml v1.8.0 github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.2 + github.com/spf13/cobra v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/urfave/cli v1.20.0 // indirect github.com/vektah/gqlparser/v2 v2.0.1 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 + golang.org/x/text v0.3.3 // indirect ) diff --git a/api/go.sum b/api/go.sum index b250ca3..00aaa14 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,29 +1,64 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/99designs/gqlgen v0.11.1 h1:QoSL8/AAJ2T3UOeQbdnBR32JcG4pO08+P/g5jdbFkUg= github.com/99designs/gqlgen v0.11.1/go.mod h1:vjFOyBZ7NwDl+GdSD4PFn7BQn5Fy7ohJwXn7Vk8zz+c= github.com/99designs/gqlgen v0.11.3 h1:oFSxl1DFS9X///uHV3y6CEfpcXWrDUxVblR4Xib2bs4= github.com/99designs/gqlgen v0.11.3/go.mod h1:RgX5GRRdDWNkh4pBrdzNpNPFVsdoUFY2+adM6nb1N+4= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0= github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/boyter/scc v2.12.0+incompatible h1:Sxhi1Ry3gdreoaF0xEITQSdjR+pJT0cXHXhlDw5FHT0= +github.com/boyter/scc v2.12.0+incompatible/go.mod h1:VB5w4e0dahmIiKnpZ7LRh/sjauoY0BmCWjIzZcShNY0= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dbaggerman/cuba v0.3.2 h1:6ZbQX3FNvkocR222YyoAIZ8wi4avrb7JcJkPvVI2AS4= +github.com/dbaggerman/cuba v0.3.2/go.mod h1:t9Oo05XRZGcjaVqsA/gFeNAOm7DYZYNhI17unI5FlwY= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ= github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/cors v1.0.0 h1:e6x8k7uWbUwYs+aXDoiUzeQFT6l0cygBYyNhD7/1Tg0= github.com/go-chi/cors v1.0.0/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= @@ -32,12 +67,28 @@ github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jordan-wright/email v0.0.0-20200602115436-fd8a7622303e h1:OGunVjqY7y4U4laftpEHv+mvZBlr7UGimJXKEGQtg48= +github.com/jordan-wright/email v0.0.0-20200602115436-fd8a7622303e/go.mod h1:Fy2gCFfZhay8jplf/Csj6cyH/oshQTkLQYZbKkcV+SY= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -46,6 +97,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg= github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -53,16 +105,39 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8= github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/monochromegane/go-gitignore v0.0.0-20200525100937-58356a36e03f h1:0HN0GKijN4mr9SmYoW/Ni3ozuNeHiSxo2s7drhv7obY= +github.com/monochromegane/go-gitignore v0.0.0-20200525100937-58356a36e03f/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -71,14 +146,30 @@ github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= @@ -87,15 +178,36 @@ github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWp github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o= github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -104,18 +216,35 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtD golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM= golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= diff --git a/api/graph/generated.go b/api/graph/generated.go index c6ea037..4da3d2c 100644 --- a/api/graph/generated.go +++ b/api/graph/generated.go @@ -77,6 +77,11 @@ type ComplexityRoot struct { TaskChecklistItem func(childComplexity int) int } + DeleteTaskChecklistPayload struct { + Ok func(childComplexity int) int + TaskChecklist func(childComplexity int) int + } + DeleteTaskGroupPayload struct { AffectedRows func(childComplexity int) int Ok func(childComplexity int) int @@ -87,6 +92,17 @@ type ComplexityRoot struct { TaskID func(childComplexity int) int } + DeleteTeamPayload struct { + Ok func(childComplexity int) int + Projects func(childComplexity int) int + Team func(childComplexity int) int + } + + DeleteUserAccountPayload struct { + Ok func(childComplexity int) int + UserAccount func(childComplexity int) int + } + LabelColor struct { ColorHex func(childComplexity int) int ID func(childComplexity int) int @@ -111,8 +127,11 @@ type ComplexityRoot struct { DeleteProject func(childComplexity int, input DeleteProject) int DeleteProjectLabel func(childComplexity int, input DeleteProjectLabel) int DeleteTask func(childComplexity int, input DeleteTaskInput) int + DeleteTaskChecklist func(childComplexity int, input DeleteTaskChecklist) int DeleteTaskChecklistItem func(childComplexity int, input DeleteTaskChecklistItem) int DeleteTaskGroup func(childComplexity int, input DeleteTaskGroupInput) int + DeleteTeam func(childComplexity int, input DeleteTeam) int + DeleteUserAccount func(childComplexity int, input DeleteUserAccount) int LogoutUser func(childComplexity int, input LogoutUser) int RemoveTaskLabel func(childComplexity int, input *RemoveTaskLabelInput) int SetTaskChecklistItemComplete func(childComplexity int, input SetTaskChecklistItemComplete) int @@ -124,6 +143,7 @@ type ComplexityRoot struct { UpdateProjectLabelName func(childComplexity int, input UpdateProjectLabelName) int UpdateProjectName func(childComplexity int, input *UpdateProjectName) int UpdateTaskChecklistItemName func(childComplexity int, input UpdateTaskChecklistItemName) int + UpdateTaskChecklistName func(childComplexity int, input UpdateTaskChecklistName) int UpdateTaskDescription func(childComplexity int, input UpdateTaskDescriptionInput) int UpdateTaskDueDate func(childComplexity int, input UpdateTaskDueDate) int UpdateTaskGroupLocation func(childComplexity int, input NewTaskGroupLocation) int @@ -170,6 +190,7 @@ type ComplexityRoot struct { Query struct { FindProject func(childComplexity int, input FindProject) int FindTask func(childComplexity int, input FindTask) int + FindTeam func(childComplexity int, input FindTeam) int FindUser func(childComplexity int, input FindUser) int LabelColors func(childComplexity int) int Me func(childComplexity int) int @@ -271,6 +292,8 @@ type LabelColorResolver interface { type MutationResolver interface { CreateRefreshToken(ctx context.Context, input NewRefreshToken) (*pg.RefreshToken, error) CreateUserAccount(ctx context.Context, input NewUserAccount) (*pg.UserAccount, error) + DeleteUserAccount(ctx context.Context, input DeleteUserAccount) (*DeleteUserAccountPayload, error) + DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) CreateTeam(ctx context.Context, input NewTeam) (*pg.Team, error) ClearProfileAvatar(ctx context.Context) (*pg.UserAccount, error) CreateTeamMember(ctx context.Context, input CreateTeamMember) (*CreateTeamMemberPayload, error) @@ -290,6 +313,8 @@ type MutationResolver interface { RemoveTaskLabel(ctx context.Context, input *RemoveTaskLabelInput) (*pg.Task, error) ToggleTaskLabel(ctx context.Context, input ToggleTaskLabelInput) (*ToggleTaskLabelPayload, error) CreateTaskChecklist(ctx context.Context, input CreateTaskChecklist) (*pg.TaskChecklist, error) + DeleteTaskChecklist(ctx context.Context, input DeleteTaskChecklist) (*DeleteTaskChecklistPayload, error) + UpdateTaskChecklistName(ctx context.Context, input UpdateTaskChecklistName) (*pg.TaskChecklist, error) CreateTaskChecklistItem(ctx context.Context, input CreateTaskChecklistItem) (*pg.TaskChecklistItem, error) UpdateTaskChecklistItemName(ctx context.Context, input UpdateTaskChecklistItemName) (*pg.TaskChecklistItem, error) SetTaskChecklistItemComplete(ctx context.Context, input SetTaskChecklistItemComplete) (*pg.TaskChecklistItem, error) @@ -330,6 +355,7 @@ type QueryResolver interface { FindProject(ctx context.Context, input FindProject) (*pg.Project, error) FindTask(ctx context.Context, input FindTask) (*pg.Task, error) Projects(ctx context.Context, input *ProjectsFilter) ([]pg.Project, error) + FindTeam(ctx context.Context, input FindTeam) (*pg.Team, error) Teams(ctx context.Context) ([]pg.Team, error) LabelColors(ctx context.Context) ([]pg.LabelColor, error) TaskGroups(ctx context.Context) ([]pg.TaskGroup, error) @@ -452,6 +478,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.DeleteTaskChecklistItemPayload.TaskChecklistItem(childComplexity), true + case "DeleteTaskChecklistPayload.ok": + if e.complexity.DeleteTaskChecklistPayload.Ok == nil { + break + } + + return e.complexity.DeleteTaskChecklistPayload.Ok(childComplexity), true + + case "DeleteTaskChecklistPayload.taskChecklist": + if e.complexity.DeleteTaskChecklistPayload.TaskChecklist == nil { + break + } + + return e.complexity.DeleteTaskChecklistPayload.TaskChecklist(childComplexity), true + case "DeleteTaskGroupPayload.affectedRows": if e.complexity.DeleteTaskGroupPayload.AffectedRows == nil { break @@ -480,6 +520,41 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.DeleteTaskPayload.TaskID(childComplexity), true + case "DeleteTeamPayload.ok": + if e.complexity.DeleteTeamPayload.Ok == nil { + break + } + + return e.complexity.DeleteTeamPayload.Ok(childComplexity), true + + case "DeleteTeamPayload.projects": + if e.complexity.DeleteTeamPayload.Projects == nil { + break + } + + return e.complexity.DeleteTeamPayload.Projects(childComplexity), true + + case "DeleteTeamPayload.team": + if e.complexity.DeleteTeamPayload.Team == nil { + break + } + + return e.complexity.DeleteTeamPayload.Team(childComplexity), true + + case "DeleteUserAccountPayload.ok": + if e.complexity.DeleteUserAccountPayload.Ok == nil { + break + } + + return e.complexity.DeleteUserAccountPayload.Ok(childComplexity), true + + case "DeleteUserAccountPayload.userAccount": + if e.complexity.DeleteUserAccountPayload.UserAccount == nil { + break + } + + return e.complexity.DeleteUserAccountPayload.UserAccount(childComplexity), true + case "LabelColor.colorHex": if e.complexity.LabelColor.ColorHex == nil { break @@ -695,6 +770,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.DeleteTask(childComplexity, args["input"].(DeleteTaskInput)), true + case "Mutation.deleteTaskChecklist": + if e.complexity.Mutation.DeleteTaskChecklist == nil { + break + } + + args, err := ec.field_Mutation_deleteTaskChecklist_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteTaskChecklist(childComplexity, args["input"].(DeleteTaskChecklist)), true + case "Mutation.deleteTaskChecklistItem": if e.complexity.Mutation.DeleteTaskChecklistItem == nil { break @@ -719,6 +806,30 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.DeleteTaskGroup(childComplexity, args["input"].(DeleteTaskGroupInput)), true + case "Mutation.deleteTeam": + if e.complexity.Mutation.DeleteTeam == nil { + break + } + + args, err := ec.field_Mutation_deleteTeam_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteTeam(childComplexity, args["input"].(DeleteTeam)), true + + case "Mutation.deleteUserAccount": + if e.complexity.Mutation.DeleteUserAccount == nil { + break + } + + args, err := ec.field_Mutation_deleteUserAccount_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteUserAccount(childComplexity, args["input"].(DeleteUserAccount)), true + case "Mutation.logoutUser": if e.complexity.Mutation.LogoutUser == nil { break @@ -851,6 +962,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.UpdateTaskChecklistItemName(childComplexity, args["input"].(UpdateTaskChecklistItemName)), true + case "Mutation.updateTaskChecklistName": + if e.complexity.Mutation.UpdateTaskChecklistName == nil { + break + } + + args, err := ec.field_Mutation_updateTaskChecklistName_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.UpdateTaskChecklistName(childComplexity, args["input"].(UpdateTaskChecklistName)), true + case "Mutation.updateTaskDescription": if e.complexity.Mutation.UpdateTaskDescription == nil { break @@ -1087,6 +1210,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.FindTask(childComplexity, args["input"].(FindTask)), true + case "Query.findTeam": + if e.complexity.Query.FindTeam == nil { + break + } + + args, err := ec.field_Query_findTeam_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.FindTeam(childComplexity, args["input"].(FindTeam)), true + case "Query.findUser": if e.complexity.Query.FindUser == nil { break @@ -1679,7 +1814,7 @@ type Task { } input ProjectsFilter { - teamID: String + teamID: UUID } input FindUser { @@ -1699,6 +1834,10 @@ type Organization { name: String! } +input FindTeam { + teamID: UUID! +} + type Query { organizations: [Organization!]! users: [UserAccount!]! @@ -1706,6 +1845,7 @@ type Query { findProject(input: FindProject!): Project! findTask(input: FindTask!): Task! projects(input: ProjectsFilter): [Project!]! + findTeam(input: FindTeam!): Team! teams: [Team!]! labelColors: [LabelColor!]! taskGroups: [TaskGroup!]! @@ -1932,11 +2072,43 @@ type DeleteProjectPayload { project: Project! } +input DeleteTeam { + teamID: UUID! +} + +type DeleteTeamPayload { + ok: Boolean! + team: Team! + projects: [Project!]! +} + +input DeleteUserAccount { + userID: UUID! +} + +type DeleteUserAccountPayload { + ok: Boolean! + userAccount: UserAccount! +} + +input UpdateTaskChecklistName { + taskChecklistID: UUID! + name: String! +} +input DeleteTaskChecklist { + taskChecklistID: UUID! +} +type DeleteTaskChecklistPayload { + ok: Boolean! + taskChecklist: TaskChecklist! +} type Mutation { createRefreshToken(input: NewRefreshToken!): RefreshToken! createUserAccount(input: NewUserAccount!): UserAccount! + deleteUserAccount(input: DeleteUserAccount!): DeleteUserAccountPayload! + deleteTeam(input: DeleteTeam!): DeleteTeamPayload! createTeam(input: NewTeam!): Team! clearProfileAvatar: UserAccount! @@ -1962,6 +2134,8 @@ type Mutation { toggleTaskLabel(input: ToggleTaskLabelInput!): ToggleTaskLabelPayload! createTaskChecklist(input: CreateTaskChecklist!): TaskChecklist! + deleteTaskChecklist(input: DeleteTaskChecklist!): DeleteTaskChecklistPayload! + updateTaskChecklistName(input: UpdateTaskChecklistName!): TaskChecklist! createTaskChecklistItem(input: CreateTaskChecklistItem!): TaskChecklistItem! updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): TaskChecklistItem! setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): TaskChecklistItem! @@ -2197,6 +2371,20 @@ func (ec *executionContext) field_Mutation_deleteTaskChecklistItem_args(ctx cont return args, nil } +func (ec *executionContext) field_Mutation_deleteTaskChecklist_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 DeleteTaskChecklist + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalNDeleteTaskChecklist2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskChecklist(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_deleteTaskGroup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -2225,6 +2413,34 @@ func (ec *executionContext) field_Mutation_deleteTask_args(ctx context.Context, return args, nil } +func (ec *executionContext) field_Mutation_deleteTeam_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 DeleteTeam + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalNDeleteTeam2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTeam(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_deleteUserAccount_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 DeleteUserAccount + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalNDeleteUserAccount2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteUserAccount(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_logoutUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -2379,6 +2595,20 @@ func (ec *executionContext) field_Mutation_updateTaskChecklistItemName_args(ctx return args, nil } +func (ec *executionContext) field_Mutation_updateTaskChecklistName_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 UpdateTaskChecklistName + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalNUpdateTaskChecklistName2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskChecklistName(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_updateTaskDescription_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -2505,6 +2735,20 @@ func (ec *executionContext) field_Query_findTask_args(ctx context.Context, rawAr return args, nil } +func (ec *executionContext) field_Query_findTeam_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 FindTeam + if tmp, ok := rawArgs["input"]; ok { + arg0, err = ec.unmarshalNFindTeam2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐFindTeam(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_findUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -2841,6 +3085,74 @@ func (ec *executionContext) _DeleteTaskChecklistItemPayload_taskChecklistItem(ct return ec.marshalNTaskChecklistItem2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskChecklistItem(ctx, field.Selections, res) } +func (ec *executionContext) _DeleteTaskChecklistPayload_ok(ctx context.Context, field graphql.CollectedField, obj *DeleteTaskChecklistPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTaskChecklistPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Ok, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) _DeleteTaskChecklistPayload_taskChecklist(ctx context.Context, field graphql.CollectedField, obj *DeleteTaskChecklistPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTaskChecklistPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.TaskChecklist, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*pg.TaskChecklist) + fc.Result = res + return ec.marshalNTaskChecklist2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskChecklist(ctx, field.Selections, res) +} + func (ec *executionContext) _DeleteTaskGroupPayload_ok(ctx context.Context, field graphql.CollectedField, obj *DeleteTaskGroupPayload) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -2977,6 +3289,176 @@ func (ec *executionContext) _DeleteTaskPayload_taskID(ctx context.Context, field return ec.marshalNString2string(ctx, field.Selections, res) } +func (ec *executionContext) _DeleteTeamPayload_ok(ctx context.Context, field graphql.CollectedField, obj *DeleteTeamPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTeamPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Ok, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) _DeleteTeamPayload_team(ctx context.Context, field graphql.CollectedField, obj *DeleteTeamPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTeamPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Team, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*pg.Team) + fc.Result = res + return ec.marshalNTeam2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTeam(ctx, field.Selections, res) +} + +func (ec *executionContext) _DeleteTeamPayload_projects(ctx context.Context, field graphql.CollectedField, obj *DeleteTeamPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteTeamPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Projects, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]pg.Project) + fc.Result = res + return ec.marshalNProject2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐProjectᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) _DeleteUserAccountPayload_ok(ctx context.Context, field graphql.CollectedField, obj *DeleteUserAccountPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteUserAccountPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Ok, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) _DeleteUserAccountPayload_userAccount(ctx context.Context, field graphql.CollectedField, obj *DeleteUserAccountPayload) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "DeleteUserAccountPayload", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.UserAccount, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*pg.UserAccount) + fc.Result = res + return ec.marshalNUserAccount2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐUserAccount(ctx, field.Selections, res) +} + func (ec *executionContext) _LabelColor_id(ctx context.Context, field graphql.CollectedField, obj *pg.LabelColor) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -3195,6 +3677,88 @@ func (ec *executionContext) _Mutation_createUserAccount(ctx context.Context, fie return ec.marshalNUserAccount2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐUserAccount(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_deleteUserAccount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_deleteUserAccount_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteUserAccount(rctx, args["input"].(DeleteUserAccount)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*DeleteUserAccountPayload) + fc.Result = res + return ec.marshalNDeleteUserAccountPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteUserAccountPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_deleteTeam(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_deleteTeam_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteTeam(rctx, args["input"].(DeleteTeam)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*DeleteTeamPayload) + fc.Result = res + return ec.marshalNDeleteTeamPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTeamPayload(ctx, field.Selections, res) +} + func (ec *executionContext) _Mutation_createTeam(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -3967,6 +4531,88 @@ func (ec *executionContext) _Mutation_createTaskChecklist(ctx context.Context, f return ec.marshalNTaskChecklist2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskChecklist(ctx, field.Selections, res) } +func (ec *executionContext) _Mutation_deleteTaskChecklist(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_deleteTaskChecklist_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteTaskChecklist(rctx, args["input"].(DeleteTaskChecklist)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*DeleteTaskChecklistPayload) + fc.Result = res + return ec.marshalNDeleteTaskChecklistPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskChecklistPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_updateTaskChecklistName(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_updateTaskChecklistName_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().UpdateTaskChecklistName(rctx, args["input"].(UpdateTaskChecklistName)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*pg.TaskChecklist) + fc.Result = res + return ec.marshalNTaskChecklist2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTaskChecklist(ctx, field.Selections, res) +} + func (ec *executionContext) _Mutation_createTaskChecklistItem(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -5441,6 +6087,47 @@ func (ec *executionContext) _Query_projects(ctx context.Context, field graphql.C return ec.marshalNProject2ᚕgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐProjectᚄ(ctx, field.Selections, res) } +func (ec *executionContext) _Query_findTeam(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + + ctx = graphql.WithFieldContext(ctx, fc) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query_findTeam_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + fc.Args = args + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().FindTeam(rctx, args["input"].(FindTeam)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*pg.Team) + fc.Result = res + return ec.marshalNTeam2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋpgᚐTeam(ctx, field.Selections, res) +} + func (ec *executionContext) _Query_teams(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -8594,6 +9281,24 @@ func (ec *executionContext) unmarshalInputDeleteProjectLabel(ctx context.Context return it, nil } +func (ec *executionContext) unmarshalInputDeleteTaskChecklist(ctx context.Context, obj interface{}) (DeleteTaskChecklist, error) { + var it DeleteTaskChecklist + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "taskChecklistID": + var err error + it.TaskChecklistID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputDeleteTaskChecklistItem(ctx context.Context, obj interface{}) (DeleteTaskChecklistItem, error) { var it DeleteTaskChecklistItem var asMap = obj.(map[string]interface{}) @@ -8648,6 +9353,42 @@ func (ec *executionContext) unmarshalInputDeleteTaskInput(ctx context.Context, o return it, nil } +func (ec *executionContext) unmarshalInputDeleteTeam(ctx context.Context, obj interface{}) (DeleteTeam, error) { + var it DeleteTeam + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "teamID": + var err error + it.TeamID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + +func (ec *executionContext) unmarshalInputDeleteUserAccount(ctx context.Context, obj interface{}) (DeleteUserAccount, error) { + var it DeleteUserAccount + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "userID": + var err error + it.UserID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputFindProject(ctx context.Context, obj interface{}) (FindProject, error) { var it FindProject var asMap = obj.(map[string]interface{}) @@ -8684,6 +9425,24 @@ func (ec *executionContext) unmarshalInputFindTask(ctx context.Context, obj inte return it, nil } +func (ec *executionContext) unmarshalInputFindTeam(ctx context.Context, obj interface{}) (FindTeam, error) { + var it FindTeam + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "teamID": + var err error + it.TeamID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputFindUser(ctx context.Context, obj interface{}) (FindUser, error) { var it FindUser var asMap = obj.(map[string]interface{}) @@ -8986,7 +9745,7 @@ func (ec *executionContext) unmarshalInputProjectsFilter(ctx context.Context, ob switch k { case "teamID": var err error - it.TeamID, err = ec.unmarshalOString2ᚖstring(ctx, v) + it.TeamID, err = ec.unmarshalOUUID2ᚖgithubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) if err != nil { return it, err } @@ -9236,6 +9995,30 @@ func (ec *executionContext) unmarshalInputUpdateTaskChecklistItemName(ctx contex return it, nil } +func (ec *executionContext) unmarshalInputUpdateTaskChecklistName(ctx context.Context, obj interface{}) (UpdateTaskChecklistName, error) { + var it UpdateTaskChecklistName + var asMap = obj.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "taskChecklistID": + var err error + it.TaskChecklistID, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + if err != nil { + return it, err + } + case "name": + var err error + it.Name, err = ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputUpdateTaskDescriptionInput(ctx context.Context, obj interface{}) (UpdateTaskDescriptionInput, error) { var it UpdateTaskDescriptionInput var asMap = obj.(map[string]interface{}) @@ -9468,6 +10251,38 @@ func (ec *executionContext) _DeleteTaskChecklistItemPayload(ctx context.Context, return out } +var deleteTaskChecklistPayloadImplementors = []string{"DeleteTaskChecklistPayload"} + +func (ec *executionContext) _DeleteTaskChecklistPayload(ctx context.Context, sel ast.SelectionSet, obj *DeleteTaskChecklistPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deleteTaskChecklistPayloadImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DeleteTaskChecklistPayload") + case "ok": + out.Values[i] = ec._DeleteTaskChecklistPayload_ok(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "taskChecklist": + out.Values[i] = ec._DeleteTaskChecklistPayload_taskChecklist(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var deleteTaskGroupPayloadImplementors = []string{"DeleteTaskGroupPayload"} func (ec *executionContext) _DeleteTaskGroupPayload(ctx context.Context, sel ast.SelectionSet, obj *DeleteTaskGroupPayload) graphql.Marshaler { @@ -9532,6 +10347,75 @@ func (ec *executionContext) _DeleteTaskPayload(ctx context.Context, sel ast.Sele return out } +var deleteTeamPayloadImplementors = []string{"DeleteTeamPayload"} + +func (ec *executionContext) _DeleteTeamPayload(ctx context.Context, sel ast.SelectionSet, obj *DeleteTeamPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deleteTeamPayloadImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DeleteTeamPayload") + case "ok": + out.Values[i] = ec._DeleteTeamPayload_ok(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "team": + out.Values[i] = ec._DeleteTeamPayload_team(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "projects": + out.Values[i] = ec._DeleteTeamPayload_projects(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var deleteUserAccountPayloadImplementors = []string{"DeleteUserAccountPayload"} + +func (ec *executionContext) _DeleteUserAccountPayload(ctx context.Context, sel ast.SelectionSet, obj *DeleteUserAccountPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deleteUserAccountPayloadImplementors) + + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DeleteUserAccountPayload") + case "ok": + out.Values[i] = ec._DeleteUserAccountPayload_ok(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + case "userAccount": + out.Values[i] = ec._DeleteUserAccountPayload_userAccount(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var labelColorImplementors = []string{"LabelColor"} func (ec *executionContext) _LabelColor(ctx context.Context, sel ast.SelectionSet, obj *pg.LabelColor) graphql.Marshaler { @@ -9608,6 +10492,16 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "deleteUserAccount": + out.Values[i] = ec._Mutation_deleteUserAccount(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "deleteTeam": + out.Values[i] = ec._Mutation_deleteTeam(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } case "createTeam": out.Values[i] = ec._Mutation_createTeam(ctx, field) if out.Values[i] == graphql.Null { @@ -9703,6 +10597,16 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { invalids++ } + case "deleteTaskChecklist": + out.Values[i] = ec._Mutation_deleteTaskChecklist(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } + case "updateTaskChecklistName": + out.Values[i] = ec._Mutation_updateTaskChecklistName(ctx, field) + if out.Values[i] == graphql.Null { + invalids++ + } case "createTaskChecklistItem": out.Values[i] = ec._Mutation_createTaskChecklistItem(ctx, field) if out.Values[i] == graphql.Null { @@ -10171,6 +11075,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr } return res }) + case "findTeam": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_findTeam(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + }) case "teams": field := field out.Concurrently(i, func() (res graphql.Marshaler) { @@ -11235,6 +12153,10 @@ func (ec *executionContext) marshalNDeleteProjectPayload2ᚖgithubᚗcomᚋjorda return ec._DeleteProjectPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNDeleteTaskChecklist2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskChecklist(ctx context.Context, v interface{}) (DeleteTaskChecklist, error) { + return ec.unmarshalInputDeleteTaskChecklist(ctx, v) +} + func (ec *executionContext) unmarshalNDeleteTaskChecklistItem2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskChecklistItem(ctx context.Context, v interface{}) (DeleteTaskChecklistItem, error) { return ec.unmarshalInputDeleteTaskChecklistItem(ctx, v) } @@ -11253,6 +12175,20 @@ func (ec *executionContext) marshalNDeleteTaskChecklistItemPayload2ᚖgithubᚗc return ec._DeleteTaskChecklistItemPayload(ctx, sel, v) } +func (ec *executionContext) marshalNDeleteTaskChecklistPayload2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskChecklistPayload(ctx context.Context, sel ast.SelectionSet, v DeleteTaskChecklistPayload) graphql.Marshaler { + return ec._DeleteTaskChecklistPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNDeleteTaskChecklistPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskChecklistPayload(ctx context.Context, sel ast.SelectionSet, v *DeleteTaskChecklistPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._DeleteTaskChecklistPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNDeleteTaskGroupInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTaskGroupInput(ctx context.Context, v interface{}) (DeleteTaskGroupInput, error) { return ec.unmarshalInputDeleteTaskGroupInput(ctx, v) } @@ -11289,6 +12225,42 @@ func (ec *executionContext) marshalNDeleteTaskPayload2ᚖgithubᚗcomᚋjordankn return ec._DeleteTaskPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNDeleteTeam2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTeam(ctx context.Context, v interface{}) (DeleteTeam, error) { + return ec.unmarshalInputDeleteTeam(ctx, v) +} + +func (ec *executionContext) marshalNDeleteTeamPayload2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTeamPayload(ctx context.Context, sel ast.SelectionSet, v DeleteTeamPayload) graphql.Marshaler { + return ec._DeleteTeamPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNDeleteTeamPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteTeamPayload(ctx context.Context, sel ast.SelectionSet, v *DeleteTeamPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._DeleteTeamPayload(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNDeleteUserAccount2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteUserAccount(ctx context.Context, v interface{}) (DeleteUserAccount, error) { + return ec.unmarshalInputDeleteUserAccount(ctx, v) +} + +func (ec *executionContext) marshalNDeleteUserAccountPayload2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteUserAccountPayload(ctx context.Context, sel ast.SelectionSet, v DeleteUserAccountPayload) graphql.Marshaler { + return ec._DeleteUserAccountPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNDeleteUserAccountPayload2ᚖgithubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐDeleteUserAccountPayload(ctx context.Context, sel ast.SelectionSet, v *DeleteUserAccountPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._DeleteUserAccountPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNFindProject2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐFindProject(ctx context.Context, v interface{}) (FindProject, error) { return ec.unmarshalInputFindProject(ctx, v) } @@ -11297,6 +12269,10 @@ func (ec *executionContext) unmarshalNFindTask2githubᚗcomᚋjordanknottᚋproj return ec.unmarshalInputFindTask(ctx, v) } +func (ec *executionContext) unmarshalNFindTeam2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐFindTeam(ctx context.Context, v interface{}) (FindTeam, error) { + return ec.unmarshalInputFindTeam(ctx, v) +} + func (ec *executionContext) unmarshalNFindUser2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐFindUser(ctx context.Context, v interface{}) (FindUser, error) { return ec.unmarshalInputFindUser(ctx, v) } @@ -12068,6 +13044,10 @@ func (ec *executionContext) unmarshalNUpdateTaskChecklistItemName2githubᚗcom return ec.unmarshalInputUpdateTaskChecklistItemName(ctx, v) } +func (ec *executionContext) unmarshalNUpdateTaskChecklistName2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskChecklistName(ctx context.Context, v interface{}) (UpdateTaskChecklistName, error) { + return ec.unmarshalInputUpdateTaskChecklistName(ctx, v) +} + func (ec *executionContext) unmarshalNUpdateTaskDescriptionInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUpdateTaskDescriptionInput(ctx context.Context, v interface{}) (UpdateTaskDescriptionInput, error) { return ec.unmarshalInputUpdateTaskDescriptionInput(ctx, v) } @@ -12503,6 +13483,29 @@ func (ec *executionContext) marshalOTime2ᚖtimeᚐTime(ctx context.Context, sel return ec.marshalOTime2timeᚐTime(ctx, sel, *v) } +func (ec *executionContext) unmarshalOUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx context.Context, v interface{}) (uuid.UUID, error) { + return UnmarshalUUID(v) +} + +func (ec *executionContext) marshalOUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx context.Context, sel ast.SelectionSet, v uuid.UUID) graphql.Marshaler { + return MarshalUUID(v) +} + +func (ec *executionContext) unmarshalOUUID2ᚖgithubᚗcomᚋgoogleᚋuuidᚐUUID(ctx context.Context, v interface{}) (*uuid.UUID, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalOUUID2ᚖgithubᚗcomᚋgoogleᚋuuidᚐUUID(ctx context.Context, sel ast.SelectionSet, v *uuid.UUID) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.marshalOUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, sel, *v) +} + func (ec *executionContext) unmarshalOUnassignTaskInput2githubᚗcomᚋjordanknottᚋprojectᚑcitadelᚋapiᚋgraphᚐUnassignTaskInput(ctx context.Context, v interface{}) (UnassignTaskInput, error) { return ec.unmarshalInputUnassignTaskInput(ctx, v) } diff --git a/api/graph/models_gen.go b/api/graph/models_gen.go index 7ce4699..654ebb3 100644 --- a/api/graph/models_gen.go +++ b/api/graph/models_gen.go @@ -59,6 +59,10 @@ type DeleteProjectPayload struct { Project *pg.Project `json:"project"` } +type DeleteTaskChecklist struct { + TaskChecklistID uuid.UUID `json:"taskChecklistID"` +} + type DeleteTaskChecklistItem struct { TaskChecklistItemID uuid.UUID `json:"taskChecklistItemID"` } @@ -68,6 +72,11 @@ type DeleteTaskChecklistItemPayload struct { TaskChecklistItem *pg.TaskChecklistItem `json:"taskChecklistItem"` } +type DeleteTaskChecklistPayload struct { + Ok bool `json:"ok"` + TaskChecklist *pg.TaskChecklist `json:"taskChecklist"` +} + type DeleteTaskGroupInput struct { TaskGroupID uuid.UUID `json:"taskGroupID"` } @@ -86,6 +95,25 @@ type DeleteTaskPayload struct { TaskID string `json:"taskID"` } +type DeleteTeam struct { + TeamID uuid.UUID `json:"teamID"` +} + +type DeleteTeamPayload struct { + Ok bool `json:"ok"` + Team *pg.Team `json:"team"` + Projects []pg.Project `json:"projects"` +} + +type DeleteUserAccount struct { + UserID uuid.UUID `json:"userID"` +} + +type DeleteUserAccountPayload struct { + Ok bool `json:"ok"` + UserAccount *pg.UserAccount `json:"userAccount"` +} + type FindProject struct { ProjectID string `json:"projectId"` } @@ -94,6 +122,10 @@ type FindTask struct { TaskID uuid.UUID `json:"taskID"` } +type FindTeam struct { + TeamID uuid.UUID `json:"teamID"` +} + type FindUser struct { UserID string `json:"userId"` } @@ -167,7 +199,7 @@ type ProjectMember struct { } type ProjectsFilter struct { - TeamID *string `json:"teamID"` + TeamID *uuid.UUID `json:"teamID"` } type RemoveTaskLabelInput struct { @@ -229,6 +261,11 @@ type UpdateTaskChecklistItemName struct { Name string `json:"name"` } +type UpdateTaskChecklistName struct { + TaskChecklistID uuid.UUID `json:"taskChecklistID"` + Name string `json:"name"` +} + type UpdateTaskDescriptionInput struct { TaskID uuid.UUID `json:"taskID"` Description string `json:"description"` diff --git a/api/graph/schema.graphqls b/api/graph/schema.graphqls index d7e1977..bf75fa3 100644 --- a/api/graph/schema.graphqls +++ b/api/graph/schema.graphqls @@ -103,7 +103,7 @@ type Task { } input ProjectsFilter { - teamID: String + teamID: UUID } input FindUser { @@ -123,6 +123,10 @@ type Organization { name: String! } +input FindTeam { + teamID: UUID! +} + type Query { organizations: [Organization!]! users: [UserAccount!]! @@ -130,6 +134,7 @@ type Query { findProject(input: FindProject!): Project! findTask(input: FindTask!): Task! projects(input: ProjectsFilter): [Project!]! + findTeam(input: FindTeam!): Team! teams: [Team!]! labelColors: [LabelColor!]! taskGroups: [TaskGroup!]! @@ -356,11 +361,43 @@ type DeleteProjectPayload { project: Project! } +input DeleteTeam { + teamID: UUID! +} + +type DeleteTeamPayload { + ok: Boolean! + team: Team! + projects: [Project!]! +} + +input DeleteUserAccount { + userID: UUID! +} + +type DeleteUserAccountPayload { + ok: Boolean! + userAccount: UserAccount! +} + +input UpdateTaskChecklistName { + taskChecklistID: UUID! + name: String! +} +input DeleteTaskChecklist { + taskChecklistID: UUID! +} +type DeleteTaskChecklistPayload { + ok: Boolean! + taskChecklist: TaskChecklist! +} type Mutation { createRefreshToken(input: NewRefreshToken!): RefreshToken! createUserAccount(input: NewUserAccount!): UserAccount! + deleteUserAccount(input: DeleteUserAccount!): DeleteUserAccountPayload! + deleteTeam(input: DeleteTeam!): DeleteTeamPayload! createTeam(input: NewTeam!): Team! clearProfileAvatar: UserAccount! @@ -386,6 +423,8 @@ type Mutation { toggleTaskLabel(input: ToggleTaskLabelInput!): ToggleTaskLabelPayload! createTaskChecklist(input: CreateTaskChecklist!): TaskChecklist! + deleteTaskChecklist(input: DeleteTaskChecklist!): DeleteTaskChecklistPayload! + updateTaskChecklistName(input: UpdateTaskChecklistName!): TaskChecklist! createTaskChecklistItem(input: CreateTaskChecklistItem!): TaskChecklistItem! updateTaskChecklistItemName(input: UpdateTaskChecklistItemName!): TaskChecklistItem! setTaskChecklistItemComplete(input: SetTaskChecklistItemComplete!): TaskChecklistItem! diff --git a/api/graph/schema.resolvers.go b/api/graph/schema.resolvers.go index a2e095c..d4b4f82 100644 --- a/api/graph/schema.resolvers.go +++ b/api/graph/schema.resolvers.go @@ -13,6 +13,7 @@ import ( "github.com/jordanknott/project-citadel/api/pg" log "github.com/sirupsen/logrus" "github.com/vektah/gqlparser/v2/gqlerror" + "golang.org/x/crypto/bcrypt" ) func (r *labelColorResolver) ID(ctx context.Context, obj *pg.LabelColor) (uuid.UUID, error) { @@ -29,17 +30,54 @@ func (r *mutationResolver) CreateRefreshToken(ctx context.Context, input NewRefr func (r *mutationResolver) CreateUserAccount(ctx context.Context, input NewUserAccount) (*pg.UserAccount, error) { createdAt := time.Now().UTC() + hashedPwd, err := bcrypt.GenerateFromPassword([]byte(input.Password), 14) + if err != nil { + return &pg.UserAccount{}, err + } userAccount, err := r.Repository.CreateUserAccount(ctx, pg.CreateUserAccountParams{ FullName: input.FullName, Initials: input.Initials, Email: input.Email, Username: input.Username, CreatedAt: createdAt, - PasswordHash: input.Password, + PasswordHash: string(hashedPwd), }) return &userAccount, err } +func (r *mutationResolver) DeleteUserAccount(ctx context.Context, input DeleteUserAccount) (*DeleteUserAccountPayload, error) { + user, err := r.Repository.GetUserAccountByID(ctx, input.UserID) + if err != nil { + return &DeleteUserAccountPayload{Ok: false}, err + } + + err = r.Repository.DeleteUserAccountByID(ctx, input.UserID) + if err != nil { + return &DeleteUserAccountPayload{Ok: false}, err + } + return &DeleteUserAccountPayload{UserAccount: &user, Ok: true}, nil +} + +func (r *mutationResolver) DeleteTeam(ctx context.Context, input DeleteTeam) (*DeleteTeamPayload, error) { + team, err := r.Repository.GetTeamByID(ctx, input.TeamID) + if err != nil { + log.Error(err) + return &DeleteTeamPayload{Ok: false}, err + } + projects, err := r.Repository.GetAllProjectsForTeam(ctx, input.TeamID) + if err != nil { + log.Error(err) + return &DeleteTeamPayload{Ok: false}, err + } + err = r.Repository.DeleteTeamByID(ctx, input.TeamID) + if err != nil { + log.Error(err) + return &DeleteTeamPayload{Ok: false}, err + } + + return &DeleteTeamPayload{Ok: true, Team: &team, Projects: projects}, nil +} + func (r *mutationResolver) CreateTeam(ctx context.Context, input NewTeam) (*pg.Team, error) { createdAt := time.Now().UTC() team, err := r.Repository.CreateTeam(ctx, pg.CreateTeamParams{input.OrganizationID, createdAt, input.Name}) @@ -277,6 +315,26 @@ func (r *mutationResolver) CreateTaskChecklist(ctx context.Context, input Create return &taskChecklist, nil } +func (r *mutationResolver) DeleteTaskChecklist(ctx context.Context, input DeleteTaskChecklist) (*DeleteTaskChecklistPayload, error) { + taskChecklist, err := r.Repository.GetTaskChecklistByID(ctx, input.TaskChecklistID) + if err != nil { + return &DeleteTaskChecklistPayload{Ok: false}, err + } + err = r.Repository.DeleteTaskChecklistByID(ctx, input.TaskChecklistID) + if err != nil { + return &DeleteTaskChecklistPayload{Ok: false}, err + } + return &DeleteTaskChecklistPayload{Ok: true, TaskChecklist: &taskChecklist}, nil +} + +func (r *mutationResolver) UpdateTaskChecklistName(ctx context.Context, input UpdateTaskChecklistName) (*pg.TaskChecklist, error) { + checklist, err := r.Repository.UpdateTaskChecklistName(ctx, pg.UpdateTaskChecklistNameParams{TaskChecklistID: input.TaskChecklistID, Name: input.Name}) + if err != nil { + return &pg.TaskChecklist{}, err + } + return &checklist, nil +} + func (r *mutationResolver) CreateTaskChecklistItem(ctx context.Context, input CreateTaskChecklistItem) (*pg.TaskChecklistItem, error) { createdAt := time.Now().UTC() taskChecklistItem, err := r.Repository.CreateTaskChecklistItem(ctx, pg.CreateTaskChecklistItemParams{ @@ -475,6 +533,9 @@ func (r *projectResolver) TaskGroups(ctx context.Context, obj *pg.Project) ([]pg func (r *projectResolver) Members(ctx context.Context, obj *pg.Project) ([]ProjectMember, error) { user, err := r.Repository.GetUserAccountByID(ctx, obj.Owner) members := []ProjectMember{} + if err == sql.ErrNoRows { + return members, nil + } if err != nil { return members, err } @@ -561,15 +622,19 @@ func (r *queryResolver) FindTask(ctx context.Context, input FindTask) (*pg.Task, func (r *queryResolver) Projects(ctx context.Context, input *ProjectsFilter) ([]pg.Project, error) { if input != nil { - teamID, err := uuid.Parse(*input.TeamID) - if err != nil { - return []pg.Project{}, err - } - return r.Repository.GetAllProjectsForTeam(ctx, teamID) + return r.Repository.GetAllProjectsForTeam(ctx, *input.TeamID) } return r.Repository.GetAllProjects(ctx) } +func (r *queryResolver) FindTeam(ctx context.Context, input FindTeam) (*pg.Team, error) { + team, err := r.Repository.GetTeamByID(ctx, input.TeamID) + if err != nil { + return &pg.Team{}, err + } + return &team, nil +} + func (r *queryResolver) Teams(ctx context.Context) ([]pg.Team, error) { return r.Repository.GetAllTeams(ctx) } @@ -727,6 +792,9 @@ func (r *teamResolver) ID(ctx context.Context, obj *pg.Team) (uuid.UUID, error) 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 == sql.ErrNoRows { + return projectMembers, nil + } if err != nil { return projectMembers, err } diff --git a/api/migrations/0034_add-cascade-delete-to_project_team_id_fkey.up.sql b/api/migrations/0034_add-cascade-delete-to_project_team_id_fkey.up.sql new file mode 100644 index 0000000..9c84845 --- /dev/null +++ b/api/migrations/0034_add-cascade-delete-to_project_team_id_fkey.up.sql @@ -0,0 +1,6 @@ +ALTER TABLE project DROP CONSTRAINT project_team_id_fkey; +ALTER TABLE project + ADD CONSTRAINT project_team_id_fkey + FOREIGN KEY (team_id) + REFERENCES team(team_id) + ON DELETE CASCADE; diff --git a/api/migrations/0035_add-cascade-delete-to-task_assigned_user_id_fkey.up.sql b/api/migrations/0035_add-cascade-delete-to-task_assigned_user_id_fkey.up.sql new file mode 100644 index 0000000..17543e0 --- /dev/null +++ b/api/migrations/0035_add-cascade-delete-to-task_assigned_user_id_fkey.up.sql @@ -0,0 +1,6 @@ +ALTER TABLE task_assigned DROP CONSTRAINT task_assigned_user_id_fkey; +ALTER TABLE task_assigned + ADD CONSTRAINT task_assigned_user_id_fkey + FOREIGN KEY (user_id) + REFERENCES user_account(user_id) + ON DELETE CASCADE; diff --git a/api/migrations/0036-add-fkey-to-user_id-on-refresh_token-table.up.sql b/api/migrations/0036-add-fkey-to-user_id-on-refresh_token-table.up.sql new file mode 100644 index 0000000..3ddc6d7 --- /dev/null +++ b/api/migrations/0036-add-fkey-to-user_id-on-refresh_token-table.up.sql @@ -0,0 +1,5 @@ +ALTER TABLE refresh_token + ADD CONSTRAINT refresh_token_user_id_fkey + FOREIGN KEY (user_id) + REFERENCES user_account(user_id) + ON DELETE CASCADE; diff --git a/api/pg/pg.go b/api/pg/pg.go index d546775..b1d5ca1 100644 --- a/api/pg/pg.go +++ b/api/pg/pg.go @@ -17,7 +17,11 @@ type Repository interface { GetAllTeams(ctx context.Context) ([]Team, error) DeleteProjectByID(ctx context.Context, projectID uuid.UUID) error + DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error + GetTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) (TaskChecklist, error) + DeleteTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) error + UpdateTaskChecklistName(ctx context.Context, arg UpdateTaskChecklistNameParams) (TaskChecklist, error) CreateProject(ctx context.Context, arg CreateProjectParams) (Project, error) GetAllProjects(ctx context.Context) ([]Project, error) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) diff --git a/api/pg/querier.go b/api/pg/querier.go index ba421c1..059c562 100644 --- a/api/pg/querier.go +++ b/api/pg/querier.go @@ -30,6 +30,7 @@ type Querier interface { DeleteRefreshTokenByUserID(ctx context.Context, userID uuid.UUID) error DeleteTaskAssignedByID(ctx context.Context, arg DeleteTaskAssignedByIDParams) (TaskAssigned, error) DeleteTaskByID(ctx context.Context, taskID uuid.UUID) error + DeleteTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) error DeleteTaskChecklistItem(ctx context.Context, taskChecklistItemID uuid.UUID) error DeleteTaskGroupByID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) DeleteTaskLabelByID(ctx context.Context, taskLabelID uuid.UUID) error @@ -37,6 +38,7 @@ type Querier interface { DeleteTasksByTaskGroupID(ctx context.Context, taskGroupID uuid.UUID) (int64, error) DeleteTeamByID(ctx context.Context, teamID uuid.UUID) error DeleteTeamMemberByUserID(ctx context.Context, userID uuid.UUID) error + DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error GetAllOrganizations(ctx context.Context) ([]Organization, error) GetAllProjects(ctx context.Context) ([]Project, error) GetAllProjectsForTeam(ctx context.Context, teamID uuid.UUID) ([]Project, error) @@ -52,6 +54,7 @@ type Querier interface { GetProjectLabelsForProject(ctx context.Context, projectID uuid.UUID) ([]ProjectLabel, error) GetRefreshTokenByID(ctx context.Context, tokenID uuid.UUID) (RefreshToken, error) GetTaskByID(ctx context.Context, taskID uuid.UUID) (Task, error) + GetTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) (TaskChecklist, error) GetTaskChecklistItemByID(ctx context.Context, taskChecklistItemID uuid.UUID) (TaskChecklistItem, error) GetTaskChecklistItemsForTaskChecklist(ctx context.Context, taskChecklistID uuid.UUID) ([]TaskChecklistItem, error) GetTaskChecklistsForTask(ctx context.Context, taskID uuid.UUID) ([]TaskChecklist, error) @@ -74,6 +77,7 @@ type Querier interface { UpdateProjectLabelName(ctx context.Context, arg UpdateProjectLabelNameParams) (ProjectLabel, error) UpdateProjectNameByID(ctx context.Context, arg UpdateProjectNameByIDParams) (Project, error) UpdateTaskChecklistItemName(ctx context.Context, arg UpdateTaskChecklistItemNameParams) (TaskChecklistItem, error) + UpdateTaskChecklistName(ctx context.Context, arg UpdateTaskChecklistNameParams) (TaskChecklist, error) UpdateTaskDescription(ctx context.Context, arg UpdateTaskDescriptionParams) (Task, error) UpdateTaskDueDate(ctx context.Context, arg UpdateTaskDueDateParams) (Task, error) UpdateTaskGroupLocation(ctx context.Context, arg UpdateTaskGroupLocationParams) (TaskGroup, error) diff --git a/api/pg/task_checklist.sql.go b/api/pg/task_checklist.sql.go index 146ada0..9cc1734 100644 --- a/api/pg/task_checklist.sql.go +++ b/api/pg/task_checklist.sql.go @@ -72,6 +72,15 @@ func (q *Queries) CreateTaskChecklistItem(ctx context.Context, arg CreateTaskChe return i, err } +const deleteTaskChecklistByID = `-- name: DeleteTaskChecklistByID :exec +DELETE FROM task_checklist WHERE task_checklist_id = $1 +` + +func (q *Queries) DeleteTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) error { + _, err := q.db.ExecContext(ctx, deleteTaskChecklistByID, taskChecklistID) + return err +} + const deleteTaskChecklistItem = `-- name: DeleteTaskChecklistItem :exec DELETE FROM task_checklist_item WHERE task_checklist_item_id = $1 ` @@ -81,6 +90,23 @@ func (q *Queries) DeleteTaskChecklistItem(ctx context.Context, taskChecklistItem return err } +const getTaskChecklistByID = `-- name: GetTaskChecklistByID :one +SELECT task_checklist_id, task_id, created_at, name, position FROM task_checklist WHERE task_checklist_id = $1 +` + +func (q *Queries) GetTaskChecklistByID(ctx context.Context, taskChecklistID uuid.UUID) (TaskChecklist, error) { + row := q.db.QueryRowContext(ctx, getTaskChecklistByID, taskChecklistID) + var i TaskChecklist + err := row.Scan( + &i.TaskChecklistID, + &i.TaskID, + &i.CreatedAt, + &i.Name, + &i.Position, + ) + return i, err +} + const getTaskChecklistItemByID = `-- name: GetTaskChecklistItemByID :one SELECT task_checklist_item_id, task_checklist_id, created_at, complete, name, position, due_date FROM task_checklist_item WHERE task_checklist_item_id = $1 ` @@ -217,3 +243,26 @@ func (q *Queries) UpdateTaskChecklistItemName(ctx context.Context, arg UpdateTas ) return i, err } + +const updateTaskChecklistName = `-- name: UpdateTaskChecklistName :one +UPDATE task_checklist SET name = $2 WHERE task_checklist_id = $1 + RETURNING task_checklist_id, task_id, created_at, name, position +` + +type UpdateTaskChecklistNameParams struct { + TaskChecklistID uuid.UUID `json:"task_checklist_id"` + Name string `json:"name"` +} + +func (q *Queries) UpdateTaskChecklistName(ctx context.Context, arg UpdateTaskChecklistNameParams) (TaskChecklist, error) { + row := q.db.QueryRowContext(ctx, updateTaskChecklistName, arg.TaskChecklistID, arg.Name) + var i TaskChecklist + err := row.Scan( + &i.TaskChecklistID, + &i.TaskID, + &i.CreatedAt, + &i.Name, + &i.Position, + ) + return i, err +} diff --git a/api/pg/user_accounts.sql.go b/api/pg/user_accounts.sql.go index c346dcc..89490ed 100644 --- a/api/pg/user_accounts.sql.go +++ b/api/pg/user_accounts.sql.go @@ -49,6 +49,15 @@ func (q *Queries) CreateUserAccount(ctx context.Context, arg CreateUserAccountPa return i, err } +const deleteUserAccountByID = `-- name: DeleteUserAccountByID :exec +DELETE FROM user_account WHERE user_id = $1 +` + +func (q *Queries) DeleteUserAccountByID(ctx context.Context, userID uuid.UUID) error { + _, err := q.db.ExecContext(ctx, deleteUserAccountByID, userID) + return err +} + const getAllUserAccounts = `-- name: GetAllUserAccounts :many SELECT user_id, created_at, email, username, password_hash, profile_bg_color, full_name, initials, profile_avatar_url FROM user_account ` diff --git a/api/query/task_checklist.sql b/api/query/task_checklist.sql index 608f61a..2c27ae3 100644 --- a/api/query/task_checklist.sql +++ b/api/query/task_checklist.sql @@ -5,6 +5,16 @@ INSERT INTO task_checklist (task_id, created_at, name, position) VALUES ($1, $2, -- name: GetTaskChecklistsForTask :many SELECT * FROM task_checklist WHERE task_id = $1; +-- name: UpdateTaskChecklistName :one +UPDATE task_checklist SET name = $2 WHERE task_checklist_id = $1 + RETURNING *; + +-- name: DeleteTaskChecklistByID :exec +DELETE FROM task_checklist WHERE task_checklist_id = $1; + +-- name: GetTaskChecklistByID :one +SELECT * FROM task_checklist WHERE task_checklist_id = $1; + -- name: CreateTaskChecklistItem :one INSERT INTO task_checklist_item (task_checklist_id, created_at, name, position, complete, due_date) VALUES ($1, $2, $3, $4, false, null) RETURNING *; diff --git a/api/query/user_accounts.sql b/api/query/user_accounts.sql index 74427cb..a3af29d 100644 --- a/api/query/user_accounts.sql +++ b/api/query/user_accounts.sql @@ -14,3 +14,6 @@ INSERT INTO user_account(full_name, initials, email, username, created_at, passw -- name: UpdateUserAccountProfileAvatarURL :one UPDATE user_account SET profile_avatar_url = $2 WHERE user_id = $1 RETURNING *; + +-- name: DeleteUserAccountByID :exec +DELETE FROM user_account WHERE user_id = $1; diff --git a/web/package.json b/web/package.json index 7277d29..162a905 100644 --- a/web/package.json +++ b/web/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@apollo/client": "^3.0.0-rc.8", "@apollo/react-common": "^3.1.4", "@apollo/react-hooks": "^3.1.3", "@fortawesome/fontawesome-svg-core": "^1.2.27", diff --git a/web/report.20200621.183857.68808.0.001.json b/web/report.20200621.183857.68808.0.001.json new file mode 100644 index 0000000..d904ca2 --- /dev/null +++ b/web/report.20200621.183857.68808.0.001.json @@ -0,0 +1,471 @@ + +{ + "header": { + "reportVersion": 1, + "event": "Allocation failed - JavaScript heap out of memory", + "trigger": "FatalError", + "filename": "report.20200621.183857.68808.0.001.json", + "dumpEventTime": "2020-06-21T18:38:57Z", + "dumpEventTimeStamp": "1592782737343", + "processId": 68808, + "cwd": "/home/jordan/Projects/project-citadel/web", + "commandLine": [ + "/usr/bin/node", + "/home/jordan/.config/coc/extensions/node_modules/coc-tsserver/bin/tsserverForkStart", + "/home/jordan/Projects/project-citadel/web/node_modules/typescript/lib/tsserver.js", + "--allowLocalPluginLoads", + "--useInferredProjectPerProjectRoot", + "--cancellationPipeName", + "/tmp/coc-nvim-tscancellation-49e6b7aafa04f6c486f4.sock*", + "--npmLocation", + "\"/usr/bin/npm\"", + "--noGetErrOnBackgroundUpdate", + "--validateDefaultNpmLocation" + ], + "nodejsVersion": "v13.8.0", + "glibcVersionRuntime": "2.30", + "glibcVersionCompiler": "2.30", + "wordSize": 64, + "arch": "x64", + "platform": "linux", + "componentVersions": { + "node": "13.8.0", + "v8": "7.9.317.25-node.28", + "uv": "1.34.1", + "zlib": "1.2.11", + "brotli": "1.0.7", + "ares": "1.15.0", + "modules": "79", + "nghttp2": "1.39.2", + "napi": "5", + "llhttp": "2.0.4", + "openssl": "1.1.1d", + "cldr": "36.0", + "icu": "65.1", + "tz": "2019c", + "unicode": "12.1" + }, + "release": { + "name": "node", + "headersUrl": "https://nodejs.org/download/release/v13.8.0/node-v13.8.0-headers.tar.gz", + "sourceUrl": "https://nodejs.org/download/release/v13.8.0/node-v13.8.0.tar.gz" + }, + "osName": "Linux", + "osRelease": "4.19.101-1-lts", + "osVersion": "#1 SMP Sat, 01 Feb 2020 16:35:36 +0000", + "osMachine": "x86_64", + "cpus": [ + { + "model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz", + "speed": 2646, + "user": 48577700, + "nice": 14200, + "sys": 8014100, + "idle": 166454900, + "irq": 735500 + }, + { + "model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz", + "speed": 2646, + "user": 48745100, + "nice": 19200, + "sys": 7840700, + "idle": 42737200, + "irq": 528800 + }, + { + "model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz", + "speed": 2647, + "user": 48543900, + "nice": 15800, + "sys": 7804900, + "idle": 42914200, + "irq": 530200 + }, + { + "model": "Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz", + "speed": 2726, + "user": 48996200, + "nice": 19900, + "sys": 7808300, + "idle": 42957300, + "irq": 390800 + } + ], + "networkInterfaces": [ + { + "name": "lo", + "internal": true, + "mac": "00:00:00:00:00:00", + "address": "127.0.0.1", + "netmask": "255.0.0.0", + "family": "IPv4" + }, + { + "name": "wlp3s0", + "internal": false, + "mac": "7c:b0:c2:fe:93:86", + "address": "192.168.43.5", + "netmask": "255.255.255.0", + "family": "IPv4" + }, + { + "name": "docker0", + "internal": false, + "mac": "02:42:13:a9:89:9d", + "address": "172.17.0.1", + "netmask": "255.255.0.0", + "family": "IPv4" + }, + { + "name": "br-e929893879ec", + "internal": false, + "mac": "02:42:48:f4:23:30", + "address": "172.19.0.1", + "netmask": "255.255.0.0", + "family": "IPv4" + }, + { + "name": "lo", + "internal": true, + "mac": "00:00:00:00:00:00", + "address": "::1", + "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", + "family": "IPv6", + "scopeid": 0 + }, + { + "name": "wlp3s0", + "internal": false, + "mac": "7c:b0:c2:fe:93:86", + "address": "2600:100b:b000:9bf1:7eb0:c2ff:fefe:9386", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 0 + }, + { + "name": "wlp3s0", + "internal": false, + "mac": "7c:b0:c2:fe:93:86", + "address": "fe80::7eb0:c2ff:fefe:9386", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 3 + }, + { + "name": "docker0", + "internal": false, + "mac": "02:42:13:a9:89:9d", + "address": "fe80::42:13ff:fea9:899d", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 5 + }, + { + "name": "veth79cfd83", + "internal": false, + "mac": "a2:e9:86:a8:58:bb", + "address": "fe80::a0e9:86ff:fea8:58bb", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 7 + }, + { + "name": "br-e929893879ec", + "internal": false, + "mac": "02:42:48:f4:23:30", + "address": "fe80::42:48ff:fef4:2330", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 8 + }, + { + "name": "vethe27cc3e", + "internal": false, + "mac": "c6:bd:d7:3b:6c:96", + "address": "fe80::c4bd:d7ff:fe3b:6c96", + "netmask": "ffff:ffff:ffff:ffff::", + "family": "IPv6", + "scopeid": 10 + } + ], + "host": "archlinux" + }, + "javascriptStack": { + "message": "No stack.", + "stack": [ + "Unavailable." + ] + }, + "nativeStack": [ + { + "pc": "0x0000557aee1dae8a", + "symbol": "report::TriggerNodeReport(v8::Isolate*, node::Environment*, char const*, char const*, std::__cxx11::basic_string, std::allocator > const&, v8::Local) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee09bd48", + "symbol": "node::OnFatalError(char const*, char const*) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee20f382", + "symbol": "v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee20f5e8", + "symbol": "v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee399906", + "symbol": " [/usr/bin/node]" + }, + { + "pc": "0x0000557aee399a49", + "symbol": " [/usr/bin/node]" + }, + { + "pc": "0x0000557aee3ac22d", + "symbol": "v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee3acf58", + "symbol": "v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee3af48c", + "symbol": "v8::internal::Heap::AllocateRawWithLightRetrySlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee3af4f4", + "symbol": "v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee37499b", + "symbol": "v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationType, v8::internal::AllocationOrigin) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee6a5540", + "symbol": "v8::internal::Runtime_AllocateInYoungGeneration(int, unsigned long*, v8::internal::Isolate*) [/usr/bin/node]" + }, + { + "pc": "0x0000557aee9febd9", + "symbol": " [/usr/bin/node]" + } + ], + "javascriptHeap": { + "totalMemory": 2152407040, + "totalCommittedMemory": 2150546232, + "usedMemory": 2137632424, + "availableMemory": 47662336, + "memoryLimit": 2197815296, + "heapSpaces": { + "read_only_space": { + "memorySize": 262144, + "committedMemory": 33328, + "capacity": 33040, + "used": 33040, + "available": 0 + }, + "new_space": { + "memorySize": 2097152, + "committedMemory": 1029144, + "capacity": 1047424, + "used": 21136, + "available": 1026288 + }, + "old_space": { + "memorySize": 2058231808, + "committedMemory": 2057888048, + "capacity": 2048399928, + "used": 2048219560, + "available": 180368 + }, + "code_space": { + "memorySize": 9080832, + "committedMemory": 8864320, + "capacity": 7755264, + "used": 7755264, + "available": 0 + }, + "map_space": { + "memorySize": 1052672, + "committedMemory": 1048960, + "capacity": 740080, + "used": 740080, + "available": 0 + }, + "large_object_space": { + "memorySize": 81305600, + "committedMemory": 81305600, + "capacity": 80548528, + "used": 80548528, + "available": 0 + }, + "code_large_object_space": { + "memorySize": 376832, + "committedMemory": 376832, + "capacity": 314816, + "used": 314816, + "available": 0 + }, + "new_large_object_space": { + "memorySize": 0, + "committedMemory": 0, + "capacity": 1047424, + "used": 0, + "available": 1047424 + } + } + }, + "resourceUsage": { + "userCpuSeconds": 3484.11, + "kernelCpuSeconds": 64.7409, + "cpuConsumptionPercent": 35.606, + "maxRss": 2330890240, + "pageFaults": { + "IORequired": 31, + "IONotRequired": 9729381 + }, + "fsActivity": { + "reads": 53144, + "writes": 16 + } + }, + "uvthreadResourceUsage": { + "userCpuSeconds": 2308.89, + "kernelCpuSeconds": 31.2857, + "cpuConsumptionPercent": 23.4792, + "fsActivity": { + "reads": 53248, + "writes": 16 + } + }, + "libuv": [ + ], + "environmentVariables": { + "COLORTERM": "truecolor", + "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus", + "DESKTOP_SESSION": "awesome", + "DISPLAY": ":0.0", + "EDITOR": "nano", + "FZF_DEFAULT_COMMAND": "ag --hidden --ignore .git -g \"\"", + "GDMSESSION": "awesome", + "GO111MODULE": "on", + "GOBIN": "/home/jordan/go/bin", + "GREP_COLOR": "37;45", + "GREP_COLORS": "mt=37;45", + "GTK_MODULES": "canberra-gtk-module", + "HOME": "/home/jordan", + "LANG": "C", + "LESS": "-F -g -i -M -R -S -w -X -z-4", + "LESS_TERMCAP_mb": "\u001b[01;31m", + "LESS_TERMCAP_md": "\u001b[01;31m", + "LESS_TERMCAP_me": "\u001b[0m", + "LESS_TERMCAP_se": "\u001b[0m", + "LESS_TERMCAP_so": "\u001b[00;47;30m", + "LESS_TERMCAP_ue": "\u001b[0m", + "LESS_TERMCAP_us": "\u001b[01;32m", + "LOGNAME": "jordan", + "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:", + "MAIL": "/var/spool/mail/jordan", + "OLDPWD": "/home/jordan/Projects/project-citadel/web", + "PAGER": "less", + "PATH": "/home/jordan/.local/bin:/usr/local/bin:/usr/local/sbin:/home/nightwolf/Programs/cmake/bin:/home/nightwolf/Programs/idea-IU-163.13906.18/bin:/home/nightwolf/Programs/wpcli:/home/nightwolf/neovim/bin:/home/nightwolf/Programs/Postman:/home/nightwolf/Programs/Android_SDK/tools/bin:/home/nightwolf/Development/PhantomJS/bin:/home/nightwolf/Programs/node/bin:/home/nightwolf/pyenv/bin:/home/nightwolf/Programs/vv:/usr/bin:/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/usr/local/go/bin:/home/jordan/go/bin:/home/jordan/.garden/bin:~/Programs/node/bin:~/.utilities:/home/jordan/.fzf/bin:/home/jordan/.gem/ruby/2.6.0/bin:/home/jordan/.garden/bin:~/Programs/node/bin:~/.utilities:/home/jordan/.gem/ruby/2.6.0/bin", + "PWD": "/home/jordan/Projects/project-citadel/web", + "SHELL": "/usr/bin/zsh", + "SHLVL": "2", + "SSH_AGENT_PID": "773", + "SSH_AUTH_SOCK": "/tmp/ssh-agent.sock.1000", + "TERM": "xterm-256color", + "TMUX": "/tmp/tmux-1000/default,13203,1", + "TMUX_PANE": "%2", + "USER": "jordan", + "VIRTUALENVWRAPPER_PYTHON": "/usr/bin/python3", + "VIRTUAL_ENV_DISABLE_PROMPT": "12", + "VISUAL": "nano", + "VTE_VERSION": "5602", + "WINDOWID": "6291459", + "WORKON_HOME": "/home/jordan/.virtualenvs", + "XAUTHORITY": "/home/jordan/.Xauthority", + "XDG_GREETER_DATA_DIR": "/var/lib/lightdm-data/jordan", + "XDG_RUNTIME_DIR": "/run/user/1000", + "XDG_SEAT": "seat0", + "XDG_SEAT_PATH": "/org/freedesktop/DisplayManager/Seat0", + "XDG_SESSION_CLASS": "user", + "XDG_SESSION_DESKTOP": "awesome", + "XDG_SESSION_ID": "1", + "XDG_SESSION_PATH": "/org/freedesktop/DisplayManager/Session0", + "XDG_SESSION_TYPE": "x11", + "XDG_VTNR": "7", + "_": "/usr/bin/nvim", + "is_vim": "ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'", + "tmux_version": "$(tmux -V | sed -En \"s/^tmux ([0-9]+(.[0-9]+)?).*/\\1/p\")", + "LC_MESSAGES": "", + "VIMRUNTIME": "/usr/share/nvim/runtime", + "NVIM_LISTEN_ADDRESS": "/tmp/nvimaHxiZq/0", + "MYVIMRC": "/home/jordan/.config/nvim/init.vim", + "COC_VIMCONFIG": "/home/jordan/.config/nvim", + "COC_DATA_HOME": "/home/jordan/.config/coc", + "TSS_LOG": "-level verbose -file /tmp/coc-nvim-tsc.log", + "NODE_PATH": "/home/jordan/Projects/project-citadel/web/node_modules" + }, + "userLimits": { + "core_file_size_blocks": { + "soft": "unlimited", + "hard": "unlimited" + }, + "data_seg_size_kbytes": { + "soft": "unlimited", + "hard": "unlimited" + }, + "file_size_blocks": { + "soft": "unlimited", + "hard": "unlimited" + }, + "max_locked_memory_bytes": { + "soft": 65536, + "hard": 65536 + }, + "max_memory_size_kbytes": { + "soft": "unlimited", + "hard": "unlimited" + }, + "open_files": { + "soft": 524288, + "hard": 524288 + }, + "stack_size_bytes": { + "soft": 8388608, + "hard": "unlimited" + }, + "cpu_time_seconds": { + "soft": "unlimited", + "hard": "unlimited" + }, + "max_user_processes": { + "soft": 31138, + "hard": 31138 + }, + "virtual_memory_kbytes": { + "soft": "unlimited", + "hard": "unlimited" + } + }, + "sharedObjects": [ + "linux-vdso.so.1", + "/usr/lib/libz.so.1", + "/usr/lib/libcares.so.2", + "/usr/lib/libnghttp2.so.14", + "/usr/lib/libcrypto.so.1.1", + "/usr/lib/libssl.so.1.1", + "/usr/lib/libicui18n.so.65", + "/usr/lib/libicuuc.so.65", + "/usr/lib/libdl.so.2", + "/usr/lib/libstdc++.so.6", + "/usr/lib/libm.so.6", + "/usr/lib/libgcc_s.so.1", + "/usr/lib/libpthread.so.0", + "/usr/lib/libc.so.6", + "/usr/lib/libicudata.so.65", + "/lib64/ld-linux-x86-64.so.2" + ] +} \ No newline at end of file diff --git a/web/src/Admin/index.tsx b/web/src/Admin/index.tsx new file mode 100644 index 0000000..87ebe61 --- /dev/null +++ b/web/src/Admin/index.tsx @@ -0,0 +1,158 @@ +import React, { useEffect } from 'react'; +import Admin from 'shared/components/Admin'; +import GlobalTopNavbar from 'App/TopNavbar'; +import { useUsersQuery, useCreateUserAccountMutation, UsersDocument } from 'shared/generated/graphql'; +import Input from 'shared/components/Input'; +import styled from 'styled-components'; +import Button from 'shared/components/Button'; +import { useForm } from 'react-hook-form'; +import { usePopup, Popup } from 'shared/components/PopupMenu'; +import produce from 'immer'; + +type CreateUserData = { + email: string; + username: string; + fullName: string; + initials: string; + password: string; +}; +const CreateUserForm = styled.form` + display: flex; + flex-direction: column; +`; +const CreateUserButton = styled(Button)` + margin-top: 8px; + padding: 6px 12px; + width: 100%; +`; + +const AddUserInput = styled(Input)` + margin-bottom: 8px; +`; + +const InputError = styled.span` + color: rgba(${props => props.theme.colors.danger}); + font-size: 12px; +`; +type AddUserPopupProps = { + onAddUser: (user: CreateUserData) => void; +}; +const AddUserPopup: React.FC = ({ onAddUser }) => { + const { register, handleSubmit, errors } = useForm(); + const createUser = (data: CreateUserData) => { + onAddUser(data); + }; + return ( + + + {errors.fullName && {errors.fullName.message}} + + {errors.email && {errors.email.message}} + + {errors.username && {errors.username.message}} + + {errors.initials && {errors.initials.message}} + + {errors.password && {errors.password.message}} + Create + + ); +}; + +const AdminRoute = () => { + useEffect(() => { + document.title = 'Citadel | Admin'; + }, []); + const { loading, data } = useUsersQuery(); + const { showPopup, hidePopup } = usePopup(); + const [createUser] = useCreateUserAccountMutation({ + update: (client, createData) => { + const cacheData: any = client.readQuery({ + query: UsersDocument, + }); + console.log(cacheData); + console.log(createData); + const newData = produce(cacheData, (draftState: any) => { + draftState.users = [...draftState.users, { ...createData.data.createUserAccount }]; + }); + + client.writeQuery({ + query: UsersDocument, + data: { + ...newData, + }, + }); + }, + }); + if (loading) { + return {}} name={null} />; + } + if (data) { + return ( + <> + {}} name={null} /> + ({ ...user, role: 'TBD' }))} + onInviteUser={() => {}} + onAddUser={$target => { + showPopup( + $target, + hidePopup()}> + { + createUser({ variables: { ...user } }); + hidePopup(); + }} + /> + , + ); + }} + /> + + ); + } + return error; +}; + +export default AdminRoute; diff --git a/web/src/App/Navbar.tsx b/web/src/App/Navbar.tsx index ff4593e..656a0f0 100644 --- a/web/src/App/Navbar.tsx +++ b/web/src/App/Navbar.tsx @@ -15,7 +15,7 @@ const GlobalNavbar = () => { - + diff --git a/web/src/App/Routes.tsx b/web/src/App/Routes.tsx index ecdf42d..59e5cca 100644 --- a/web/src/App/Routes.tsx +++ b/web/src/App/Routes.tsx @@ -3,14 +3,16 @@ import { Router, Switch, Route } from 'react-router-dom'; import * as H from 'history'; import Dashboard from 'Dashboard'; +import Admin from 'Admin'; import Projects from 'Projects'; import Project from 'Projects/Project'; +import Teams from 'Teams'; import Login from 'Auth'; import Profile from 'Profile'; import styled from 'styled-components'; const MainContent = styled.div` - padding: 0 0 0 80px; + padding: 0 0 0 0; background: #262c49; height: 100%; display: flex; @@ -28,7 +30,9 @@ const Routes = ({ history }: RoutesProps) => ( + + ); diff --git a/web/src/App/TopNavbar.tsx b/web/src/App/TopNavbar.tsx index 86684c0..f3ca37b 100644 --- a/web/src/App/TopNavbar.tsx +++ b/web/src/App/TopNavbar.tsx @@ -1,24 +1,172 @@ -import React, { useState, useContext } from 'react'; +import React, { useState, useContext, useEffect } from 'react'; import TopNavbar from 'shared/components/TopNavbar'; +import styled from 'styled-components/macro'; import DropdownMenu, { ProfileMenu } from 'shared/components/DropdownMenu'; -import ProjectSettings, { DeleteProject } from 'shared/components/ProjectSettings'; +import ProjectSettings, { DeleteConfirm, DELETE_INFO } from 'shared/components/ProjectSettings'; import { useHistory } from 'react-router'; import UserIDContext from 'App/context'; -import { useMeQuery, useDeleteProjectMutation, GetProjectsDocument } from 'shared/generated/graphql'; +import { + useMeQuery, + useDeleteProjectMutation, + useGetProjectsQuery, + GetProjectsDocument, +} from 'shared/generated/graphql'; import { usePopup, Popup } from 'shared/components/PopupMenu'; +import { History } from 'history'; import produce from 'immer'; +import { Link } from 'react-router-dom'; -type GlobalTopNavbarProps = { - projectID: string | null; - name: string | null; - projectMembers?: null | Array; - onSaveProjectName?: (projectName: string) => void; +const TeamContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const TeamTitle = styled.h3` + font-size: 14px; + font-weight: 700; +`; + +const TeamProjects = styled.div` + display: flex; + flex-direction: column; + margin-top: 8px; + margin-bottom: 4px; +`; + +const TeamProjectLink = styled(Link)` + display: flex; + font-weight: 700; + height: 36px; + overflow: hidden; + padding: 0; + position: relative; + text-decoration: none; + user-select: none; +`; + +const TeamProjectBackground = styled.div<{ color: string }>` + background-image: url(null); + background-color: ${props => props.color}; + + background-size: cover; + background-position: 50%; + position: absolute; + width: 100%; + height: 36px; + opacity: 1; + border-radius: 3px; + &:before { + background: rgba(${props => props.theme.colors.bg.secondary}); + bottom: 0; + content: ''; + left: 0; + opacity: 0.88; + position: absolute; + right: 0; + top: 0; + } +`; + +const TeamProjectAvatar = styled.div<{ color: string }>` + background-image: url(null); + background-color: ${props => props.color}; + + display: inline-block; + flex: 0 0 auto; + background-size: cover; + border-radius: 3px 0 0 3px; + height: 36px; + width: 36px; + position: relative; + opacity: 0.7; +`; + +const TeamProjectContent = styled.div` + display: flex; + position: relative; + flex: 1; + width: 100%; + padding: 9px 0 9px 10px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +`; + +const TeamProjectTitle = styled.div` + font-weight: 700; + display: block; + padding-right: 12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +`; + +const TeamProjectContainer = styled.div` + box-sizing: border-box; + border-radius: 3px; + position: relative; + margin: 0 4px 4px 0; + min-width: 0; + &:hover ${TeamProjectTitle} { + color: rgba(${props => props.theme.colors.text.secondary}); + } + &:hover ${TeamProjectAvatar} { + opacity: 1; + } + &:hover ${TeamProjectBackground}:before { + opacity: 0.78; + } +`; + +const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f']; + +const ProjectFinder = () => { + const { loading, data } = useGetProjectsQuery(); + if (loading) { + return loading; + } + if (data) { + const { projects, teams, organizations } = data; + const projectTeams = teams.map(team => { + return { + id: team.id, + name: team.name, + projects: projects.filter(project => project.team.id === team.id), + }; + }); + return ( + <> + {projectTeams.map(team => ( + + {team.name} + + {team.projects.map((project, idx) => ( + + + + + + {project.name} + + + + ))} + + + ))} + + ); + } + return error; }; -const GlobalTopNavbar: React.FC = ({ projectID, name, projectMembers, onSaveProjectName }) => { - const { loading, data } = useMeQuery(); - const { showPopup, hidePopup, setTab } = usePopup(); - const history = useHistory(); - const { userID, setUserID } = useContext(UserIDContext); +type ProjectPopupProps = { + history: History; + name: string; + projectID: string; +}; + +export const ProjectPopup: React.FC = ({ history, name, projectID }) => { + const { hidePopup, setTab } = usePopup(); const [deleteProject] = useDeleteProjectMutation({ update: (client, deleteData) => { const cacheData: any = client.readQuery({ @@ -42,6 +190,62 @@ const GlobalTopNavbar: React.FC = ({ projectID, name, proj }); }, }); + return ( + <> + + { + setTab(1); + }} + /> + + + { + if (projectID) { + deleteProject({ variables: { projectID } }); + hidePopup(); + history.push('/projects'); + } + }} + /> + + + ); +}; + +type GlobalTopNavbarProps = { + nameOnly?: boolean; + projectID: string | null; + name: string | null; + initialTab?: number; + popupContent?: JSX.Element; + menuType?: Array; + projectMembers?: null | Array; + onSaveProjectName?: (projectName: string) => void; +}; + +const GlobalTopNavbar: React.FC = ({ + initialTab, + menuType, + projectID, + name, + popupContent, + projectMembers, + onSaveProjectName, + nameOnly, +}) => { + console.log(popupContent); + const { loading, data } = useMeQuery(); + const { showPopup, hidePopup, setTab } = usePopup(); + const history = useHistory(); + const [currentTab, setCurrentTab] = useState(initialTab); + useEffect(() => { + setCurrentTab(initialTab); + }, [initialTab]); + const { userID, setUserID } = useContext(UserIDContext); const onLogout = () => { fetch('http://localhost:3333/auth/logout', { method: 'POST', @@ -61,42 +265,26 @@ const GlobalTopNavbar: React.FC = ({ projectID, name, proj { + history.push('/admin'); + hidePopup(); + }} onProfile={() => { history.push('/profile'); hidePopup(); }} /> , - 185, + 195, ); }; const onOpenSettings = ($target: React.RefObject) => { - showPopup( - $target, - <> - - { - setTab(1, 325); - }} - /> - - - { - if (projectID) { - deleteProject({ variables: { projectID } }); - hidePopup(); - history.push('/projects'); - } - }} - /> - - , - 185, - ); + console.log('maybe firing popup'); + if (popupContent) { + console.log('showing popup'); + showPopup($target, popupContent, 185); + } }; if (!userID) { @@ -105,12 +293,25 @@ const GlobalTopNavbar: React.FC = ({ projectID, name, proj return ( <> { + showPopup( + $target, + + + , + ); + }} + currentTab={currentTab} user={data ? data.me : null} onNotificationClick={() => {}} + onDashboardClick={() => { + history.push('/'); + }} projectMembers={projectMembers} onProfileClick={onProfileClick} - onSaveProjectName={onSaveProjectName} + onSaveName={onSaveProjectName} onOpenSettings={onOpenSettings} /> diff --git a/web/src/App/index.tsx b/web/src/App/index.tsx index 502df34..2b17a8f 100644 --- a/web/src/App/index.tsx +++ b/web/src/App/index.tsx @@ -41,20 +41,19 @@ const App = () => { <> - - - - + + + + {loading ? (
loading
) : ( <> - )} -
-
+ +
diff --git a/web/src/Projects/Project/Details/index.tsx b/web/src/Projects/Project/Details/index.tsx index 90f0b44..1816eed 100644 --- a/web/src/Projects/Project/Details/index.tsx +++ b/web/src/Projects/Project/Details/index.tsx @@ -5,6 +5,9 @@ import PopupMenu, { Popup, usePopup } from 'shared/components/PopupMenu'; import MemberManager from 'shared/components/MemberManager'; import { useRouteMatch, useHistory } from 'react-router'; import { + useDeleteTaskChecklistMutation, + useUpdateTaskChecklistNameMutation, + useCreateTaskChecklistMutation, useFindTaskQuery, useUpdateTaskDueDateMutation, useSetTaskCompleteMutation, @@ -20,6 +23,82 @@ import UserIDContext from 'App/context'; import MiniProfile from 'shared/components/MiniProfile'; import DueDateManager from 'shared/components/DueDateManager'; import produce from 'immer'; +import styled from 'styled-components'; +import Button from 'shared/components/Button'; +import Input from 'shared/components/Input'; +import { useForm } from 'react-hook-form'; + +const calculateChecklistBadge = (draftState: any) => { + const total = draftState.checklists.reduce((prev: any, next: any) => { + return ( + prev + + next.items.reduce((innerPrev: any, _item: any) => { + return innerPrev + 1; + }, 0) + ); + }, 0); + const complete = draftState.checklists.reduce( + (prev: any, next: any) => + prev + + next.items.reduce((innerPrev: any, item: any) => { + return innerPrev + (item.complete ? 1 : 0); + }, 0), + 0, + ); + return { total, complete }; +}; + +const DeleteChecklistButton = styled(Button)` + width: 100%; + padding: 6px 12px; + margin-top: 8px; +`; +type CreateChecklistData = { + name: string; +}; +const CreateChecklistForm = styled.form` + display: flex; + flex-direction: column; +`; + +const CreateChecklistButton = styled(Button)` + margin-top: 8px; + padding: 6px 12px; + width: 100%; +`; + +const CreateChecklistInput = styled(Input)` + margin-bottom: 8px; +`; + +const InputError = styled.span` + color: rgba(${props => props.theme.colors.danger}); + font-size: 12px; +`; +type CreateChecklistPopupProps = { + onCreateChecklist: (data: CreateChecklistData) => void; +}; +const CreateChecklistPopup: React.FC = ({ onCreateChecklist }) => { + const { register, handleSubmit, errors } = useForm(); + const createUser = (data: CreateChecklistData) => { + onCreateChecklist(data); + }; + console.log(errors); + return ( + + + Create + + ); +}; type DetailsProps = { taskID: string; @@ -50,8 +129,93 @@ const Details: React.FC = ({ const match = useRouteMatch(); const [currentMemberTask, setCurrentMemberTask] = useState(''); const [memberPopupData, setMemberPopupData] = useState(initialMemberPopupState); - const [setTaskChecklistItemComplete] = useSetTaskChecklistItemCompleteMutation(); + const [setTaskChecklistItemComplete] = useSetTaskChecklistItemCompleteMutation({ + update: client => { + const cacheData: any = client.readQuery({ + query: FindTaskDocument, + variables: { taskID }, + }); + const newData = produce(cacheData.findTask, (draftState: any) => { + const { complete, total } = calculateChecklistBadge(draftState); + if (!draftState.badges) { + draftState.badges = { + checklist: {}, + }; + } + draftState.badges.checklist = { + __typename: 'ChecklistBadge', + complete, + total, + }; + }); + client.writeQuery({ + query: FindTaskDocument, + variables: { taskID }, + data: { + findTask: newData, + }, + }); + }, + }); + const [deleteTaskChecklist] = useDeleteTaskChecklistMutation({ + update: (client, deleteData) => { + const cacheData: any = client.readQuery({ + query: FindTaskDocument, + variables: { taskID }, + }); + console.log(deleteData); + + const newData = produce(cacheData.findTask, (draftState: any) => { + draftState.checklists = cacheData.findTask.checklists.filter( + (checklist: any) => checklist.id !== deleteData.data.deleteTaskChecklist.taskChecklist.id, + ); + const { complete, total } = calculateChecklistBadge(draftState); + if (!draftState.badges) { + draftState.badges = { + checklist: {}, + }; + } + draftState.badges.checklist = { + __typename: 'ChecklistBadge', + complete, + total, + }; + if (complete === 0 && total === 0) { + draftState.badges.checklist = null; + } + }); + client.writeQuery({ + query: FindTaskDocument, + variables: { taskID }, + data: { + findTask: newData, + }, + }); + }, + }); const [updateTaskChecklistItemName] = useUpdateTaskChecklistItemNameMutation(); + const [createTaskChecklist] = useCreateTaskChecklistMutation({ + update: (client, createData) => { + const cacheData: any = client.readQuery({ + query: FindTaskDocument, + variables: { taskID }, + }); + console.log(createData); + + const newData = { + findTask: { + ...cacheData.findTask, + checklists: [...cacheData.findTask.checklists, { ...createData.data.createTaskChecklist }], + }, + }; + client.writeQuery({ + query: FindTaskDocument, + variables: { taskID }, + data: newData, + }); + }, + }); + const [updateTaskChecklistName] = useUpdateTaskChecklistNameMutation(); const [deleteTaskChecklistItem] = useDeleteTaskChecklistItemMutation({ update: (client, deleteData) => { const cacheData: any = client.readQuery({ @@ -69,6 +233,17 @@ const Details: React.FC = ({ draftState.checklists[idx].items = cacheData.findTask.checklists[idx].items.filter( (item: any) => item.id !== deleteData.data.deleteTaskChecklistItem.taskChecklistItem.id, ); + const { complete, total } = calculateChecklistBadge(draftState); + if (!draftState.badges) { + draftState.badges = { + checklist: {}, + }; + } + draftState.badges.checklist = { + __typename: 'ChecklistBadge', + complete, + total, + }; } }); client.writeQuery({ @@ -97,8 +272,23 @@ const Details: React.FC = ({ ...cacheData.findTask.checklists[idx].items, { ...newTaskItem.data.createTaskChecklistItem }, ]; + console.log(draftState.checklists.map((item: any) => item.items)); + const { complete, total } = calculateChecklistBadge(draftState); + if (!draftState.badges) { + draftState.badges = { + checklist: {}, + }; + } + draftState.badges.checklist = { + __typename: 'ChecklistBadge', + complete, + total, + }; + console.log(draftState.badges.checklist); } }); + + console.log(newData); client.writeQuery({ query: FindTaskDocument, variables: { taskID }, @@ -156,11 +346,24 @@ const Details: React.FC = ({ updateTaskChecklistItemName({ variables: { taskChecklistItemID: itemID, name: itemName } }); }} onCloseModal={() => history.push(projectURL)} + onChangeChecklistName={(checklistID, newName) => { + updateTaskChecklistName({ variables: { taskChecklistID: checklistID, name: newName } }); + }} onDeleteItem={itemID => { deleteTaskChecklistItem({ variables: { taskChecklistItemID: itemID } }); }} onToggleChecklistItem={(itemID, complete) => { - setTaskChecklistItemComplete({ variables: { taskChecklistItemID: itemID, complete } }); + setTaskChecklistItemComplete({ + variables: { taskChecklistItemID: itemID, complete }, + optimisticResponse: { + __typename: 'Mutation', + setTaskChecklistItemComplete: { + __typename: 'TaskChecklistItem', + id: itemID, + complete, + }, + }, + }); }} onAddItem={(taskChecklistID, name, position) => { createTaskChecklistItem({ variables: { taskChecklistID, name, position } }); @@ -202,6 +405,57 @@ const Details: React.FC = ({ ); }} onOpenAddLabelPopup={onOpenAddLabelPopup} + onOpenAddChecklistPopup={(_task, $target) => { + showPopup( + $target, + { + hidePopup(); + }} + > + { + let position = 65535; + console.log(data.findTask.checklists); + if (data.findTask.checklists) { + const [lastChecklist] = data.findTask.checklists.slice(-1); + console.log(`lastCheclist ${lastChecklist}`); + if (lastChecklist) { + position = lastChecklist.position * 2 + 1; + } + } + createTaskChecklist({ + variables: { + taskID: data.findTask.id, + name: checklistData.name, + position, + }, + }); + hidePopup(); + }} + /> + , + ); + }} + onDeleteChecklist={($target, checklistID) => { + showPopup( + $target, + hidePopup()}> +

Deleting a checklist is permanent and there is no way to get it back.

+ { + deleteTaskChecklist({ variables: { taskChecklistID: checklistID } }); + hidePopup(); + }} + > + Delete Checklist + +
, + ); + }} onOpenDueDatePopop={(task, $targetRef) => { showPopup( $targetRef, diff --git a/web/src/Projects/Project/index.tsx b/web/src/Projects/Project/index.tsx index bcc86d1..80cdd86 100644 --- a/web/src/Projects/Project/index.tsx +++ b/web/src/Projects/Project/index.tsx @@ -1,7 +1,9 @@ import React, { useState, useRef, useContext, useEffect } from 'react'; -import GlobalTopNavbar from 'App/TopNavbar'; +import { MENU_TYPES } from 'shared/components/TopNavbar'; +import GlobalTopNavbar, { ProjectPopup } from 'App/TopNavbar'; import styled from 'styled-components/macro'; -import { Bolt, ToggleOn, Tags } from 'shared/icons'; +import { DataProxy } from '@apollo/client'; +import { Bolt, ToggleOn, Tags, CheckCircle, Sort, Filter } from 'shared/icons'; import { usePopup, Popup } from 'shared/components/PopupMenu'; import { useParams, Route, useRouteMatch, useHistory, RouteComponentProps } from 'react-router-dom'; import { @@ -26,6 +28,7 @@ import { useCreateProjectLabelMutation, useUnassignTaskMutation, useUpdateTaskDueDateMutation, + FindProjectQuery, } from 'shared/generated/graphql'; import TaskAssignee from 'shared/components/TaskAssignee'; @@ -42,11 +45,35 @@ import produce from 'immer'; import MiniProfile from 'shared/components/MiniProfile'; import Details from './Details'; import { useApolloClient } from '@apollo/react-hooks'; +import { DocumentNode } from 'graphql'; import UserIDContext from 'App/context'; import DueDateManager from 'shared/components/DueDateManager'; +type UpdateCacheFn = (cache: T) => T; +function updateApolloCache(client: DataProxy, document: DocumentNode, update: UpdateCacheFn, variables?: object) { + let queryArgs: DataProxy.Query; + if (variables) { + queryArgs = { + query: document, + variables, + }; + } else { + queryArgs = { + query: document, + }; + } + const cache: T | null = client.readQuery(queryArgs); + if (cache) { + const newCache = update(cache); + client.writeQuery({ + ...queryArgs, + data: newCache, + }); + } +} + const getCacheData = (client: any, projectID: string) => { - const cacheData: any = client.readQuery({ + const cacheData: FindProjectQuery = client.readQuery({ query: FindProjectDocument, variables: { projectId: projectID, @@ -220,7 +247,7 @@ const initialQuickCardEditorState: QuickCardEditorState = { const ProjectBar = styled.div` display: flex; align-items: center; - justify-content: flex-end; + justify-content: space-between; height: 40px; padding: 0 12px; `; @@ -302,13 +329,17 @@ const Project = () => { const [createTaskGroup] = useCreateTaskGroupMutation({ onCompleted: newTaskGroupData => {}, update: (client, newTaskGroupData) => { - const cacheData = getCacheData(client, projectID); - const newData = { - ...cacheData.findProject, - taskGroups: [...cacheData.findProject.taskGroups, { ...newTaskGroupData.data.createTaskGroup, tasks: [] }], - }; - - writeCacheData(client, projectID, cacheData, newData); + updateApolloCache( + client, + FindProjectDocument, + cache => { + console.log(cache); + return produce(cache, draftCache => { + draftCache.findProject.taskGroups.push({ ...newTaskGroupData.data.createTaskGroup, tasks: [] }); + }); + }, + { projectId: projectID }, + ); }, }); @@ -316,7 +347,7 @@ const Project = () => { onCompleted: newTaskData => {}, update: (client, newTaskData) => { const cacheData = getCacheData(client, projectID); - const newTaskGroups = produce(cacheData.findProject.taskGroups, (draftState: any) => { + const newTaskGroups = produce(cacheData.findProject.taskGroups, draftState => { const targetIndex = draftState.findIndex( (taskGroup: any) => taskGroup.id === newTaskData.data.createTask.taskGroup.id, ); @@ -388,14 +419,18 @@ const Project = () => { createTask: { __typename: 'Task', id: '' + Math.round(Math.random() * -1000000), - name: name, + name, + complete: false, taskGroup: { __typename: 'TaskGroup', id: taskGroup.id, name: taskGroup.name, position: taskGroup.position, }, - position: position, + badges: { + checklist: null, + }, + position, dueDate: null, description: null, labels: [], @@ -509,11 +544,28 @@ const Project = () => { onSaveProjectName={projectName => { updateProjectName({ variables: { projectID, name: projectName } }); }} + popupContent={} + menuType={MENU_TYPES.PROJECT_MENU} + initialTab={0} projectMembers={data.findProject.members} projectID={projectID} name={data.findProject.name} /> + + + + All Tasks + + + + Filter + + + + Sort + + ` `; const Wrapper = styled.div` + position: relative; display: flex; flex-direction: row; align-items: flex-start; @@ -146,10 +147,24 @@ const Wrapper = styled.div` const ProjectSectionTitleWrapper = styled.div` align-items: center; display: flex; + justify-content: space-between; height: 32px; margin-bottom: 24px; padding: 8px 0; position: relative; + margin-top: 16px; +`; + +const SectionActions = styled.div` + display: flex; + align-items: center; +`; + +const SectionAction = styled(Button)` + padding: 6px 12px; +`; +const SectionActionLink = styled(Link)` + margin-right: 8px; `; const ProjectSectionTitle = styled.h3` @@ -171,13 +186,19 @@ const ProjectGrid = styled.div` `; const AddTeamButton = styled(Button)` padding: 6px 12px; - float: right; + position: absolute; + top: 6px; + right: 12px; `; +type ShowNewProject = { + open: boolean; + initialTeamID: null | string; +}; const ProjectLink = styled(Link)``; const Projects = () => { - const { showPopup } = usePopup(); + const { showPopup, hidePopup } = usePopup(); const { loading, data } = useGetProjectsQuery(); useEffect(() => { document.title = 'Citadel'; @@ -202,9 +223,24 @@ const Projects = () => { }); }, }); - const [showNewProject, setShowNewProject] = useState(false); + + const [showNewProject, setShowNewProject] = useState({ open: false, initialTeamID: null }); const { userID, setUserID } = useContext(UserIDContext); - const [createTeam] = useCreateTeamMutation(); + const [createTeam] = useCreateTeamMutation({ + update: (client, createData) => { + const cacheData: any = client.readQuery({ + query: GetProjectsDocument, + }); + const newData = { + ...cacheData, + teams: [...cacheData.teams, { ...createData.data.createTeam }], + }; + client.writeQuery({ + query: GetProjectsDocument, + data: newData, + }); + }, + }); if (loading) { return ( <> @@ -234,11 +270,18 @@ const Projects = () => { onClick={$target => { showPopup( $target, - + { + hidePopup(); + }} + > { if (organizationID) { createTeam({ variables: { name: teamName, organizationID } }); + hidePopup(); } }} /> @@ -253,6 +296,17 @@ const Projects = () => {
{team.name} + + + Projects + + + Members + + + Settings + + {team.projects.map((project, idx) => ( @@ -268,7 +322,7 @@ const Projects = () => { { - setShowNewProject(true); + setShowNewProject({ open: true, initialTeamID: team.id }); }} > @@ -281,16 +335,17 @@ const Projects = () => {
); })} - {showNewProject && ( + {showNewProject.open && ( { if (userID) { createProject({ variables: { teamID, name, userID } }); - setShowNewProject(false); + setShowNewProject({ open: false, initialTeamID: null }); } }} onClose={() => { - setShowNewProject(false); + setShowNewProject({ open: false, initialTeamID: null }); }} teams={teams} /> diff --git a/web/src/Teams/index.tsx b/web/src/Teams/index.tsx new file mode 100644 index 0000000..d7cd199 --- /dev/null +++ b/web/src/Teams/index.tsx @@ -0,0 +1,227 @@ +import React, { useState, useContext, useEffect } from 'react'; +import styled from 'styled-components/macro'; +import { MENU_TYPES } from 'shared/components/TopNavbar'; +import GlobalTopNavbar from 'App/TopNavbar'; +import { useGetTeamQuery, useDeleteTeamMutation, GetProjectsDocument } from 'shared/generated/graphql'; +import { useParams, useHistory } from 'react-router'; +import { usePopup, Popup } from 'shared/components/PopupMenu'; +import { History } from 'history'; +import produce from 'immer'; +import { TeamSettings, DeleteConfirm, DELETE_INFO } from 'shared/components/ProjectSettings'; +import { Link } from 'react-router-dom'; + +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; +`; + +type TeamPopupProps = { + history: History; + name: string; + teamID: string; +}; + +export const TeamPopup: React.FC = ({ history, name, teamID }) => { + const { hidePopup, setTab } = usePopup(); + const [deleteTeam] = useDeleteTeamMutation({ + update: (client, deleteData) => { + const cacheData: any = client.readQuery({ + query: GetProjectsDocument, + }); + + console.log(cacheData); + console.log(deleteData); + + const newData = produce(cacheData, (draftState: any) => { + draftState.teams = draftState.teams.filter((team: any) => team.id !== deleteData.data.deleteTeam.team.id); + draftState.projects = draftState.projects.filter( + (project: any) => project.team.id !== deleteData.data.deleteTeam.team.id, + ); + }); + + client.writeQuery({ + query: GetProjectsDocument, + data: { + ...newData, + }, + }); + }, + }); + return ( + <> + + { + setTab(1, 340); + }} + /> + + hidePopup()}> + { + if (teamID) { + deleteTeam({ variables: { teamID } }); + hidePopup(); + history.push('/projects'); + } + }} + /> + + + ); +}; + +const ProjectsContainer = styled.div` + margin: 40px 16px 0; + width: 100%; + max-width: 825px; + min-width: 288px; +`; + +type TeamsRouteProps = { + teamID: string; +}; + +const colors = ['#e362e3', '#7a6ff0', '#37c5ab', '#aa62e3', '#e8384f']; +const Projects = () => { + const { teamID } = useParams(); + const history = useHistory(); + const { loading, data } = useGetTeamQuery({ variables: { teamID } }); + useEffect(() => { + document.title = 'Citadel | Teams'; + }, []); + if (loading) { + return ( + <> + loading + + ); + } + + if (data) { + return ( + <> + } + onSaveProjectName={() => {}} + projectID={null} + name={data.findTeam.name} + /> + + + + {data.projects.map((project, idx) => ( + + + + + {project.name} + + + + ))} + + + + + ); + } + return
Error!
; +}; + +export default Projects; diff --git a/web/src/citadel.d.ts b/web/src/citadel.d.ts index ffb929f..a9bd4f3 100644 --- a/web/src/citadel.d.ts +++ b/web/src/citadel.d.ts @@ -17,6 +17,15 @@ type ContextMenuEvent = { taskGroupID: string; }; +type User = { + id: string; + fullName: string; + username: string; + email: string; + role: string; + profileIcon: ProfileIcon; +}; + type TaskUser = { id: string; fullName: string; diff --git a/web/src/projects.d.ts b/web/src/projects.d.ts index 0418a1f..44bd43b 100644 --- a/web/src/projects.d.ts +++ b/web/src/projects.d.ts @@ -47,10 +47,20 @@ type TaskChecklistItem = { dueDate?: null | string; }; +type ChecklistBadge = { + complete: number; + total: number; +}; + +type TaskBadges = { + checklist?: ChecklistBadge | null; +}; + type Task = { id: string; taskGroup: InnerTaskGroup; name: string; + badges?: TaskBadges; position: number; dueDate?: string; complete?: boolean; diff --git a/web/src/shared/components/Admin/Admin.stories.tsx b/web/src/shared/components/Admin/Admin.stories.tsx index a037f8c..2591c72 100644 --- a/web/src/shared/components/Admin/Admin.stories.tsx +++ b/web/src/shared/components/Admin/Admin.stories.tsx @@ -1,7 +1,10 @@ import React, { useRef } from 'react'; import Admin from '.'; +import { theme } from 'App/ThemeStyles'; import NormalizeStyles from 'App/NormalizeStyles'; import BaseStyles from 'App/BaseStyles'; +import { ThemeProvider } from 'styled-components'; +import { action } from '@storybook/addon-actions'; export default { component: Admin, @@ -19,7 +22,27 @@ export const Default = () => { <> - + + + ); }; diff --git a/web/src/shared/components/Admin/index.tsx b/web/src/shared/components/Admin/index.tsx index c3fb76b..74f4b25 100644 --- a/web/src/shared/components/Admin/index.tsx +++ b/web/src/shared/components/Admin/index.tsx @@ -1,34 +1,28 @@ import React, { useState, useRef } from 'react'; import styled from 'styled-components'; -import { User, Plus } from 'shared/icons'; +import { User, Plus, Lock, Pencil, Trash } from 'shared/icons'; import { AgGridReact } from 'ag-grid-react'; import 'ag-grid-community/dist/styles/ag-grid.css'; import 'ag-grid-community/dist/styles/ag-theme-material.css'; +import Button from 'shared/components/Button'; -const NewUserButton = styled.button` - outline: none; - border: none; - cursor: pointer; - line-height: 20px; - padding: 0.75rem; - background-color: transparent; - display: flex; - align-items: center; - justify-content: center; - color: rgba(115, 103, 240); - font-size: 14px; - - border-radius: 0.5rem; - border-width: 1px; - border-style: solid; - border-color: transparent; - border-image: initial; - border-color: rgba(115, 103, 240); - span { - padding-left: 0.5rem; - } +const NewUserButton = styled(Button)` + padding: 6px 12px; + margin-right: 12px; `; + +const InviteUserButton = styled(Button)` + padding: 6px 12px; + margin-right: 8px; +`; + +const MemberActions = styled.div` + display: flex; + justify-content: flex-end; + align-items: center; +`; + const GridTable = styled.div` height: 620px; `; @@ -93,8 +87,29 @@ const Header = styled.div` min-height: 112px; `; +const ActionButtonsContainer = styled.div` + display: flex; + align-items: center; +`; + +const EditUserIcon = styled(Pencil)` + margin-right: 8px; +`; + +const LockUserIcon = styled(Lock)` + margin-right: 8px; +`; + +const DeleteUserIcon = styled(Trash)``; + const ActionButtons = () => { - return Hello!; + return ( + <> + + + + + ); }; const data = { defaultColDef: { @@ -103,16 +118,14 @@ const data = { }, columnDefs: [ { - minWidth: 125, - width: 125, + minWidth: 55, + width: 55, headerCheckboxSelection: true, checkboxSelection: true, - headerName: 'ID', - field: 'id', }, { minWidth: 210, headerName: 'Username', editable: true, field: 'username' }, { minWidth: 225, headerName: 'Email', field: 'email' }, - { minWidth: 200, headerName: 'Name', editable: true, field: 'full_name' }, + { minWidth: 200, headerName: 'Name', editable: true, field: 'fullName' }, { minWidth: 200, headerName: 'Role', editable: true, field: 'role' }, { minWidth: 200, @@ -123,14 +136,13 @@ const data = { frameworkComponents: { actionButtons: ActionButtons, }, - rowData: [ - { id: '1', full_name: 'Jordan Knott', username: 'jordan', email: 'jordan@jordanthedev.com', role: 'Admin' }, - { id: '2', full_name: 'Jordan Test', username: 'jordantest', email: 'jordan@jordanthedev.com', role: 'Admin' }, - { id: '3', full_name: 'Jordan Other', username: 'alphatest1050', email: 'jordan@jordanthedev.com', role: 'Admin' }, - { id: '5', full_name: 'Jordan French', username: 'other', email: 'jordan@jordanthedev.com', role: 'Admin' }, - ], }; -const ListTable = () => { + +type ListTableProps = { + users: Array; +}; + +const ListTable: React.FC = ({ users }) => { return (
@@ -138,7 +150,7 @@ const ListTable = () => { rowSelection="multiple" defaultColDef={data.defaultColDef} columnDefs={data.columnDefs} - rowData={data.rowData} + rowData={users} frameworkComponents={data.frameworkComponents} onFirstDataRendered={params => { params.api.sizeColumnsToFit(); @@ -146,7 +158,7 @@ const ListTable = () => { onGridSizeChanged={params => { params.api.sizeColumnsToFit(); }} - > + />
); @@ -184,6 +196,7 @@ const TabNavContent = styled.ul` const TabNavItem = styled.li` padding: 0.35rem 0.3rem; + height: 48px; display: block; position: relative; `; @@ -282,9 +295,16 @@ const NavItem: React.FC = ({ active, name, tab, onClick }) => { ); }; -const Admin = () => { - const [currentTop, setTop] = useState(0); - const [currentTab, setTab] = useState(0); +type AdminProps = { + initialTab: number; + onAddUser: ($target: React.RefObject) => void; + onInviteUser: ($target: React.RefObject) => void; + users: Array; +}; + +const Admin: React.FC = ({ initialTab, onAddUser, onInviteUser, users }) => { + const [currentTop, setTop] = useState(initialTab * 48); + const [currentTab, setTab] = useState(initialTab); const $tabNav = useRef(null); return ( @@ -309,11 +329,17 @@ const Admin = () => { - - - Add New - - + + + + Create member + + + + Invite member + + + diff --git a/web/src/shared/components/Card/index.tsx b/web/src/shared/components/Card/index.tsx index a423637..9dcbfa7 100644 --- a/web/src/shared/components/Card/index.tsx +++ b/web/src/shared/components/Card/index.tsx @@ -42,7 +42,7 @@ type Props = { onClick?: (e: React.MouseEvent) => void; description?: null | string; dueDate?: DueDate; - checklists?: Checklist; + checklists?: Checklist | null; labels?: Array; watched?: boolean; wrapperProps?: any; diff --git a/web/src/shared/components/Checklist/index.tsx b/web/src/shared/components/Checklist/index.tsx index 85b1f0f..388e871 100644 --- a/web/src/shared/components/Checklist/index.tsx +++ b/web/src/shared/components/Checklist/index.tsx @@ -501,7 +501,7 @@ const ChecklistTitleEditor = React.forwardRef( ); type ChecklistProps = { checklistID: string; - onDeleteChecklist: (checklistID: string) => void; + onDeleteChecklist: ($target: React.RefObject, checklistID: string) => void; name: string; onChangeName: (item: string) => void; onToggleItem: (taskID: string, complete: boolean) => void; @@ -554,8 +554,8 @@ const Checklist: React.FC = ({ setEditting(true)}>{name} { - onDeleteChecklist(checklistID); + onClick={$target => { + onDeleteChecklist($target, checklistID); }} color="danger" variant="outline" diff --git a/web/src/shared/components/DropdownMenu/DropdownMenu.stories.tsx b/web/src/shared/components/DropdownMenu/DropdownMenu.stories.tsx index f3e5218..1b4f5ea 100644 --- a/web/src/shared/components/DropdownMenu/DropdownMenu.stories.tsx +++ b/web/src/shared/components/DropdownMenu/DropdownMenu.stories.tsx @@ -51,6 +51,7 @@ export const Default = () => { {menu.isOpen && ( { setMenu({ top: 0, left: 0, isOpen: false }); }} diff --git a/web/src/shared/components/DropdownMenu/index.tsx b/web/src/shared/components/DropdownMenu/index.tsx index 45e6038..53d10ed 100644 --- a/web/src/shared/components/DropdownMenu/index.tsx +++ b/web/src/shared/components/DropdownMenu/index.tsx @@ -1,6 +1,6 @@ import React, { useRef } from 'react'; import useOnOutsideClick from 'shared/hooks/onOutsideClick'; -import { Exit, User } from 'shared/icons'; +import { Exit, User, Cog } from 'shared/icons'; import { Separator, Container, WrapperDiamond, Wrapper, ActionsList, ActionItem, ActionTitle } from './Styles'; type DropdownMenuProps = { @@ -8,15 +8,16 @@ type DropdownMenuProps = { top: number; onLogout: () => void; onCloseDropdown: () => void; + onAdminConsole: () => void; }; -const DropdownMenu: React.FC = ({ left, top, onLogout, onCloseDropdown }) => { +const DropdownMenu: React.FC = ({ left, top, onLogout, onCloseDropdown, onAdminConsole }) => { const $containerRef = useRef(null); useOnOutsideClick($containerRef, true, onCloseDropdown, null); return ( - + Profile @@ -36,16 +37,21 @@ const DropdownMenu: React.FC = ({ left, top, onLogout, onClos type ProfileMenuProps = { onProfile: () => void; onLogout: () => void; + onAdminConsole: () => void; }; -const ProfileMenu: React.FC = ({ onProfile, onLogout }) => { +const ProfileMenu: React.FC = ({ onAdminConsole, onProfile, onLogout }) => { return ( <> + + + Admin Console + + Profile - diff --git a/web/src/shared/components/Input/index.tsx b/web/src/shared/components/Input/index.tsx index 4d654c3..a6f8826 100644 --- a/web/src/shared/components/Input/index.tsx +++ b/web/src/shared/components/Input/index.tsx @@ -80,8 +80,10 @@ type InputProps = { variant?: 'normal' | 'alternate'; label?: string; width?: string; + floatingLabel?: boolean; placeholder?: string; icon?: JSX.Element; + autocomplete?: boolean; id?: string; name?: string; className?: string; @@ -95,6 +97,7 @@ const Input = React.forwardRef( { width = 'auto', variant = 'normal', + autocomplete, label, placeholder, icon, @@ -102,6 +105,7 @@ const Input = React.forwardRef( onChange, className, onClick, + floatingLabel, value: initialValue, id, }: InputProps, @@ -124,12 +128,13 @@ const Input = React.forwardRef( return ( = ({ onClick={() => { onTaskClick(task); }} + checklists={task.badges && task.badges.checklist} onCardMemberClick={onCardMemberClick} onContextMenu={onQuickEditorOpen} /> diff --git a/web/src/shared/components/Login/index.tsx b/web/src/shared/components/Login/index.tsx index d0a9dfd..bb64406 100644 --- a/web/src/shared/components/Login/index.tsx +++ b/web/src/shared/components/Login/index.tsx @@ -38,7 +38,7 @@ const Login = ({ onSubmit }: LoginProps) => { - + Citadel Login @@ -66,7 +66,7 @@ const Login = ({ onSubmit }: LoginProps) => { ref={register({ required: 'Password is required' })} /> - + {errors.password && {errors.password.message}} diff --git a/web/src/shared/components/Navbar/Navbar.stories.tsx b/web/src/shared/components/Navbar/Navbar.stories.tsx index 701d3b8..8c482bb 100644 --- a/web/src/shared/components/Navbar/Navbar.stories.tsx +++ b/web/src/shared/components/Navbar/Navbar.stories.tsx @@ -28,10 +28,10 @@ export const Default = () => { - + - + diff --git a/web/src/shared/components/Navbar/index.tsx b/web/src/shared/components/Navbar/index.tsx index e171c0d..cc11a5e 100644 --- a/web/src/shared/components/Navbar/index.tsx +++ b/web/src/shared/components/Navbar/index.tsx @@ -35,7 +35,7 @@ export const ButtonContainer: React.FC = ({ children }) => ( export const PrimaryLogo = () => { return ( - + Citadel ); diff --git a/web/src/shared/components/NewProject/NewProject.stories.tsx b/web/src/shared/components/NewProject/NewProject.stories.tsx index fe71860..89fbf56 100644 --- a/web/src/shared/components/NewProject/NewProject.stories.tsx +++ b/web/src/shared/components/NewProject/NewProject.stories.tsx @@ -23,6 +23,7 @@ export const Default = () => { {}} diff --git a/web/src/shared/components/NewProject/index.tsx b/web/src/shared/components/NewProject/index.tsx index bd05292..8c0333e 100644 --- a/web/src/shared/components/NewProject/index.tsx +++ b/web/src/shared/components/NewProject/index.tsx @@ -207,14 +207,15 @@ const CreateButton = styled.button` } `; type NewProjectProps = { + initialTeamID: string | null; teams: Array; onClose: () => void; onCreateProject: (projectName: string, teamID: string) => void; }; -const NewProject: React.FC = ({ teams, onClose, onCreateProject }) => { +const NewProject: React.FC = ({ initialTeamID, teams, onClose, onCreateProject }) => { const [projectName, setProjectName] = useState(''); - const [team, setTeam] = useState(null); + const [team, setTeam] = useState(initialTeamID); const options = teams.map(t => ({ label: t.name, value: t.id })); return ( diff --git a/web/src/shared/components/PopupMenu/LabelManager.tsx b/web/src/shared/components/PopupMenu/LabelManager.tsx index 3f77238..89f7ed5 100644 --- a/web/src/shared/components/PopupMenu/LabelManager.tsx +++ b/web/src/shared/components/PopupMenu/LabelManager.tsx @@ -58,7 +58,7 @@ const LabelManager: React.FC = ({ labels, taskLabels, onLabelToggle, onLa onLabelEdit(label.id); }} > - + ` `; export const CloseButton = styled.div` - padding: 10px 12px 10px 8px; + padding: 18px 18px 14px 12px; position: absolute; top: 0; right: 0; @@ -312,7 +318,7 @@ export const CreateLabelButton = styled.button` `; export const PreviousButton = styled.div` - padding: 10px 12px 10px 8px; + padding: 18px 18px 14px 12px; position: absolute; top: 0; left: 0; diff --git a/web/src/shared/components/ProfileIcon/index.tsx b/web/src/shared/components/ProfileIcon/index.tsx index 4a53224..8b20c78 100644 --- a/web/src/shared/components/ProfileIcon/index.tsx +++ b/web/src/shared/components/ProfileIcon/index.tsx @@ -2,7 +2,6 @@ import React, { useRef } from 'react'; import styled from 'styled-components'; export const Container = styled.div<{ size: number | string; bgColor: string | null; backgroundURL: string | null }>` - margin-left: 10px; width: ${props => props.size}px; height: ${props => props.size}px; border-radius: 9999px; diff --git a/web/src/shared/components/ProjectSettings/index.tsx b/web/src/shared/components/ProjectSettings/index.tsx index 9c2435a..d292389 100644 --- a/web/src/shared/components/ProjectSettings/index.tsx +++ b/web/src/shared/components/ProjectSettings/index.tsx @@ -52,6 +52,21 @@ const ProjectSettings: React.FC = ({ onDeleteProject }) => { ); }; +type TeamSettingsProps = { + onDeleteTeam: () => void; +}; +export const TeamSettings: React.FC = ({ onDeleteTeam }) => { + return ( + <> + + onDeleteTeam()}> + Delete Team + + + + ); +}; + const ConfirmWrapper = styled.div``; const ConfirmSubTitle = styled.h3` @@ -76,25 +91,40 @@ const ConfirmDeleteButton = styled(Button)` padding: 6px 12px; `; -type DeleteProjectProps = { - name: string; - onDeleteProject: () => void; +type DeleteConfirmProps = { + description: string; + deletedItems: Array; + onConfirmDelete: () => void; }; -const DeleteProject: React.FC = ({ name, onDeleteProject }) => { + +export const DELETE_INFO = { + DELETE_PROJECTS: { + description: 'Deleting the project will also delete the following:', + deletedItems: ['Task groups and tasks'], + }, + DELETE_TEAMS: { + description: 'Deleting the team will also delete the following:', + deletedItems: ['Projects under the team', 'All task groups & tasks associated with the team projects'], + }, +}; + +const DeleteConfirm: React.FC = ({ description, deletedItems, onConfirmDelete }) => { return ( - Deleting the project will also delete the following: + {description} - Task groups and tasks + {deletedItems.map(item => ( + {item} + ))} - onDeleteProject()} color="danger"> + onConfirmDelete()} color="danger"> Delete ); }; -export { DeleteProject }; +export { DeleteConfirm }; export default ProjectSettings; diff --git a/web/src/shared/components/TaskDetails/TaskDetails.stories.tsx b/web/src/shared/components/TaskDetails/TaskDetails.stories.tsx index ddccaff..76f3c02 100644 --- a/web/src/shared/components/TaskDetails/TaskDetails.stories.tsx +++ b/web/src/shared/components/TaskDetails/TaskDetails.stories.tsx @@ -71,6 +71,9 @@ export const Default = () => { onToggleTaskComplete={action('toggle task complete')} onToggleChecklistItem={action('toggle checklist item')} onOpenAddLabelPopup={action('open add label popup')} + onChangeChecklistName={action('change checklist name')} + onDeleteChecklist={action('delete checklist')} + onOpenAddChecklistPopup={action(' open checklist')} onOpenDueDatePopop={action('open due date popup')} /> ); diff --git a/web/src/shared/components/TaskDetails/index.tsx b/web/src/shared/components/TaskDetails/index.tsx index 0be4433..d8c936d 100644 --- a/web/src/shared/components/TaskDetails/index.tsx +++ b/web/src/shared/components/TaskDetails/index.tsx @@ -140,13 +140,19 @@ type TaskDetailsProps = { onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject) => void; onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject) => void; onOpenDueDatePopop: (task: Task, $targetRef: React.RefObject) => void; + onOpenAddChecklistPopup: (task: Task, $targetRef: React.RefObject) => void; onMemberProfile: ($targetRef: React.RefObject, memberID: string) => void; + onChangeChecklistName: (checklistID: string, name: string) => void; + onDeleteChecklist: ($target: React.RefObject, checklistID: string) => void; onCloseModal: () => void; }; const TaskDetails: React.FC = ({ task, + onDeleteChecklist, onTaskNameChange, + onOpenAddChecklistPopup, + onChangeChecklistName, onToggleTaskComplete, onTaskDescriptionChange, onChangeItemName, @@ -183,6 +189,9 @@ const TaskDetails: React.FC = ({ const onAddMember = ($target: React.RefObject) => { onOpenAddMemberPopup(task, $target); }; + const onAddChecklist = ($target: React.RefObject) => { + onOpenAddChecklistPopup(task, $target) + } const $dueDateLabel = useRef(null); const $addLabelRef = useRef(null); @@ -290,16 +299,16 @@ const TaskDetails: React.FC = ({ name={checklist.name} checklistID={checklist.id} items={checklist.items} - onDeleteChecklist={() => {}} - onChangeName={() => {}} + onDeleteChecklist={onDeleteChecklist} + onChangeName={newName => onChangeChecklistName(checklist.id, newName)} onToggleItem={onToggleChecklistItem} onDeleteItem={onDeleteItem} onAddItem={n => { if (task.checklists) { - let position = 1; - const lastChecklist = task.checklists.sort((a, b) => a.position - b.position)[-1]; - if (lastChecklist) { - position = lastChecklist.position * 2 + 1; + let position = 65535; + const [lastItem] = checklist.items.sort((a, b) => a.position - b.position).slice(-1); + if (lastItem) { + position = lastItem.position * 2 + 1; } onAddItem(checklist.id, n, position); } @@ -317,7 +326,7 @@ const TaskDetails: React.FC = ({ onAddMember($target)}>Members onAddLabel($target)}>Labels - Checklist + onAddChecklist($target)}>Checklist onOpenDueDatePopop(task, $target)}>Due Date Attachment Cover diff --git a/web/src/shared/components/TopNavbar/Styles.ts b/web/src/shared/components/TopNavbar/Styles.ts index 8f4a999..5330e4f 100644 --- a/web/src/shared/components/TopNavbar/Styles.ts +++ b/web/src/shared/components/TopNavbar/Styles.ts @@ -2,6 +2,8 @@ import styled, { css } from 'styled-components'; import TextareaAutosize from 'react-autosize-textarea'; import { mixin } from 'shared/utils/styles'; import Button from 'shared/components/Button'; +import { Citadel } from 'shared/icons'; +import { Link } from 'react-router-dom'; export const NavbarWrapper = styled.div` width: 100%; @@ -9,7 +11,6 @@ export const NavbarWrapper = styled.div` export const ProjectMembers = styled.div` display: flex; - padding-right: 18px; align-items: center; `; export const NavbarHeader = styled.header` @@ -34,9 +35,9 @@ export const BreadcrumpSeparator = styled.span` `; export const ProjectActions = styled.div` + flex: 1; align-items: flex-start; display: flex; - flex: 1; flex-direction: column; min-width: 1px; `; @@ -56,10 +57,11 @@ export const ProfileNameWrapper = styled.div` line-height: 1.25; `; -export const NotificationContainer = styled.div` +export const IconContainer = styled.div` margin-right: 20px; cursor: pointer; `; + export const ProfileNamePrimary = styled.div` color: #c2c6dc; font-weight: 600; @@ -70,7 +72,6 @@ export const ProfileNameSecondary = styled.small` `; export const ProfileIcon = styled.div<{ bgColor: string | null; backgroundURL: string | null }>` - margin-left: 10px; width: 40px; height: 40px; border-radius: 9999px; @@ -84,9 +85,9 @@ export const ProfileIcon = styled.div<{ bgColor: string | null; backgroundURL: s background-size: contain; `; -export const ProjectMeta = styled.div` +export const ProjectMeta = styled.div<{ nameOnly?: boolean }>` display: flex; - padding-top: 9px; + ${props => !props.nameOnly && 'padding-top: 9px;'} margin-left: -14px; align-items: center; max-width: 100%; @@ -188,7 +189,7 @@ export const ProjectSwitcher = styled.button` export const Separator = styled.div` color: #c2c6dc; - font-size: 16px; + font-size: 20px; padding-left: 4px; padding-right: 4px; `; @@ -214,3 +215,36 @@ export const InviteButton = styled(Button)` margin: 0 0 0 8px; padding: 6px 12px; `; + +export const ProjectFinder = styled(Button)` + margin-right: 20px; + padding: 6px 12px; +`; + +export const NavSeparator = styled.div` + width: 1px; + background: rgba(${props => props.theme.colors.border}); + height: 34px; + margin: 0 20px; +`; + +export const LogoContainer = styled(Link)` + display: block; + left: 50%; + position: absolute; + transform: translateX(-50%); + display: flex; + align-items: center; + justify-content: center; +`; + +export const CitadelTitle = styled.h2` + margin-left: 5px; + color: rgba(${props => props.theme.colors.text.primary}); + font-size: 20px; +`; + +export const CitadelLogo = styled(Citadel)` + fill: rgba(${props => props.theme.colors.text.primary}); + stroke: rgba(${props => props.theme.colors.text.primary}); +`; diff --git a/web/src/shared/components/TopNavbar/TopNavbar.stories.tsx b/web/src/shared/components/TopNavbar/TopNavbar.stories.tsx index 6891795..d56dcb1 100644 --- a/web/src/shared/components/TopNavbar/TopNavbar.stories.tsx +++ b/web/src/shared/components/TopNavbar/TopNavbar.stories.tsx @@ -26,7 +26,8 @@ export const Default = () => { { }} onNotificationClick={action('notifications click')} onOpenSettings={action('open settings')} + onDashboardClick={action('open dashboard')} onProfileClick={action('profile click')} /> diff --git a/web/src/shared/components/TopNavbar/index.tsx b/web/src/shared/components/TopNavbar/index.tsx index 7390be3..8cd593d 100644 --- a/web/src/shared/components/TopNavbar/index.tsx +++ b/web/src/shared/components/TopNavbar/index.tsx @@ -1,22 +1,27 @@ import React, { useRef, useState, useEffect } from 'react'; -import { Star, Ellipsis, Bell, Cog, AngleDown } from 'shared/icons'; +import { Home, Star, Bell, AngleDown, BarChart, CheckCircle } from 'shared/icons'; +import styled from 'styled-components'; import ProfileIcon from 'shared/components/ProfileIcon'; +import TaskAssignee from 'shared/components/TaskAssignee'; +import { usePopup, Popup } from 'shared/components/PopupMenu'; +import MiniProfile from 'shared/components/MiniProfile'; import { - NotificationContainer, + CitadelLogo, + CitadelTitle, + ProjectFinder, + LogoContainer, + NavSeparator, + IconContainer, ProjectNameTextarea, InviteButton, GlobalActions, ProjectActions, - ProjectSwitcher, - Separator, ProjectMeta, ProjectName, ProjectTabs, ProjectTab, NavbarWrapper, NavbarHeader, - Breadcrumbs, - BreadcrumpSeparator, ProjectSettingsButton, ProfileContainer, ProfileNameWrapper, @@ -24,18 +29,20 @@ import { ProfileNameSecondary, ProjectMembers, } from './Styles'; -import TaskAssignee from 'shared/components/TaskAssignee'; -import { usePopup, Popup } from 'shared/components/PopupMenu'; -import MiniProfile from 'shared/components/MiniProfile'; +import { Link } from 'react-router-dom'; + +const HomeDashboard = styled(Home)``; type ProjectHeadingProps = { - projectName: string; + onFavorite?: () => void; + name: string; onSaveProjectName?: (projectName: string) => void; onOpenSettings: ($target: React.RefObject) => void; }; const ProjectHeading: React.FC = ({ - projectName: initialProjectName, + onFavorite, + name: initialProjectName, onSaveProjectName, onOpenSettings, }) => { @@ -73,7 +80,6 @@ const ProjectHeading: React.FC = ({ const $settings = useRef(null); return ( <> - » {isEditProjectName ? ( = ({ > - - - + {onFavorite && ( + onFavorite()}> + + + )} ); }; +type MenuType = { + [key: number]: string; +}; +type MenuTypes = { + [key: string]: Array; +}; + +export const MENU_TYPES: MenuTypes = { + PROJECT_MENU: ['Board', 'Timeline', 'Calender'], + TEAM_MENU: ['Projects', 'Members', 'Settings'], +}; + type NavBarProps = { - projectName: string | null; + menuType?: Array | null; + name: string | null; + currentTab?: number; + onOpenProjectFinder: ($target: React.RefObject) => void; + onFavorite?: () => void; onProfileClick: ($target: React.RefObject) => void; - onSaveProjectName?: (projectName: string) => void; + onTabClick?: (tab: number) => void; + onSaveName?: (name: string) => void; onNotificationClick: () => void; + onDashboardClick: () => void; user: TaskUser | null; onOpenSettings: ($target: React.RefObject) => void; projectMembers?: Array | null; }; const NavBar: React.FC = ({ - projectName, - onSaveProjectName, + menuType, + currentTab, + onOpenProjectFinder, + onFavorite, + onTabClick, + name, + onSaveName, onProfileClick, onNotificationClick, + onDashboardClick, user, projectMembers, onOpenSettings, @@ -152,45 +184,61 @@ const NavBar: React.FC = ({ - Projects - {projectName && ( + {name && ( )} - {projectName && ( + {name && ( - Board - Calender - Timeline - Wiki + {menuType && + menuType.map((name, idx) => { + console.log(`${name} : ${idx} === ${currentTab}`); + return {name}; + })} )} + + + Citadel + {projectMembers && ( - - {projectMembers.map(member => ( - - ))} - Invite - + <> + + {projectMembers.map(member => ( + + ))} + Invite + + + )} - + + Projects + + + + + + + + - + + + + {user && ( - - - {user.fullName} - Manager - - } - + + + )} diff --git a/web/src/shared/generated/graphql.tsx b/web/src/shared/generated/graphql.tsx index 421f871..3893397 100644 --- a/web/src/shared/generated/graphql.tsx +++ b/web/src/shared/generated/graphql.tsx @@ -131,7 +131,7 @@ export type Task = { }; export type ProjectsFilter = { - teamID?: Maybe; + teamID?: Maybe; }; export type FindUser = { @@ -152,6 +152,10 @@ export type Organization = { name: Scalars['String']; }; +export type FindTeam = { + teamID: Scalars['UUID']; +}; + export type Query = { __typename?: 'Query'; organizations: Array; @@ -160,6 +164,7 @@ export type Query = { findProject: Project; findTask: Task; projects: Array; + findTeam: Team; teams: Array; labelColors: Array; taskGroups: Array; @@ -186,6 +191,11 @@ export type QueryProjectsArgs = { input?: Maybe; }; + +export type QueryFindTeamArgs = { + input: FindTeam; +}; + export type NewRefreshToken = { userId: Scalars['String']; }; @@ -419,10 +429,48 @@ export type DeleteProjectPayload = { project: Project; }; +export type DeleteTeam = { + teamID: Scalars['UUID']; +}; + +export type DeleteTeamPayload = { + __typename?: 'DeleteTeamPayload'; + ok: Scalars['Boolean']; + team: Team; + projects: Array; +}; + +export type DeleteUserAccount = { + userID: Scalars['UUID']; +}; + +export type DeleteUserAccountPayload = { + __typename?: 'DeleteUserAccountPayload'; + ok: Scalars['Boolean']; + userAccount: UserAccount; +}; + +export type UpdateTaskChecklistName = { + taskChecklistID: Scalars['UUID']; + name: Scalars['String']; +}; + +export type DeleteTaskChecklist = { + taskChecklistID: Scalars['UUID']; +}; + +export type DeleteTaskChecklistPayload = { + __typename?: 'DeleteTaskChecklistPayload'; + ok: Scalars['Boolean']; + taskChecklist: TaskChecklist; +}; + export type Mutation = { __typename?: 'Mutation'; createRefreshToken: RefreshToken; createUserAccount: UserAccount; + deleteUserAccount: DeleteUserAccountPayload; + deleteTeam: DeleteTeamPayload; createTeam: Team; clearProfileAvatar: UserAccount; createTeamMember: CreateTeamMemberPayload; @@ -442,6 +490,8 @@ export type Mutation = { removeTaskLabel: Task; toggleTaskLabel: ToggleTaskLabelPayload; createTaskChecklist: TaskChecklist; + deleteTaskChecklist: DeleteTaskChecklistPayload; + updateTaskChecklistName: TaskChecklist; createTaskChecklistItem: TaskChecklistItem; updateTaskChecklistItemName: TaskChecklistItem; setTaskChecklistItemComplete: TaskChecklistItem; @@ -469,6 +519,16 @@ export type MutationCreateUserAccountArgs = { }; +export type MutationDeleteUserAccountArgs = { + input: DeleteUserAccount; +}; + + +export type MutationDeleteTeamArgs = { + input: DeleteTeam; +}; + + export type MutationCreateTeamArgs = { input: NewTeam; }; @@ -559,6 +619,16 @@ export type MutationCreateTaskChecklistArgs = { }; +export type MutationDeleteTaskChecklistArgs = { + input: DeleteTaskChecklist; +}; + + +export type MutationUpdateTaskChecklistNameArgs = { + input: UpdateTaskChecklistName; +}; + + export type MutationCreateTaskChecklistItemArgs = { input: CreateTaskChecklistItem; }; @@ -699,43 +769,6 @@ export type CreateProjectLabelMutation = ( ) } ); -export type CreateTaskMutationVariables = { - taskGroupID: Scalars['String']; - name: Scalars['String']; - position: Scalars['Float']; -}; - - -export type CreateTaskMutation = ( - { __typename?: 'Mutation' } - & { createTask: ( - { __typename?: 'Task' } - & Pick - & { taskGroup: ( - { __typename?: 'TaskGroup' } - & Pick - ), labels: Array<( - { __typename?: 'TaskLabel' } - & Pick - & { projectLabel: ( - { __typename?: 'ProjectLabel' } - & Pick - & { labelColor: ( - { __typename?: 'LabelColor' } - & Pick - ) } - ) } - )>, assigned: Array<( - { __typename?: 'ProjectMember' } - & Pick - & { profileIcon: ( - { __typename?: 'ProfileIcon' } - & Pick - ) } - )> } - ) } -); - export type CreateTaskGroupMutationVariables = { projectID: Scalars['String']; name: Scalars['String']; @@ -849,6 +882,12 @@ export type FindTaskQuery = ( & { taskGroup: ( { __typename?: 'TaskGroup' } & Pick + ), badges: ( + { __typename?: 'TaskBadges' } + & { checklist?: Maybe<( + { __typename?: 'ChecklistBadge' } + & Pick + )> } ), checklists: Array<( { __typename?: 'TaskChecklist' } & Pick @@ -881,9 +920,15 @@ export type FindTaskQuery = ( export type TaskFieldsFragment = ( { __typename?: 'Task' } & Pick - & { taskGroup: ( + & { badges: ( + { __typename?: 'TaskBadges' } + & { checklist?: Maybe<( + { __typename?: 'ChecklistBadge' } + & Pick + )> } + ), taskGroup: ( { __typename?: 'TaskGroup' } - & Pick + & Pick ), labels: Array<( { __typename?: 'TaskLabel' } & Pick @@ -958,6 +1003,40 @@ export type DeleteProjectMutation = ( ) } ); +export type CreateTaskMutationVariables = { + taskGroupID: Scalars['String']; + name: Scalars['String']; + position: Scalars['Float']; +}; + + +export type CreateTaskMutation = ( + { __typename?: 'Mutation' } + & { createTask: ( + { __typename?: 'Task' } + & TaskFieldsFragment + ) } +); + +export type CreateTaskChecklistMutationVariables = { + taskID: Scalars['UUID']; + name: Scalars['String']; + position: Scalars['Float']; +}; + + +export type CreateTaskChecklistMutation = ( + { __typename?: 'Mutation' } + & { createTaskChecklist: ( + { __typename?: 'TaskChecklist' } + & Pick + & { items: Array<( + { __typename?: 'TaskChecklistItem' } + & Pick + )> } + ) } +); + export type CreateTaskChecklistItemMutationVariables = { taskChecklistID: Scalars['UUID']; name: Scalars['String']; @@ -973,6 +1052,23 @@ export type CreateTaskChecklistItemMutation = ( ) } ); +export type DeleteTaskChecklistMutationVariables = { + taskChecklistID: Scalars['UUID']; +}; + + +export type DeleteTaskChecklistMutation = ( + { __typename?: 'Mutation' } + & { deleteTaskChecklist: ( + { __typename?: 'DeleteTaskChecklistPayload' } + & Pick + & { taskChecklist: ( + { __typename?: 'TaskChecklist' } + & Pick + ) } + ) } +); + export type DeleteTaskChecklistItemMutationVariables = { taskChecklistItemID: Scalars['UUID']; }; @@ -1000,7 +1096,7 @@ export type SetTaskChecklistItemCompleteMutation = ( { __typename?: 'Mutation' } & { setTaskChecklistItemComplete: ( { __typename?: 'TaskChecklistItem' } - & Pick + & Pick ) } ); @@ -1032,6 +1128,24 @@ export type UpdateTaskChecklistItemNameMutation = ( ) } ); +export type UpdateTaskChecklistNameMutationVariables = { + taskChecklistID: Scalars['UUID']; + name: Scalars['String']; +}; + + +export type UpdateTaskChecklistNameMutation = ( + { __typename?: 'Mutation' } + & { updateTaskChecklistName: ( + { __typename?: 'TaskChecklist' } + & Pick + & { items: Array<( + { __typename?: 'TaskChecklistItem' } + & Pick + )> } + ) } +); + export type UpdateTaskGroupNameMutationVariables = { taskGroupID: Scalars['UUID']; name: Scalars['String']; @@ -1060,6 +1174,43 @@ export type CreateTeamMutation = ( ) } ); +export type DeleteTeamMutationVariables = { + teamID: Scalars['UUID']; +}; + + +export type DeleteTeamMutation = ( + { __typename?: 'Mutation' } + & { deleteTeam: ( + { __typename?: 'DeleteTeamPayload' } + & Pick + & { team: ( + { __typename?: 'Team' } + & Pick + ) } + ) } +); + +export type GetTeamQueryVariables = { + teamID: Scalars['UUID']; +}; + + +export type GetTeamQuery = ( + { __typename?: 'Query' } + & { findTeam: ( + { __typename?: 'Team' } + & Pick + ), projects: Array<( + { __typename?: 'Project' } + & Pick + & { team: ( + { __typename?: 'Team' } + & Pick + ) } + )> } +); + export type ToggleTaskLabelMutationVariables = { taskID: Scalars['UUID']; projectLabelID: Scalars['UUID']; @@ -1220,6 +1371,42 @@ export type UpdateTaskNameMutation = ( ) } ); +export type CreateUserAccountMutationVariables = { + username: Scalars['String']; + email: Scalars['String']; + fullName: Scalars['String']; + initials: Scalars['String']; + password: Scalars['String']; +}; + + +export type CreateUserAccountMutation = ( + { __typename?: 'Mutation' } + & { createUserAccount: ( + { __typename?: 'UserAccount' } + & Pick + & { profileIcon: ( + { __typename?: 'ProfileIcon' } + & Pick + ) } + ) } +); + +export type UsersQueryVariables = {}; + + +export type UsersQuery = ( + { __typename?: 'Query' } + & { users: Array<( + { __typename?: 'UserAccount' } + & Pick + & { profileIcon: ( + { __typename?: 'ProfileIcon' } + & Pick + ) } + )> } +); + export const TaskFieldsFragmentDoc = gql` fragment TaskFields on Task { id @@ -1228,8 +1415,16 @@ export const TaskFieldsFragmentDoc = gql` dueDate complete position + badges { + checklist { + complete + total + } + } taskGroup { id + name + position } labels { id @@ -1412,73 +1607,6 @@ export function useCreateProjectLabelMutation(baseOptions?: ApolloReactHooks.Mut export type CreateProjectLabelMutationHookResult = ReturnType; export type CreateProjectLabelMutationResult = ApolloReactCommon.MutationResult; export type CreateProjectLabelMutationOptions = ApolloReactCommon.BaseMutationOptions; -export const CreateTaskDocument = gql` - mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) { - createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) { - id - name - position - description - dueDate - taskGroup { - id - name - position - } - labels { - id - assignedDate - projectLabel { - id - name - createdDate - labelColor { - id - colorHex - position - name - } - } - } - assigned { - id - fullName - profileIcon { - url - initials - bgColor - } - } - } -} - `; -export type CreateTaskMutationFn = ApolloReactCommon.MutationFunction; - -/** - * __useCreateTaskMutation__ - * - * To run a mutation, you first call `useCreateTaskMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useCreateTaskMutation` 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 [createTaskMutation, { data, loading, error }] = useCreateTaskMutation({ - * variables: { - * taskGroupID: // value for 'taskGroupID' - * name: // value for 'name' - * position: // value for 'position' - * }, - * }); - */ -export function useCreateTaskMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { - return ApolloReactHooks.useMutation(CreateTaskDocument, baseOptions); - } -export type CreateTaskMutationHookResult = ReturnType; -export type CreateTaskMutationResult = ApolloReactCommon.MutationResult; -export type CreateTaskMutationOptions = ApolloReactCommon.BaseMutationOptions; export const CreateTaskGroupDocument = gql` mutation createTaskGroup($projectID: String!, $name: String!, $position: Float!) { createTaskGroup(input: {projectID: $projectID, name: $name, position: $position}) { @@ -1698,6 +1826,12 @@ export const FindTaskDocument = gql` taskGroup { id } + badges { + checklist { + total + complete + } + } checklists { id name @@ -1882,6 +2016,83 @@ export function useDeleteProjectMutation(baseOptions?: ApolloReactHooks.Mutation export type DeleteProjectMutationHookResult = ReturnType; export type DeleteProjectMutationResult = ApolloReactCommon.MutationResult; export type DeleteProjectMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const CreateTaskDocument = gql` + mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) { + createTask(input: {taskGroupID: $taskGroupID, name: $name, position: $position}) { + ...TaskFields + } +} + ${TaskFieldsFragmentDoc}`; +export type CreateTaskMutationFn = ApolloReactCommon.MutationFunction; + +/** + * __useCreateTaskMutation__ + * + * To run a mutation, you first call `useCreateTaskMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateTaskMutation` 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 [createTaskMutation, { data, loading, error }] = useCreateTaskMutation({ + * variables: { + * taskGroupID: // value for 'taskGroupID' + * name: // value for 'name' + * position: // value for 'position' + * }, + * }); + */ +export function useCreateTaskMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + return ApolloReactHooks.useMutation(CreateTaskDocument, baseOptions); + } +export type CreateTaskMutationHookResult = ReturnType; +export type CreateTaskMutationResult = ApolloReactCommon.MutationResult; +export type CreateTaskMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const CreateTaskChecklistDocument = gql` + mutation createTaskChecklist($taskID: UUID!, $name: String!, $position: Float!) { + createTaskChecklist(input: {taskID: $taskID, name: $name, position: $position}) { + id + name + position + items { + id + name + taskChecklistID + complete + position + } + } +} + `; +export type CreateTaskChecklistMutationFn = ApolloReactCommon.MutationFunction; + +/** + * __useCreateTaskChecklistMutation__ + * + * To run a mutation, you first call `useCreateTaskChecklistMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateTaskChecklistMutation` 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 [createTaskChecklistMutation, { data, loading, error }] = useCreateTaskChecklistMutation({ + * variables: { + * taskID: // value for 'taskID' + * name: // value for 'name' + * position: // value for 'position' + * }, + * }); + */ +export function useCreateTaskChecklistMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + return ApolloReactHooks.useMutation(CreateTaskChecklistDocument, baseOptions); + } +export type CreateTaskChecklistMutationHookResult = ReturnType; +export type CreateTaskChecklistMutationResult = ApolloReactCommon.MutationResult; +export type CreateTaskChecklistMutationOptions = ApolloReactCommon.BaseMutationOptions; export const CreateTaskChecklistItemDocument = gql` mutation createTaskChecklistItem($taskChecklistID: UUID!, $name: String!, $position: Float!) { createTaskChecklistItem(input: {taskChecklistID: $taskChecklistID, name: $name, position: $position}) { @@ -1920,6 +2131,41 @@ export function useCreateTaskChecklistItemMutation(baseOptions?: ApolloReactHook export type CreateTaskChecklistItemMutationHookResult = ReturnType; export type CreateTaskChecklistItemMutationResult = ApolloReactCommon.MutationResult; export type CreateTaskChecklistItemMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const DeleteTaskChecklistDocument = gql` + mutation deleteTaskChecklist($taskChecklistID: UUID!) { + deleteTaskChecklist(input: {taskChecklistID: $taskChecklistID}) { + ok + taskChecklist { + id + } + } +} + `; +export type DeleteTaskChecklistMutationFn = ApolloReactCommon.MutationFunction; + +/** + * __useDeleteTaskChecklistMutation__ + * + * To run a mutation, you first call `useDeleteTaskChecklistMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteTaskChecklistMutation` 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 [deleteTaskChecklistMutation, { data, loading, error }] = useDeleteTaskChecklistMutation({ + * variables: { + * taskChecklistID: // value for 'taskChecklistID' + * }, + * }); + */ +export function useDeleteTaskChecklistMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + return ApolloReactHooks.useMutation(DeleteTaskChecklistDocument, baseOptions); + } +export type DeleteTaskChecklistMutationHookResult = ReturnType; +export type DeleteTaskChecklistMutationResult = ApolloReactCommon.MutationResult; +export type DeleteTaskChecklistMutationOptions = ApolloReactCommon.BaseMutationOptions; export const DeleteTaskChecklistItemDocument = gql` mutation deleteTaskChecklistItem($taskChecklistItemID: UUID!) { deleteTaskChecklistItem(input: {taskChecklistItemID: $taskChecklistItemID}) { @@ -1960,10 +2206,7 @@ export const SetTaskChecklistItemCompleteDocument = gql` mutation setTaskChecklistItemComplete($taskChecklistItemID: UUID!, $complete: Boolean!) { setTaskChecklistItemComplete(input: {taskChecklistItemID: $taskChecklistItemID, complete: $complete}) { id - name - taskChecklistID complete - position } } `; @@ -2060,6 +2303,48 @@ export function useUpdateTaskChecklistItemNameMutation(baseOptions?: ApolloReact export type UpdateTaskChecklistItemNameMutationHookResult = ReturnType; export type UpdateTaskChecklistItemNameMutationResult = ApolloReactCommon.MutationResult; export type UpdateTaskChecklistItemNameMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const UpdateTaskChecklistNameDocument = gql` + mutation updateTaskChecklistName($taskChecklistID: UUID!, $name: String!) { + updateTaskChecklistName(input: {taskChecklistID: $taskChecklistID, name: $name}) { + id + name + position + items { + id + name + taskChecklistID + complete + position + } + } +} + `; +export type UpdateTaskChecklistNameMutationFn = ApolloReactCommon.MutationFunction; + +/** + * __useUpdateTaskChecklistNameMutation__ + * + * To run a mutation, you first call `useUpdateTaskChecklistNameMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateTaskChecklistNameMutation` 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 [updateTaskChecklistNameMutation, { data, loading, error }] = useUpdateTaskChecklistNameMutation({ + * variables: { + * taskChecklistID: // value for 'taskChecklistID' + * name: // value for 'name' + * }, + * }); + */ +export function useUpdateTaskChecklistNameMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + return ApolloReactHooks.useMutation(UpdateTaskChecklistNameDocument, baseOptions); + } +export type UpdateTaskChecklistNameMutationHookResult = ReturnType; +export type UpdateTaskChecklistNameMutationResult = ApolloReactCommon.MutationResult; +export type UpdateTaskChecklistNameMutationOptions = ApolloReactCommon.BaseMutationOptions; export const UpdateTaskGroupNameDocument = gql` mutation updateTaskGroupName($taskGroupID: UUID!, $name: String!) { updateTaskGroupName(input: {taskGroupID: $taskGroupID, name: $name}) { @@ -2129,6 +2414,84 @@ export function useCreateTeamMutation(baseOptions?: ApolloReactHooks.MutationHoo export type CreateTeamMutationHookResult = ReturnType; export type CreateTeamMutationResult = ApolloReactCommon.MutationResult; export type CreateTeamMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const DeleteTeamDocument = gql` + mutation deleteTeam($teamID: UUID!) { + deleteTeam(input: {teamID: $teamID}) { + ok + team { + id + } + } +} + `; +export type DeleteTeamMutationFn = ApolloReactCommon.MutationFunction; + +/** + * __useDeleteTeamMutation__ + * + * To run a mutation, you first call `useDeleteTeamMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteTeamMutation` 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 [deleteTeamMutation, { data, loading, error }] = useDeleteTeamMutation({ + * variables: { + * teamID: // value for 'teamID' + * }, + * }); + */ +export function useDeleteTeamMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + return ApolloReactHooks.useMutation(DeleteTeamDocument, baseOptions); + } +export type DeleteTeamMutationHookResult = ReturnType; +export type DeleteTeamMutationResult = ApolloReactCommon.MutationResult; +export type DeleteTeamMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const GetTeamDocument = gql` + query getTeam($teamID: UUID!) { + findTeam(input: {teamID: $teamID}) { + id + createdAt + name + } + projects(input: {teamID: $teamID}) { + id + name + team { + id + name + } + } +} + `; + +/** + * __useGetTeamQuery__ + * + * To run a query within a React component, call `useGetTeamQuery` and pass it any options that fit your needs. + * When your component renders, `useGetTeamQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetTeamQuery({ + * variables: { + * teamID: // value for 'teamID' + * }, + * }); + */ +export function useGetTeamQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { + return ApolloReactHooks.useQuery(GetTeamDocument, baseOptions); + } +export function useGetTeamLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + return ApolloReactHooks.useLazyQuery(GetTeamDocument, baseOptions); + } +export type GetTeamQueryHookResult = ReturnType; +export type GetTeamLazyQueryHookResult = ReturnType; +export type GetTeamQueryResult = ApolloReactCommon.QueryResult; export const ToggleTaskLabelDocument = gql` mutation toggleTaskLabel($taskID: UUID!, $projectLabelID: UUID!) { toggleTaskLabel(input: {taskID: $taskID, projectLabelID: $projectLabelID}) { @@ -2472,4 +2835,89 @@ export function useUpdateTaskNameMutation(baseOptions?: ApolloReactHooks.Mutatio } export type UpdateTaskNameMutationHookResult = ReturnType; export type UpdateTaskNameMutationResult = ApolloReactCommon.MutationResult; -export type UpdateTaskNameMutationOptions = ApolloReactCommon.BaseMutationOptions; \ No newline at end of file +export type UpdateTaskNameMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const CreateUserAccountDocument = gql` + mutation createUserAccount($username: String!, $email: String!, $fullName: String!, $initials: String!, $password: String!) { + createUserAccount(input: {username: $username, email: $email, fullName: $fullName, initials: $initials, password: $password}) { + id + email + fullName + initials + username + profileIcon { + url + initials + bgColor + } + } +} + `; +export type CreateUserAccountMutationFn = ApolloReactCommon.MutationFunction; + +/** + * __useCreateUserAccountMutation__ + * + * To run a mutation, you first call `useCreateUserAccountMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateUserAccountMutation` 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 [createUserAccountMutation, { data, loading, error }] = useCreateUserAccountMutation({ + * variables: { + * username: // value for 'username' + * email: // value for 'email' + * fullName: // value for 'fullName' + * initials: // value for 'initials' + * password: // value for 'password' + * }, + * }); + */ +export function useCreateUserAccountMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + return ApolloReactHooks.useMutation(CreateUserAccountDocument, baseOptions); + } +export type CreateUserAccountMutationHookResult = ReturnType; +export type CreateUserAccountMutationResult = ApolloReactCommon.MutationResult; +export type CreateUserAccountMutationOptions = ApolloReactCommon.BaseMutationOptions; +export const UsersDocument = gql` + query users { + users { + id + email + fullName + username + profileIcon { + url + initials + bgColor + } + } +} + `; + +/** + * __useUsersQuery__ + * + * To run a query within a React component, call `useUsersQuery` and pass it any options that fit your needs. + * When your component renders, `useUsersQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useUsersQuery({ + * variables: { + * }, + * }); + */ +export function useUsersQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { + return ApolloReactHooks.useQuery(UsersDocument, baseOptions); + } +export function useUsersLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + return ApolloReactHooks.useLazyQuery(UsersDocument, baseOptions); + } +export type UsersQueryHookResult = ReturnType; +export type UsersLazyQueryHookResult = ReturnType; +export type UsersQueryResult = ApolloReactCommon.QueryResult; \ No newline at end of file diff --git a/web/src/shared/graphql/createTask.graphqls b/web/src/shared/graphql/createTask.graphqls deleted file mode 100644 index 1c51f85..0000000 --- a/web/src/shared/graphql/createTask.graphqls +++ /dev/null @@ -1,38 +0,0 @@ -mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) { - createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) { - id - name - position - description - dueDate - taskGroup { - id - name - position - } - labels { - id - assignedDate - projectLabel { - id - name - createdDate - labelColor { - id - colorHex - position - name - } - } - } - assigned { - id - fullName - profileIcon { - url - initials - bgColor - } - } - } -} diff --git a/web/src/shared/graphql/findTask.graphqls b/web/src/shared/graphql/findTask.graphqls index 577ab0c..678c760 100644 --- a/web/src/shared/graphql/findTask.graphqls +++ b/web/src/shared/graphql/findTask.graphqls @@ -9,6 +9,12 @@ query findTask($taskID: UUID!) { taskGroup { id } + badges { + checklist { + total + complete + } + } checklists { id name diff --git a/web/src/shared/graphql/fragments/task.ts b/web/src/shared/graphql/fragments/task.ts index 96f20cd..0cb2032 100644 --- a/web/src/shared/graphql/fragments/task.ts +++ b/web/src/shared/graphql/fragments/task.ts @@ -8,8 +8,16 @@ const TASK_FRAGMENT = gql` dueDate complete position + badges { + checklist { + complete + total + } + } taskGroup { id + name + position } labels { id diff --git a/web/src/shared/graphql/task/createTask.ts b/web/src/shared/graphql/task/createTask.ts new file mode 100644 index 0000000..8dfd5d4 --- /dev/null +++ b/web/src/shared/graphql/task/createTask.ts @@ -0,0 +1,13 @@ +import gql from 'graphql-tag'; +import TASK_FRAGMENT from '../fragments/task'; + +const CREATE_TASK_MUTATION = gql` + mutation createTask($taskGroupID: String!, $name: String!, $position: Float!) { + createTask(input: { taskGroupID: $taskGroupID, name: $name, position: $position }) { + ...TaskFields + } + } + ${TASK_FRAGMENT} +`; + +export default CREATE_TASK_MUTATION; diff --git a/web/src/shared/graphql/task/createTaskChecklist.ts b/web/src/shared/graphql/task/createTaskChecklist.ts new file mode 100644 index 0000000..6e14a4a --- /dev/null +++ b/web/src/shared/graphql/task/createTaskChecklist.ts @@ -0,0 +1,20 @@ +import gql from 'graphql-tag'; + +const CREATE_TASK_CHECKLIST_MUTATION = gql` + mutation createTaskChecklist($taskID: UUID!, $name: String!, $position: Float!) { + createTaskChecklist(input: { taskID: $taskID, name: $name, position: $position }) { + id + name + position + items { + id + name + taskChecklistID + complete + position + } + } + } +`; + +export default CREATE_TASK_CHECKLIST_MUTATION; diff --git a/web/src/shared/graphql/task/deleteTaskChecklist.ts b/web/src/shared/graphql/task/deleteTaskChecklist.ts new file mode 100644 index 0000000..75d2161 --- /dev/null +++ b/web/src/shared/graphql/task/deleteTaskChecklist.ts @@ -0,0 +1,14 @@ +import gql from 'graphql-tag'; + +const DELETE_TASK_CHECKLIST_MUTATION = gql` + mutation deleteTaskChecklist($taskChecklistID: UUID!) { + deleteTaskChecklist(input: { taskChecklistID: $taskChecklistID }) { + ok + taskChecklist { + id + } + } + } +`; + +export default DELETE_TASK_CHECKLIST_MUTATION; diff --git a/web/src/shared/graphql/task/setTaskChecklistItemComplete.ts b/web/src/shared/graphql/task/setTaskChecklistItemComplete.ts index 8928250..8157b84 100644 --- a/web/src/shared/graphql/task/setTaskChecklistItemComplete.ts +++ b/web/src/shared/graphql/task/setTaskChecklistItemComplete.ts @@ -4,10 +4,7 @@ const SET_TASK_CHECKLIST_ITEM_COMPLETE = gql` mutation setTaskChecklistItemComplete($taskChecklistItemID: UUID!, $complete: Boolean!) { setTaskChecklistItemComplete(input: { taskChecklistItemID: $taskChecklistItemID, complete: $complete }) { id - name - taskChecklistID complete - position } } `; diff --git a/web/src/shared/graphql/task/updateTaskChecklistName.ts b/web/src/shared/graphql/task/updateTaskChecklistName.ts new file mode 100644 index 0000000..0f21d61 --- /dev/null +++ b/web/src/shared/graphql/task/updateTaskChecklistName.ts @@ -0,0 +1,19 @@ +import gql from 'graphql-tag'; + +const UPDATE_TASK_CHECKLIST_NAME_MUTATION = gql` + mutation updateTaskChecklistName($taskChecklistID: UUID!, $name: String!) { + updateTaskChecklistName(input: { taskChecklistID: $taskChecklistID, name: $name }) { + id + name + position + items { + id + name + taskChecklistID + complete + position + } + } + } +`; +export default UPDATE_TASK_CHECKLIST_NAME_MUTATION; diff --git a/web/src/shared/graphql/team/deleteTeam.ts b/web/src/shared/graphql/team/deleteTeam.ts new file mode 100644 index 0000000..c7c32b9 --- /dev/null +++ b/web/src/shared/graphql/team/deleteTeam.ts @@ -0,0 +1,14 @@ +import gql from 'graphql-tag'; + +export const DELETE_TEAM_MUTATION = gql` + mutation deleteTeam($teamID: UUID!) { + deleteTeam(input: { teamID: $teamID }) { + ok + team { + id + } + } + } +`; + +export default DELETE_TEAM_MUTATION; diff --git a/web/src/shared/graphql/team/getTeam.ts b/web/src/shared/graphql/team/getTeam.ts new file mode 100644 index 0000000..316d2cd --- /dev/null +++ b/web/src/shared/graphql/team/getTeam.ts @@ -0,0 +1,21 @@ +import gql from 'graphql-tag'; + +export const GET_TEAM_QUERY = gql` + query getTeam($teamID: UUID!) { + findTeam(input: { teamID: $teamID }) { + id + createdAt + name + } + projects(input: { teamID: $teamID }) { + id + name + team { + id + name + } + } + } +`; + +export default GET_TEAM_QUERY; diff --git a/web/src/shared/graphql/user/createUser.ts b/web/src/shared/graphql/user/createUser.ts new file mode 100644 index 0000000..eeab07b --- /dev/null +++ b/web/src/shared/graphql/user/createUser.ts @@ -0,0 +1,28 @@ +import gql from 'graphql-tag'; + +export const CREATE_USER_MUTATION = gql` + mutation createUserAccount( + $username: String! + $email: String! + $fullName: String! + $initials: String! + $password: String! + ) { + createUserAccount( + input: { username: $username, email: $email, fullName: $fullName, initials: $initials, password: $password } + ) { + id + email + fullName + initials + username + profileIcon { + url + initials + bgColor + } + } + } +`; + +export default CREATE_USER_MUTATION; diff --git a/web/src/shared/graphql/users.graphqls b/web/src/shared/graphql/users.graphqls new file mode 100644 index 0000000..85dcc7e --- /dev/null +++ b/web/src/shared/graphql/users.graphqls @@ -0,0 +1,14 @@ +query users { + users { + id + email + fullName + username + profileIcon { + url + initials + bgColor + } + } +} + diff --git a/web/src/shared/icons/BarChart.tsx b/web/src/shared/icons/BarChart.tsx new file mode 100644 index 0000000..2f8be8e --- /dev/null +++ b/web/src/shared/icons/BarChart.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Icon, { IconProps } from './Icon'; + +const BarChart: React.FC = ({ width = '16px', height = '16px', className }) => { + return ( + + + + ); +}; + +export default BarChart; diff --git a/web/src/shared/icons/Citadel.tsx b/web/src/shared/icons/Citadel.tsx index aefc0f8..2cef5f5 100644 --- a/web/src/shared/icons/Citadel.tsx +++ b/web/src/shared/icons/Citadel.tsx @@ -1,29 +1,15 @@ import React from 'react'; +import Icon, { IconProps } from './Icon'; -type Props = { - size: number | string; - color: string; -}; - -const Citadel = ({ size, color }: Props) => { +const Citadel: React.FC = ({ width = '16px', height = '16px', className }) => { return ( - + - - + + - + ); }; -Citadel.defaultProps = { - size: 16, - color: '#7367f0', -}; - export default Citadel; diff --git a/web/src/shared/icons/Filter.tsx b/web/src/shared/icons/Filter.tsx new file mode 100644 index 0000000..8c6aba9 --- /dev/null +++ b/web/src/shared/icons/Filter.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Icon, { IconProps } from './Icon'; + +const Filter: React.FC = ({ width = '16px', height = '16px', className }) => { + return ( + + + + ); +}; + +export default Filter; diff --git a/web/src/shared/icons/Home.tsx b/web/src/shared/icons/Home.tsx index ef63aae..4b78892 100644 --- a/web/src/shared/icons/Home.tsx +++ b/web/src/shared/icons/Home.tsx @@ -1,21 +1,12 @@ import React from 'react'; +import Icon, { IconProps } from './Icon'; -type Props = { - size: number | string; - color: string; -}; - -const Home = ({ size, color }: Props) => { +const Checkmark: React.FC = ({ width = '16px', height = '16px', className }) => { return ( - - - + + + ); }; -Home.defaultProps = { - size: 16, - color: '#000', -}; - -export default Home; +export default Checkmark; diff --git a/web/src/shared/icons/Icon.tsx b/web/src/shared/icons/Icon.tsx index 0dd9272..ffa47de 100644 --- a/web/src/shared/icons/Icon.tsx +++ b/web/src/shared/icons/Icon.tsx @@ -18,6 +18,7 @@ type Props = { const Svg = styled.svg` fill: rgba(${props => props.theme.colors.text.primary}); + stroke: rgba(${props => props.theme.colors.text.primary}); `; const Icon: React.FC = ({ width, height, viewBox, className, onClick, children }) => { diff --git a/web/src/shared/icons/Lock.tsx b/web/src/shared/icons/Lock.tsx index 01649d7..bfc61a7 100644 --- a/web/src/shared/icons/Lock.tsx +++ b/web/src/shared/icons/Lock.tsx @@ -1,21 +1,12 @@ import React from 'react'; +import Icon, { IconProps } from './Icon'; -type Props = { - size: number | string; - color: string; -}; - -const Lock = ({ size, color }: Props) => { +const Lock: React.FC = ({ width = '16px', height = '16px', className }) => { return ( - - - + + + ); }; -Lock.defaultProps = { - size: 16, - color: '#000', -}; - export default Lock; diff --git a/web/src/shared/icons/Pencil.tsx b/web/src/shared/icons/Pencil.tsx index 62ea5bb..77c67c3 100644 --- a/web/src/shared/icons/Pencil.tsx +++ b/web/src/shared/icons/Pencil.tsx @@ -1,21 +1,12 @@ import React from 'react'; +import Icon, { IconProps } from './Icon'; -type Props = { - size: number | string; - color: string; -}; - -const Pencil = ({ size, color }: Props) => { +const Sort: React.FC = ({ width = '16px', height = '16px', className }) => { return ( - - - + + + ); }; -Pencil.defaultProps = { - size: 16, - color: '#000', -}; - -export default Pencil; +export default Sort; diff --git a/web/src/shared/icons/Sort.tsx b/web/src/shared/icons/Sort.tsx new file mode 100644 index 0000000..4d348fd --- /dev/null +++ b/web/src/shared/icons/Sort.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Icon, { IconProps } from './Icon'; + +const Sort: React.FC = ({ width = '16px', height = '16px', className }) => { + return ( + + + + ); +}; + +export default Sort; diff --git a/web/src/shared/icons/index.ts b/web/src/shared/icons/index.ts index 0762cdc..ffe8cc6 100644 --- a/web/src/shared/icons/index.ts +++ b/web/src/shared/icons/index.ts @@ -1,5 +1,8 @@ import Cross from './Cross'; import Cog from './Cog'; +import Sort from './Sort'; +import Filter from './Filter'; +import BarChart from './BarChart'; import Trash from './Trash'; import CheckCircle from './CheckCircle'; import Clock from './Clock'; @@ -58,6 +61,9 @@ export { Clock, CheckSquareOutline, CheckSquare, + BarChart, Square, + Filter, + Sort, ToggleOn, }; diff --git a/web/yarn.lock b/web/yarn.lock index 315c56e..3f47027 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -2,6 +2,21 @@ # yarn lockfile v1 +"@apollo/client@^3.0.0-rc.8": + version "3.0.0-rc.8" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.0.0-rc.8.tgz#70643547ed8bb10d1d96f64320c3510a57f0cf6a" + integrity sha512-8Sw4Htao2mZb4vERbvMj9GOf4x/+CLYNuF5gULY7TtMWG4ZANLxbe1DeFCicxVo2xRVQ960nf5vVJhxKcjDOQA== + dependencies: + "@types/zen-observable" "^0.8.0" + "@wry/equality" "^0.1.9" + fast-json-stable-stringify "^2.0.0" + graphql-tag "^2.10.2" + optimism "^0.12.1" + symbol-observable "^1.2.0" + ts-invariant "^0.4.4" + tslib "^1.10.0" + zen-observable "^0.8.14" + "@apollo/federation@0.13.2": version "0.13.2" resolved "https://registry.yarnpkg.com/@apollo/federation/-/federation-0.13.2.tgz#a9f842abd1619fe5cd732c56cfbc45dab0ae784a" @@ -3637,6 +3652,13 @@ "@types/node" ">=6" tslib "^1.9.3" +"@wry/context@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.5.2.tgz#f2a5d5ab9227343aa74c81e06533c1ef84598ec7" + integrity sha512-B/JLuRZ/vbEKHRUiGj6xiMojST1kHhu4WcreLfNN7q9DqQFrb97cWgf/kiYsPSUCAMVN0HzfFc8XjJdzgZzfjw== + dependencies: + tslib "^1.9.3" + "@wry/equality@^0.1.2", "@wry/equality@^0.1.9": version "0.1.9" resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.9.tgz#b13e18b7a8053c6858aa6c85b54911fb31e3a909" @@ -8349,7 +8371,7 @@ graphql-request@^1.5.0: dependencies: cross-fetch "2.2.2" -graphql-tag@2.10.3, graphql-tag@^2.10.1, graphql-tag@^2.10.3: +graphql-tag@2.10.3, graphql-tag@^2.10.1, graphql-tag@^2.10.2, graphql-tag@^2.10.3: version "2.10.3" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.3.tgz#ea1baba5eb8fc6339e4c4cf049dabe522b0edf03" integrity sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA== @@ -11668,6 +11690,13 @@ optimism@^0.10.0: dependencies: "@wry/context" "^0.4.0" +optimism@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.12.1.tgz#933f9467b9aef0e601655adb9638f893e486ad02" + integrity sha512-t8I7HM1dw0SECitBYAqFOVHoBAHEQBTeKjIL9y9ImHzAVkdyPK4ifTgM4VJRDtTUY4r/u5Eqxs4XcGPHaoPkeQ== + dependencies: + "@wry/context" "^0.5.2" + optimize-css-assets-webpack-plugin@5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz#e2f1d4d94ad8c0af8967ebd7cf138dcb1ef14572" @@ -17236,7 +17265,7 @@ zen-observable-ts@^0.8.21: tslib "^1.9.3" zen-observable "^0.8.0" -zen-observable@^0.8.0: +zen-observable@^0.8.0, zen-observable@^0.8.14: version "0.8.15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==