diff --git a/api/.graphqlconfig.yml b/api/.graphqlconfig.yml index a35fd2e44..d65be026a 100644 --- a/api/.graphqlconfig.yml +++ b/api/.graphqlconfig.yml @@ -4,7 +4,7 @@ projects: extensions: endpoints: dev1: - url: http://0.0.0.0:4000/graphql + url: http://localhost:4001/graphql database: schemaPath: src/generated/prisma.graphql diff --git a/api/Dockerfile b/api/Dockerfile index 0506ccdef..32704198c 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,6 +1,6 @@ FROM mhart/alpine-node:8 -ENV PORT=4000 +ENV PORT=4001 RUN mkdir -p /var/www/app && \ mkdir -p /var/www/app/node_modules diff --git a/api/database/datamodel.graphql b/api/database/datamodel.graphql index 0ba5945e1..ddc89814c 100644 --- a/api/database/datamodel.graphql +++ b/api/database/datamodel.graphql @@ -30,6 +30,7 @@ type Team { slug: String! @unique profile: TeamProfile members: [TeamMembers!]! @relation (name: "TeamMembers", onDelete: SET_NULL) + owner: String! wallet: Wallet fb: [FinancialBeings!]! @relation (name: "TeamFinancialBeing", onDelete: SET_NULL) status: [TeamStatus!]! @@ -41,6 +42,7 @@ type TeamProfile { motto: String avatar: String updatedAt: DateTime! + createdAt: DateTime! } type TeamStatus { @@ -59,7 +61,7 @@ type TeamMembers { type Member { id: ID! @unique - nickname: String + alias: String team: TeamMembers @relation (name: "MemberOnTeam", onDelete: SET_NULL) authId: String! @unique visible: Boolean! @default(value: true) diff --git a/api/docker-compose.api.yml b/api/docker-compose.api.yml new file mode 100644 index 000000000..689c37fa6 --- /dev/null +++ b/api/docker-compose.api.yml @@ -0,0 +1,19 @@ +version: '3' +services: + api: + image: aateamapi-dev:v1 + build: + context: . + dockerfile: ./Dockerfile + env_file: ./.env + container_name: aateamapi + tty: true + stdin_open: true + volumes: + - ./:/var/www/app + - /var/www/app/node_modules + working_dir: /var/www/app + restart: always + command: node -r dotenv/config src/index.js + ports: + - "4001:4001" diff --git a/api/docker-compose.yml b/api/docker-compose.yml index 37a6ee436..57dc62ef6 100644 --- a/api/docker-compose.yml +++ b/api/docker-compose.yml @@ -27,24 +27,5 @@ services: POSTGRES_PASSWORD: prisma volumes: - postgres:/var/lib/postgresql/data - node: - image: aateamapi-dev:v1 - build: - context: . - dockerfile: ./Dockerfile - depends_on: - - prisma - env_file: ./.env - container_name: aateamapi - tty: true - stdin_open: true - volumes: - - ./:/var/www/app - - /var/www/app/node_modules - working_dir: /var/www/app - restart: always - command: node -r dotenv/config src/index.js - ports: - - "4000:4000" volumes: postgres: diff --git a/api/package-lock.json b/api/package-lock.json index f49d52a1e..81ed969ea 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1492,9 +1492,9 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "config-chain": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz", - "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, "requires": { "ini": "^1.3.4", @@ -4084,6 +4084,11 @@ "iterall": "^1.2.1" } }, + "graphql-tag": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.9.2.tgz", + "integrity": "sha512-qnNmof9pAqj/LUzs3lStP0Gw1qhdVCUS7Ab7+SUB6KD5aX1uqxWQRwMnOGTkhKuLvLNIs1TvNz+iS9kUGl1MhA==" + }, "graphql-tools": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-3.1.1.tgz", @@ -6371,14 +6376,14 @@ "dev": true }, "prisma": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-1.15.3.tgz", - "integrity": "sha512-95s0Kk8PtptrVhp4ajXPd7A7QrbWdCEyZ/nnvcz0Sz9nzufYYZfBjxgibUyoaNs/XL0KXtiibQIdZhpWIJgOhw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-1.16.0.tgz", + "integrity": "sha512-8jZYCrkd1xjy+JJbQi81f8PBOLe8LLWwSu0fK0cqekm3FeafBd8xk5b0RWcIsHyVd6ypKCQt3tnZEMVzmbKrdQ==", "dev": true, "requires": { "fs-extra": "^5.0.0", - "prisma-cli-core": "1.0.75", - "prisma-cli-engine": "1.0.78", + "prisma-cli-core": "1.0.76", + "prisma-cli-engine": "1.0.79", "semver": "^5.4.1", "source-map-support": "^0.4.18" }, @@ -6497,9 +6502,9 @@ } }, "prisma-cli-core": { - "version": "1.0.75", - "resolved": "https://registry.npmjs.org/prisma-cli-core/-/prisma-cli-core-1.0.75.tgz", - "integrity": "sha512-DiH686w2u9aiz4RoNX3T0XgGzeai8IrXX/Qsocam+lKtBB63aupqCxbj6kp16sNoiXhDPzI2Zx/Hwm0GG9sUhw==", + "version": "1.0.76", + "resolved": "https://registry.npmjs.org/prisma-cli-core/-/prisma-cli-core-1.0.76.tgz", + "integrity": "sha512-HtHn/RM2GlJi23KoIXHoboP97aC54SYDqr7B4uT/80JP6A6nQp5HLt4sJgNfKapaHFZVzXYM7M3LEfGq3tgQbw==", "dev": true, "requires": { "adm-zip": "^0.4.7", @@ -6533,9 +6538,9 @@ "opn": "^5.1.0", "pause": "^0.1.0", "portfinder": "^1.0.13", - "prisma-db-introspection": "0.0.45", + "prisma-db-introspection": "0.0.46", "prisma-json-schema": "0.0.4", - "prisma-yml": "1.0.90", + "prisma-yml": "1.0.96", "scuid": "^1.0.2", "semver": "^5.4.1", "sillyname": "^0.1.0", @@ -6599,12 +6604,12 @@ } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "dotenv": { @@ -6766,12 +6771,6 @@ "regex-cache": "^0.4.2" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "prisma-json-schema": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.0.4.tgz", @@ -6779,9 +6778,9 @@ "dev": true }, "prisma-yml": { - "version": "1.0.90", - "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.90.tgz", - "integrity": "sha512-i5qMN/p1YvLV931YgfJtUAaHO7nOL07oW/EqQ1AixBGRi9nB21TpZGy9b50TwunaMt8LfVYB5ER82338qrJA5g==", + "version": "1.0.96", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.96.tgz", + "integrity": "sha512-w3XGzJHUq5pwBoFy/amkGOcTDj4XoHh8UFUUUZcHo5fSyoF8AWeRTu1yWINaPVGxbcmpwKNetntAW4ijiq7M5g==", "dev": true, "requires": { "ajv": "5", @@ -6828,9 +6827,9 @@ } }, "prisma-cli-engine": { - "version": "1.0.78", - "resolved": "https://registry.npmjs.org/prisma-cli-engine/-/prisma-cli-engine-1.0.78.tgz", - "integrity": "sha512-chtmecxBgXPQUzvDS0U/R+pHZ/nzV1ksO+OiKr98kT7dBR9QJQbRG0v/j+zoIW4vdR9eyIaXy5i+dW8HU8XnDg==", + "version": "1.0.79", + "resolved": "https://registry.npmjs.org/prisma-cli-engine/-/prisma-cli-engine-1.0.79.tgz", + "integrity": "sha512-CdEIJs4ld1IiQQt2CFFPmeDypN4Ct4sTgmFODVjJix790mpEtxMagzCcA+mhfnRL65P6xiHn76Gdxbz60GyYBA==", "dev": true, "requires": { "@heroku/linewrap": "^1.0.0", @@ -6873,7 +6872,7 @@ "memfs": "^2.5.3", "opn": "^5.1.0", "prisma-json-schema": "0.0.4", - "prisma-yml": "1.0.90", + "prisma-yml": "1.0.96", "raven": "2.3.0", "replaceall": "^0.1.6", "rwlockfile": "^1.4.8", @@ -6890,12 +6889,12 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "dotenv": { @@ -6931,12 +6930,6 @@ "through": "^2.3.6" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "prisma-json-schema": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.0.4.tgz", @@ -6944,9 +6937,9 @@ "dev": true }, "prisma-yml": { - "version": "1.0.90", - "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.90.tgz", - "integrity": "sha512-i5qMN/p1YvLV931YgfJtUAaHO7nOL07oW/EqQ1AixBGRi9nB21TpZGy9b50TwunaMt8LfVYB5ER82338qrJA5g==", + "version": "1.0.96", + "resolved": "https://registry.npmjs.org/prisma-yml/-/prisma-yml-1.0.96.tgz", + "integrity": "sha512-w3XGzJHUq5pwBoFy/amkGOcTDj4XoHh8UFUUUZcHo5fSyoF8AWeRTu1yWINaPVGxbcmpwKNetntAW4ijiq7M5g==", "dev": true, "requires": { "ajv": "5", @@ -7002,9 +6995,9 @@ } }, "prisma-db-introspection": { - "version": "0.0.45", - "resolved": "https://registry.npmjs.org/prisma-db-introspection/-/prisma-db-introspection-0.0.45.tgz", - "integrity": "sha512-tOdv4mBFZLVds1Wst92l2iarmzlxwFo/EFHs4kW1P0rR2My7BoxRgV0HSZy9xvo9gbU3P8e5JEZofMNrS+5v+g==", + "version": "0.0.46", + "resolved": "https://registry.npmjs.org/prisma-db-introspection/-/prisma-db-introspection-0.0.46.tgz", + "integrity": "sha512-/b7evS9b5xKS7UrcOERkCmV+IWC6OAgFtInFsvdwn4MeLEaVbkOfzRakdOo+t2gb+N7Yy8Eey9eKYgB5jiJAbA==", "dev": true, "requires": { "ajv": "5", @@ -7029,12 +7022,12 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "dotenv": { @@ -7043,12 +7036,6 @@ "integrity": "sha1-hk7xN5rO1Vzm+V3r7NzhefegzR0=", "dev": true }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, "prisma-json-schema": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/prisma-json-schema/-/prisma-json-schema-0.0.1.tgz", diff --git a/api/package.json b/api/package.json index 9df8a2bf9..784b66264 100644 --- a/api/package.json +++ b/api/package.json @@ -11,6 +11,7 @@ "dotenv": "^6.0.0", "express-jwt": "^5.3.1", "graphql-import": "^0.6.0", + "graphql-tag": "^2.9.2", "graphql-tools": "3.1.1", "graphql-yoga": "^1.16.1", "jsonwebtoken": "^8.3.0", @@ -22,6 +23,6 @@ "devDependencies": { "graphql-cli": "^2.16.5", "npm-run-all": "4.1.3", - "prisma": "^1.14.0" + "prisma": "^1.16.0" } } diff --git a/api/src/auth/authDirectives.js b/api/src/auth/authDirectives.js index 56720ff10..0ba153fce 100644 --- a/api/src/auth/authDirectives.js +++ b/api/src/auth/authDirectives.js @@ -1,18 +1,35 @@ const _get = require('lodash.get') -const validateAndParseIdToken = require('./helpers/validateAndParseIdToken') -const createMember = require('./index') +const { validateIdToken } = require('./validateIdToken') -const memberLocationOnContext = 'request.member' +const memberLocationOnContext = 'request.user' const bearerAccessToken = 'request.headers.authorization' const ctxMember = ctx => _get(ctx, memberLocationOnContext) const ctxToken = ctx => _get(ctx, bearerAccessToken) +const createMember = async (ctx, idToken, info) => { + console.log('createMember', idToken) + const member = await ctx.db.mutation.upsertMember({ + where: { + authId: idToken.sub, + }, + create: { + authId: idToken.sub, + alias: idToken.nickname + }, + update: { + authId: idToken.sub, + alias: idToken.nickname + } + }, info) + return member +} + const isLoggedIn = async ctx => { let member = ctxMember(ctx, memberLocationOnContext) let token = ctxToken(ctx, bearerAccessToken) let memberToken - + console.log('isLoggedIn: ', ctx, member, token) if (!member && token) { let scheme, credentials const tokenParts = token.split(' ') @@ -24,21 +41,28 @@ const isLoggedIn = async ctx => { if (/^Bearer$/i.test(scheme)) { token = credentials //verify token + console.log('isLoggedIn2 : ', token) try { - memberToken = await validateAndParseIdToken(token) - const auth0id = memberToken.sub + memberToken = await validateIdToken(token) + const authId = memberToken.sub + console.log('isLoggedIn3 : ', await authId, ctx.db.query.member) + exists = await ctx.db.query.member({ where: { authId: authId } }) + console.log('isLoggedIn4 : ', await exists, createMember ) + if (!member && exists === null) { + member = await createMember(ctx, memberToken).then(res => { + console.log('isLoggedIn5 : ', res) + return res + }) - member = await ctx.db.query.member({ where: { auth0id } }) - - if (!member) { - member = await createMember(ctx, memberToken) + return member } + return member } catch (err) { memberToken = false } } } - + console.log('isLoggedIn5 : ', await member) if (!member) { throw new Error(`Not logged in`) } @@ -47,8 +71,8 @@ const isLoggedIn = async ctx => { const isRequestingMemberAlsoOwner = ({ ctx, memberId, type, typeId }) => - ctx.db.exists[type]({ id: typeId, owner: { auth0id: memberId } }) -const isRequestingMember = ({ ctx, memberId }) => ctx.db.exists.Member({ auth0id: memberId }) + ctx.db.exists[type]({ slug: typeId, owner: memberId }) +const isRequestingMember = ({ ctx, memberId }) => ctx.db.exists.Member({ authId: memberId }) const directiveResolvers = { isAuthenticated: async (next, source, args, ctx) => { @@ -65,12 +89,14 @@ const directiveResolvers = { throw new Error(`Unauthorized, incorrect role`) }, isOwner: async (next, source, { type }, ctx) => { - const { id: typeId } = + console.log('directive isOwner: ', source, type, ctx) + const { slug: typeId } = source && source.id ? source : ctx.request.body.variables ? ctx.request.body.variables : { id: null } - const { auth0id: memberId } = await isLoggedIn(ctx) - console.log('directive isOwner 0: ', type, typeId, memberId) + const user = await isLoggedIn(ctx) + console.log('directive isOwner 0: ', typeId, user.sub) + const memberId = user.sub const isOwner = type === `Member` ? memberId === typeId @@ -82,14 +108,14 @@ const directiveResolvers = { throw new Error(`Unauthorized, must be owner`) }, isOwnerOrHasRole: async (next, source, { roles, type }, ctx, ...p) => { - const { auth0id: memberId } = await isLoggedIn(ctx) + const { authId: memberId } = await isLoggedIn(ctx) console.log('directive isOwnerOrHasRole 1: ', memberId, roles, type) if(memberId === undefined && role=== undefined) throw new Error(`Not logged in`) if (roles.includes(role)) { return next() } - const { auth0id: typeId } = ctx.request.body.variables + const { authId: typeId } = ctx.request.body.variables const isOwner = await isRequestingMemberAlsoOwner({ ctx, memberId, diff --git a/api/src/auth/middleware/getMember.js b/api/src/auth/middleware/getMember.js index 7b2642c50..7d19b2d6c 100644 --- a/api/src/auth/middleware/getMember.js +++ b/api/src/auth/middleware/getMember.js @@ -1,9 +1,9 @@ const getMember = async (req, res, next, db) => { if (!req.user) return next() const member = await db.query.member({ - where: { auth0id: req.user.sub } + where: { authId: req.user.sub } }) - req.user = { token: req.user, ...member } + req.user = { user: member } next() } diff --git a/api/src/auth/middleware/jwt.js b/api/src/auth/middleware/jwt.js index 39e8ecbe3..0ff677156 100644 --- a/api/src/auth/middleware/jwt.js +++ b/api/src/auth/middleware/jwt.js @@ -21,4 +21,4 @@ const checkJwt = jwt({ algorithms: [`RS256`] }) -module.exports = { checkJwt } +module.exports = checkJwt diff --git a/api/src/auth/validateIdToken.js b/api/src/auth/validateIdToken.js index 25434c85f..dd0b794b3 100644 --- a/api/src/auth/validateIdToken.js +++ b/api/src/auth/validateIdToken.js @@ -7,7 +7,7 @@ const jwks = jwksClient({ jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json` }) -const validateParseIdToken = idToken => +const validateIdToken = idToken => new Promise((resolve, reject) => { const { header, payload } = jwt.decode(idToken, { complete: true }) if (!header || !header.kid || !payload) reject(new Error('Invalid Token')) @@ -25,4 +25,4 @@ const validateParseIdToken = idToken => }) }) -module.exports = validateParseIdToken +module.exports = { validateIdToken } diff --git a/api/src/generated/prisma.graphql b/api/src/generated/prisma.graphql new file mode 100644 index 000000000..6d118e29a --- /dev/null +++ b/api/src/generated/prisma.graphql @@ -0,0 +1,2327 @@ +# source: http://localhost:4466 +# timestamp: Wed Sep 12 2018 08:41:57 GMT-0700 (Pacific Daylight Time) + +type AggregateFBStatus { + count: Int! +} + +type AggregateFinancialBeings { + count: Int! +} + +type AggregateMember { + count: Int! +} + +type AggregateMemberStatus { + count: Int! +} + +type AggregateTeam { + count: Int! +} + +type AggregateTeamMembers { + count: Int! +} + +type AggregateTeamProfile { + count: Int! +} + +type AggregateTeamStatus { + count: Int! +} + +type AggregateWallet { + count: Int! +} + +type BatchPayload { + """The number of nodes that have been affected by the Batch operation.""" + count: Long! +} + +scalar DateTime + +enum FBKind { + TRADER + INDICATOR + EXTRACTOR + PLOTTER +} + +type FBStatus { + status: Status + reason: String + createdAt: DateTime! +} + +"""A connection to a list of items.""" +type FBStatusConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FBStatusEdge]! + aggregate: AggregateFBStatus! +} + +input FBStatusCreateInput { + status: Status + reason: String +} + +input FBStatusCreateManyInput { + create: [FBStatusCreateInput!] +} + +"""An edge in a connection.""" +type FBStatusEdge { + """The item at the end of the edge.""" + node: FBStatus! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum FBStatusOrderByInput { + status_ASC + status_DESC + reason_ASC + reason_DESC + createdAt_ASC + createdAt_DESC + id_ASC + id_DESC + updatedAt_ASC + updatedAt_DESC +} + +type FBStatusPreviousValues { + status: Status + reason: String + createdAt: DateTime! +} + +type FBStatusSubscriptionPayload { + mutation: MutationType! + node: FBStatus + updatedFields: [String!] + previousValues: FBStatusPreviousValues +} + +input FBStatusSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [FBStatusSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [FBStatusSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [FBStatusSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: FBStatusWhereInput +} + +input FBStatusUpdateInput { + status: Status + reason: String +} + +input FBStatusUpdateManyInput { + create: [FBStatusCreateInput!] +} + +input FBStatusWhereInput { + """Logical AND on all given filters.""" + AND: [FBStatusWhereInput!] + + """Logical OR on all given filters.""" + OR: [FBStatusWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [FBStatusWhereInput!] + status: Status + + """All values that are not equal to given value.""" + status_not: Status + + """All values that are contained in given list.""" + status_in: [Status!] + + """All values that are not contained in given list.""" + status_not_in: [Status!] + reason: String + + """All values that are not equal to given value.""" + reason_not: String + + """All values that are contained in given list.""" + reason_in: [String!] + + """All values that are not contained in given list.""" + reason_not_in: [String!] + + """All values less than the given value.""" + reason_lt: String + + """All values less than or equal the given value.""" + reason_lte: String + + """All values greater than the given value.""" + reason_gt: String + + """All values greater than or equal the given value.""" + reason_gte: String + + """All values containing the given string.""" + reason_contains: String + + """All values not containing the given string.""" + reason_not_contains: String + + """All values starting with the given string.""" + reason_starts_with: String + + """All values not starting with the given string.""" + reason_not_starts_with: String + + """All values ending with the given string.""" + reason_ends_with: String + + """All values not ending with the given string.""" + reason_not_ends_with: String + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime +} + +enum FBType { + BOT + NET + ADVANCED +} + +type FinancialBeings implements Node { + id: ID! + type: FBType + kind: FBKind + team(where: TeamWhereInput): Team + status(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FBStatus!] + updatedAt: DateTime! + createdAt: DateTime! +} + +"""A connection to a list of items.""" +type FinancialBeingsConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [FinancialBeingsEdge]! + aggregate: AggregateFinancialBeings! +} + +input FinancialBeingsCreateInput { + type: FBType + kind: FBKind + team: TeamCreateOneWithoutFbInput + status: FBStatusCreateManyInput +} + +input FinancialBeingsCreateManyWithoutTeamInput { + create: [FinancialBeingsCreateWithoutTeamInput!] + connect: [FinancialBeingsWhereUniqueInput!] +} + +input FinancialBeingsCreateWithoutTeamInput { + type: FBType + kind: FBKind + status: FBStatusCreateManyInput +} + +"""An edge in a connection.""" +type FinancialBeingsEdge { + """The item at the end of the edge.""" + node: FinancialBeings! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum FinancialBeingsOrderByInput { + id_ASC + id_DESC + type_ASC + type_DESC + kind_ASC + kind_DESC + updatedAt_ASC + updatedAt_DESC + createdAt_ASC + createdAt_DESC +} + +type FinancialBeingsPreviousValues { + id: ID! + type: FBType + kind: FBKind + updatedAt: DateTime! + createdAt: DateTime! +} + +type FinancialBeingsSubscriptionPayload { + mutation: MutationType! + node: FinancialBeings + updatedFields: [String!] + previousValues: FinancialBeingsPreviousValues +} + +input FinancialBeingsSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [FinancialBeingsSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [FinancialBeingsSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [FinancialBeingsSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: FinancialBeingsWhereInput +} + +input FinancialBeingsUpdateInput { + type: FBType + kind: FBKind + team: TeamUpdateOneWithoutFbInput + status: FBStatusUpdateManyInput +} + +input FinancialBeingsUpdateManyWithoutTeamInput { + create: [FinancialBeingsCreateWithoutTeamInput!] + connect: [FinancialBeingsWhereUniqueInput!] + disconnect: [FinancialBeingsWhereUniqueInput!] + delete: [FinancialBeingsWhereUniqueInput!] + update: [FinancialBeingsUpdateWithWhereUniqueWithoutTeamInput!] + upsert: [FinancialBeingsUpsertWithWhereUniqueWithoutTeamInput!] +} + +input FinancialBeingsUpdateWithoutTeamDataInput { + type: FBType + kind: FBKind + status: FBStatusUpdateManyInput +} + +input FinancialBeingsUpdateWithWhereUniqueWithoutTeamInput { + where: FinancialBeingsWhereUniqueInput! + data: FinancialBeingsUpdateWithoutTeamDataInput! +} + +input FinancialBeingsUpsertWithWhereUniqueWithoutTeamInput { + where: FinancialBeingsWhereUniqueInput! + update: FinancialBeingsUpdateWithoutTeamDataInput! + create: FinancialBeingsCreateWithoutTeamInput! +} + +input FinancialBeingsWhereInput { + """Logical AND on all given filters.""" + AND: [FinancialBeingsWhereInput!] + + """Logical OR on all given filters.""" + OR: [FinancialBeingsWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [FinancialBeingsWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + type: FBType + + """All values that are not equal to given value.""" + type_not: FBType + + """All values that are contained in given list.""" + type_in: [FBType!] + + """All values that are not contained in given list.""" + type_not_in: [FBType!] + kind: FBKind + + """All values that are not equal to given value.""" + kind_not: FBKind + + """All values that are contained in given list.""" + kind_in: [FBKind!] + + """All values that are not contained in given list.""" + kind_not_in: [FBKind!] + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + team: TeamWhereInput + status_every: FBStatusWhereInput + status_some: FBStatusWhereInput + status_none: FBStatusWhereInput +} + +input FinancialBeingsWhereUniqueInput { + id: ID +} + +""" +The `Long` scalar type represents non-fractional signed whole numeric values. +Long can represent values between -(2^63) and 2^63 - 1. +""" +scalar Long + +type Member implements Node { + id: ID! + alias: String + team(where: TeamMembersWhereInput): TeamMembers + authId: String! + visible: Boolean! + createdAt: DateTime! +} + +"""A connection to a list of items.""" +type MemberConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [MemberEdge]! + aggregate: AggregateMember! +} + +input MemberCreateInput { + alias: String + authId: String! + visible: Boolean + team: TeamMembersCreateOneWithoutMemberInput +} + +input MemberCreateOneWithoutTeamInput { + create: MemberCreateWithoutTeamInput + connect: MemberWhereUniqueInput +} + +input MemberCreateWithoutTeamInput { + alias: String + authId: String! + visible: Boolean +} + +"""An edge in a connection.""" +type MemberEdge { + """The item at the end of the edge.""" + node: Member! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum MemberOrderByInput { + id_ASC + id_DESC + alias_ASC + alias_DESC + authId_ASC + authId_DESC + visible_ASC + visible_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC +} + +type MemberPreviousValues { + id: ID! + alias: String + authId: String! + visible: Boolean! + createdAt: DateTime! +} + +type MemberStatus { + status: Status + reason: String + createdAt: DateTime! +} + +"""A connection to a list of items.""" +type MemberStatusConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [MemberStatusEdge]! + aggregate: AggregateMemberStatus! +} + +input MemberStatusCreateInput { + status: Status + reason: String +} + +input MemberStatusCreateManyInput { + create: [MemberStatusCreateInput!] +} + +"""An edge in a connection.""" +type MemberStatusEdge { + """The item at the end of the edge.""" + node: MemberStatus! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum MemberStatusOrderByInput { + status_ASC + status_DESC + reason_ASC + reason_DESC + createdAt_ASC + createdAt_DESC + id_ASC + id_DESC + updatedAt_ASC + updatedAt_DESC +} + +type MemberStatusPreviousValues { + status: Status + reason: String + createdAt: DateTime! +} + +type MemberStatusSubscriptionPayload { + mutation: MutationType! + node: MemberStatus + updatedFields: [String!] + previousValues: MemberStatusPreviousValues +} + +input MemberStatusSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [MemberStatusSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [MemberStatusSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [MemberStatusSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: MemberStatusWhereInput +} + +input MemberStatusUpdateInput { + status: Status + reason: String +} + +input MemberStatusUpdateManyInput { + create: [MemberStatusCreateInput!] +} + +input MemberStatusWhereInput { + """Logical AND on all given filters.""" + AND: [MemberStatusWhereInput!] + + """Logical OR on all given filters.""" + OR: [MemberStatusWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [MemberStatusWhereInput!] + status: Status + + """All values that are not equal to given value.""" + status_not: Status + + """All values that are contained in given list.""" + status_in: [Status!] + + """All values that are not contained in given list.""" + status_not_in: [Status!] + reason: String + + """All values that are not equal to given value.""" + reason_not: String + + """All values that are contained in given list.""" + reason_in: [String!] + + """All values that are not contained in given list.""" + reason_not_in: [String!] + + """All values less than the given value.""" + reason_lt: String + + """All values less than or equal the given value.""" + reason_lte: String + + """All values greater than the given value.""" + reason_gt: String + + """All values greater than or equal the given value.""" + reason_gte: String + + """All values containing the given string.""" + reason_contains: String + + """All values not containing the given string.""" + reason_not_contains: String + + """All values starting with the given string.""" + reason_starts_with: String + + """All values not starting with the given string.""" + reason_not_starts_with: String + + """All values ending with the given string.""" + reason_ends_with: String + + """All values not ending with the given string.""" + reason_not_ends_with: String + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime +} + +type MemberSubscriptionPayload { + mutation: MutationType! + node: Member + updatedFields: [String!] + previousValues: MemberPreviousValues +} + +input MemberSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [MemberSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [MemberSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [MemberSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: MemberWhereInput +} + +input MemberUpdateInput { + alias: String + authId: String + visible: Boolean + team: TeamMembersUpdateOneWithoutMemberInput +} + +input MemberUpdateOneWithoutTeamInput { + create: MemberCreateWithoutTeamInput + connect: MemberWhereUniqueInput + disconnect: Boolean + delete: Boolean + update: MemberUpdateWithoutTeamDataInput + upsert: MemberUpsertWithoutTeamInput +} + +input MemberUpdateWithoutTeamDataInput { + alias: String + authId: String + visible: Boolean +} + +input MemberUpsertWithoutTeamInput { + update: MemberUpdateWithoutTeamDataInput! + create: MemberCreateWithoutTeamInput! +} + +input MemberWhereInput { + """Logical AND on all given filters.""" + AND: [MemberWhereInput!] + + """Logical OR on all given filters.""" + OR: [MemberWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [MemberWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + alias: String + + """All values that are not equal to given value.""" + alias_not: String + + """All values that are contained in given list.""" + alias_in: [String!] + + """All values that are not contained in given list.""" + alias_not_in: [String!] + + """All values less than the given value.""" + alias_lt: String + + """All values less than or equal the given value.""" + alias_lte: String + + """All values greater than the given value.""" + alias_gt: String + + """All values greater than or equal the given value.""" + alias_gte: String + + """All values containing the given string.""" + alias_contains: String + + """All values not containing the given string.""" + alias_not_contains: String + + """All values starting with the given string.""" + alias_starts_with: String + + """All values not starting with the given string.""" + alias_not_starts_with: String + + """All values ending with the given string.""" + alias_ends_with: String + + """All values not ending with the given string.""" + alias_not_ends_with: String + authId: String + + """All values that are not equal to given value.""" + authId_not: String + + """All values that are contained in given list.""" + authId_in: [String!] + + """All values that are not contained in given list.""" + authId_not_in: [String!] + + """All values less than the given value.""" + authId_lt: String + + """All values less than or equal the given value.""" + authId_lte: String + + """All values greater than the given value.""" + authId_gt: String + + """All values greater than or equal the given value.""" + authId_gte: String + + """All values containing the given string.""" + authId_contains: String + + """All values not containing the given string.""" + authId_not_contains: String + + """All values starting with the given string.""" + authId_starts_with: String + + """All values not starting with the given string.""" + authId_not_starts_with: String + + """All values ending with the given string.""" + authId_ends_with: String + + """All values not ending with the given string.""" + authId_not_ends_with: String + visible: Boolean + + """All values that are not equal to given value.""" + visible_not: Boolean + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + team: TeamMembersWhereInput +} + +input MemberWhereUniqueInput { + id: ID + authId: String +} + +type Mutation { + createTeam(data: TeamCreateInput!): Team! + createTeamMembers(data: TeamMembersCreateInput!): TeamMembers! + createMember(data: MemberCreateInput!): Member! + createFinancialBeings(data: FinancialBeingsCreateInput!): FinancialBeings! + createTeamProfile(data: TeamProfileCreateInput!): TeamProfile! + createWallet(data: WalletCreateInput!): Wallet! + createFBStatus(data: FBStatusCreateInput!): FBStatus! + createTeamStatus(data: TeamStatusCreateInput!): TeamStatus! + createMemberStatus(data: MemberStatusCreateInput!): MemberStatus! + updateTeam(data: TeamUpdateInput!, where: TeamWhereUniqueInput!): Team + updateMember(data: MemberUpdateInput!, where: MemberWhereUniqueInput!): Member + updateFinancialBeings(data: FinancialBeingsUpdateInput!, where: FinancialBeingsWhereUniqueInput!): FinancialBeings + deleteTeam(where: TeamWhereUniqueInput!): Team + deleteMember(where: MemberWhereUniqueInput!): Member + deleteFinancialBeings(where: FinancialBeingsWhereUniqueInput!): FinancialBeings + upsertTeam(where: TeamWhereUniqueInput!, create: TeamCreateInput!, update: TeamUpdateInput!): Team! + upsertMember(where: MemberWhereUniqueInput!, create: MemberCreateInput!, update: MemberUpdateInput!): Member! + upsertFinancialBeings(where: FinancialBeingsWhereUniqueInput!, create: FinancialBeingsCreateInput!, update: FinancialBeingsUpdateInput!): FinancialBeings! + updateManyTeams(data: TeamUpdateInput!, where: TeamWhereInput): BatchPayload! + updateManyTeamMemberses(data: TeamMembersUpdateInput!, where: TeamMembersWhereInput): BatchPayload! + updateManyMembers(data: MemberUpdateInput!, where: MemberWhereInput): BatchPayload! + updateManyFinancialBeingses(data: FinancialBeingsUpdateInput!, where: FinancialBeingsWhereInput): BatchPayload! + updateManyTeamProfiles(data: TeamProfileUpdateInput!, where: TeamProfileWhereInput): BatchPayload! + updateManyWallets(data: WalletUpdateInput!, where: WalletWhereInput): BatchPayload! + updateManyFBStatuses(data: FBStatusUpdateInput!, where: FBStatusWhereInput): BatchPayload! + updateManyTeamStatuses(data: TeamStatusUpdateInput!, where: TeamStatusWhereInput): BatchPayload! + updateManyMemberStatuses(data: MemberStatusUpdateInput!, where: MemberStatusWhereInput): BatchPayload! + deleteManyTeams(where: TeamWhereInput): BatchPayload! + deleteManyTeamMemberses(where: TeamMembersWhereInput): BatchPayload! + deleteManyMembers(where: MemberWhereInput): BatchPayload! + deleteManyFinancialBeingses(where: FinancialBeingsWhereInput): BatchPayload! + deleteManyTeamProfiles(where: TeamProfileWhereInput): BatchPayload! + deleteManyWallets(where: WalletWhereInput): BatchPayload! + deleteManyFBStatuses(where: FBStatusWhereInput): BatchPayload! + deleteManyTeamStatuses(where: TeamStatusWhereInput): BatchPayload! + deleteManyMemberStatuses(where: MemberStatusWhereInput): BatchPayload! +} + +enum MutationType { + CREATED + UPDATED + DELETED +} + +"""An object with an ID""" +interface Node { + """The id of the object.""" + id: ID! +} + +"""Information about pagination in a connection.""" +type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String +} + +type Query { + teams(where: TeamWhereInput, orderBy: TeamOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Team]! + teamMemberses(where: TeamMembersWhereInput, orderBy: TeamMembersOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamMembers]! + members(where: MemberWhereInput, orderBy: MemberOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Member]! + financialBeingses(where: FinancialBeingsWhereInput, orderBy: FinancialBeingsOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FinancialBeings]! + teamProfiles(where: TeamProfileWhereInput, orderBy: TeamProfileOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamProfile]! + wallets(where: WalletWhereInput, orderBy: WalletOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Wallet]! + fBStatuses(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FBStatus]! + teamStatuses(where: TeamStatusWhereInput, orderBy: TeamStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamStatus]! + memberStatuses(where: MemberStatusWhereInput, orderBy: MemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [MemberStatus]! + team(where: TeamWhereUniqueInput!): Team + member(where: MemberWhereUniqueInput!): Member + financialBeings(where: FinancialBeingsWhereUniqueInput!): FinancialBeings + teamsConnection(where: TeamWhereInput, orderBy: TeamOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamConnection! + teamMembersesConnection(where: TeamMembersWhereInput, orderBy: TeamMembersOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamMembersConnection! + membersConnection(where: MemberWhereInput, orderBy: MemberOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): MemberConnection! + financialBeingsesConnection(where: FinancialBeingsWhereInput, orderBy: FinancialBeingsOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): FinancialBeingsConnection! + teamProfilesConnection(where: TeamProfileWhereInput, orderBy: TeamProfileOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamProfileConnection! + walletsConnection(where: WalletWhereInput, orderBy: WalletOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): WalletConnection! + fBStatusesConnection(where: FBStatusWhereInput, orderBy: FBStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): FBStatusConnection! + teamStatusesConnection(where: TeamStatusWhereInput, orderBy: TeamStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): TeamStatusConnection! + memberStatusesConnection(where: MemberStatusWhereInput, orderBy: MemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): MemberStatusConnection! + + """Fetches an object given its ID""" + node( + """The ID of an object""" + id: ID! + ): Node +} + +enum Role { + OWNER + ADMIN + MEMBER +} + +enum Status { + ACTIVE + INACTIVE + APPLY + INVITE +} + +type Subscription { + team(where: TeamSubscriptionWhereInput): TeamSubscriptionPayload + teamMembers(where: TeamMembersSubscriptionWhereInput): TeamMembersSubscriptionPayload + member(where: MemberSubscriptionWhereInput): MemberSubscriptionPayload + financialBeings(where: FinancialBeingsSubscriptionWhereInput): FinancialBeingsSubscriptionPayload + teamProfile(where: TeamProfileSubscriptionWhereInput): TeamProfileSubscriptionPayload + wallet(where: WalletSubscriptionWhereInput): WalletSubscriptionPayload + fBStatus(where: FBStatusSubscriptionWhereInput): FBStatusSubscriptionPayload + teamStatus(where: TeamStatusSubscriptionWhereInput): TeamStatusSubscriptionPayload + memberStatus(where: MemberStatusSubscriptionWhereInput): MemberStatusSubscriptionPayload +} + +type Team implements Node { + id: ID! + name: String! + slug: String! + profile(where: TeamProfileWhereInput): TeamProfile + members(where: TeamMembersWhereInput, orderBy: TeamMembersOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamMembers!] + owner: String! + wallet(where: WalletWhereInput): Wallet + fb(where: FinancialBeingsWhereInput, orderBy: FinancialBeingsOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [FinancialBeings!] + status(where: TeamStatusWhereInput, orderBy: TeamStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [TeamStatus!] + createdAt: DateTime! +} + +"""A connection to a list of items.""" +type TeamConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [TeamEdge]! + aggregate: AggregateTeam! +} + +input TeamCreateInput { + name: String! + slug: String! + owner: String! + profile: TeamProfileCreateOneInput + members: TeamMembersCreateManyWithoutTeamInput + wallet: WalletCreateOneInput + fb: FinancialBeingsCreateManyWithoutTeamInput + status: TeamStatusCreateManyInput +} + +input TeamCreateOneWithoutFbInput { + create: TeamCreateWithoutFbInput + connect: TeamWhereUniqueInput +} + +input TeamCreateOneWithoutMembersInput { + create: TeamCreateWithoutMembersInput + connect: TeamWhereUniqueInput +} + +input TeamCreateWithoutFbInput { + name: String! + slug: String! + owner: String! + profile: TeamProfileCreateOneInput + members: TeamMembersCreateManyWithoutTeamInput + wallet: WalletCreateOneInput + status: TeamStatusCreateManyInput +} + +input TeamCreateWithoutMembersInput { + name: String! + slug: String! + owner: String! + profile: TeamProfileCreateOneInput + wallet: WalletCreateOneInput + fb: FinancialBeingsCreateManyWithoutTeamInput + status: TeamStatusCreateManyInput +} + +"""An edge in a connection.""" +type TeamEdge { + """The item at the end of the edge.""" + node: Team! + + """A cursor for use in pagination.""" + cursor: String! +} + +type TeamMembers { + member(where: MemberWhereInput): Member + team(where: TeamWhereInput): Team + role: Role + status(where: MemberStatusWhereInput, orderBy: MemberStatusOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [MemberStatus!] + updatedAt: DateTime! +} + +"""A connection to a list of items.""" +type TeamMembersConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [TeamMembersEdge]! + aggregate: AggregateTeamMembers! +} + +input TeamMembersCreateInput { + role: Role + member: MemberCreateOneWithoutTeamInput + team: TeamCreateOneWithoutMembersInput + status: MemberStatusCreateManyInput +} + +input TeamMembersCreateManyWithoutTeamInput { + create: [TeamMembersCreateWithoutTeamInput!] +} + +input TeamMembersCreateOneWithoutMemberInput { + create: TeamMembersCreateWithoutMemberInput +} + +input TeamMembersCreateWithoutMemberInput { + role: Role + team: TeamCreateOneWithoutMembersInput + status: MemberStatusCreateManyInput +} + +input TeamMembersCreateWithoutTeamInput { + role: Role + member: MemberCreateOneWithoutTeamInput + status: MemberStatusCreateManyInput +} + +"""An edge in a connection.""" +type TeamMembersEdge { + """The item at the end of the edge.""" + node: TeamMembers! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum TeamMembersOrderByInput { + role_ASC + role_DESC + updatedAt_ASC + updatedAt_DESC + id_ASC + id_DESC + createdAt_ASC + createdAt_DESC +} + +type TeamMembersPreviousValues { + role: Role + updatedAt: DateTime! +} + +type TeamMembersSubscriptionPayload { + mutation: MutationType! + node: TeamMembers + updatedFields: [String!] + previousValues: TeamMembersPreviousValues +} + +input TeamMembersSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [TeamMembersSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamMembersSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamMembersSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: TeamMembersWhereInput +} + +input TeamMembersUpdateInput { + role: Role + member: MemberUpdateOneWithoutTeamInput + team: TeamUpdateOneWithoutMembersInput + status: MemberStatusUpdateManyInput +} + +input TeamMembersUpdateManyWithoutTeamInput { + create: [TeamMembersCreateWithoutTeamInput!] +} + +input TeamMembersUpdateOneWithoutMemberInput { + create: TeamMembersCreateWithoutMemberInput + disconnect: Boolean + delete: Boolean + update: TeamMembersUpdateWithoutMemberDataInput + upsert: TeamMembersUpsertWithoutMemberInput +} + +input TeamMembersUpdateWithoutMemberDataInput { + role: Role + team: TeamUpdateOneWithoutMembersInput + status: MemberStatusUpdateManyInput +} + +input TeamMembersUpsertWithoutMemberInput { + update: TeamMembersUpdateWithoutMemberDataInput! + create: TeamMembersCreateWithoutMemberInput! +} + +input TeamMembersWhereInput { + """Logical AND on all given filters.""" + AND: [TeamMembersWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamMembersWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamMembersWhereInput!] + role: Role + + """All values that are not equal to given value.""" + role_not: Role + + """All values that are contained in given list.""" + role_in: [Role!] + + """All values that are not contained in given list.""" + role_not_in: [Role!] + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime + member: MemberWhereInput + team: TeamWhereInput + status_every: MemberStatusWhereInput + status_some: MemberStatusWhereInput + status_none: MemberStatusWhereInput +} + +enum TeamOrderByInput { + id_ASC + id_DESC + name_ASC + name_DESC + slug_ASC + slug_DESC + owner_ASC + owner_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC +} + +type TeamPreviousValues { + id: ID! + name: String! + slug: String! + owner: String! + createdAt: DateTime! +} + +type TeamProfile { + description: String + motto: String + avatar: String + updatedAt: DateTime! +} + +"""A connection to a list of items.""" +type TeamProfileConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [TeamProfileEdge]! + aggregate: AggregateTeamProfile! +} + +input TeamProfileCreateInput { + description: String + motto: String + avatar: String +} + +input TeamProfileCreateOneInput { + create: TeamProfileCreateInput +} + +"""An edge in a connection.""" +type TeamProfileEdge { + """The item at the end of the edge.""" + node: TeamProfile! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum TeamProfileOrderByInput { + description_ASC + description_DESC + motto_ASC + motto_DESC + avatar_ASC + avatar_DESC + updatedAt_ASC + updatedAt_DESC + id_ASC + id_DESC + createdAt_ASC + createdAt_DESC +} + +type TeamProfilePreviousValues { + description: String + motto: String + avatar: String + updatedAt: DateTime! +} + +type TeamProfileSubscriptionPayload { + mutation: MutationType! + node: TeamProfile + updatedFields: [String!] + previousValues: TeamProfilePreviousValues +} + +input TeamProfileSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [TeamProfileSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamProfileSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamProfileSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: TeamProfileWhereInput +} + +input TeamProfileUpdateDataInput { + description: String + motto: String + avatar: String +} + +input TeamProfileUpdateInput { + description: String + motto: String + avatar: String +} + +input TeamProfileUpdateOneInput { + create: TeamProfileCreateInput + disconnect: Boolean + delete: Boolean + update: TeamProfileUpdateDataInput + upsert: TeamProfileUpsertNestedInput +} + +input TeamProfileUpsertNestedInput { + update: TeamProfileUpdateDataInput! + create: TeamProfileCreateInput! +} + +input TeamProfileWhereInput { + """Logical AND on all given filters.""" + AND: [TeamProfileWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamProfileWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamProfileWhereInput!] + description: String + + """All values that are not equal to given value.""" + description_not: String + + """All values that are contained in given list.""" + description_in: [String!] + + """All values that are not contained in given list.""" + description_not_in: [String!] + + """All values less than the given value.""" + description_lt: String + + """All values less than or equal the given value.""" + description_lte: String + + """All values greater than the given value.""" + description_gt: String + + """All values greater than or equal the given value.""" + description_gte: String + + """All values containing the given string.""" + description_contains: String + + """All values not containing the given string.""" + description_not_contains: String + + """All values starting with the given string.""" + description_starts_with: String + + """All values not starting with the given string.""" + description_not_starts_with: String + + """All values ending with the given string.""" + description_ends_with: String + + """All values not ending with the given string.""" + description_not_ends_with: String + motto: String + + """All values that are not equal to given value.""" + motto_not: String + + """All values that are contained in given list.""" + motto_in: [String!] + + """All values that are not contained in given list.""" + motto_not_in: [String!] + + """All values less than the given value.""" + motto_lt: String + + """All values less than or equal the given value.""" + motto_lte: String + + """All values greater than the given value.""" + motto_gt: String + + """All values greater than or equal the given value.""" + motto_gte: String + + """All values containing the given string.""" + motto_contains: String + + """All values not containing the given string.""" + motto_not_contains: String + + """All values starting with the given string.""" + motto_starts_with: String + + """All values not starting with the given string.""" + motto_not_starts_with: String + + """All values ending with the given string.""" + motto_ends_with: String + + """All values not ending with the given string.""" + motto_not_ends_with: String + avatar: String + + """All values that are not equal to given value.""" + avatar_not: String + + """All values that are contained in given list.""" + avatar_in: [String!] + + """All values that are not contained in given list.""" + avatar_not_in: [String!] + + """All values less than the given value.""" + avatar_lt: String + + """All values less than or equal the given value.""" + avatar_lte: String + + """All values greater than the given value.""" + avatar_gt: String + + """All values greater than or equal the given value.""" + avatar_gte: String + + """All values containing the given string.""" + avatar_contains: String + + """All values not containing the given string.""" + avatar_not_contains: String + + """All values starting with the given string.""" + avatar_starts_with: String + + """All values not starting with the given string.""" + avatar_not_starts_with: String + + """All values ending with the given string.""" + avatar_ends_with: String + + """All values not ending with the given string.""" + avatar_not_ends_with: String + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime +} + +type TeamStatus { + status: Status + reason: String + createdAt: DateTime! +} + +"""A connection to a list of items.""" +type TeamStatusConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [TeamStatusEdge]! + aggregate: AggregateTeamStatus! +} + +input TeamStatusCreateInput { + status: Status + reason: String +} + +input TeamStatusCreateManyInput { + create: [TeamStatusCreateInput!] +} + +"""An edge in a connection.""" +type TeamStatusEdge { + """The item at the end of the edge.""" + node: TeamStatus! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum TeamStatusOrderByInput { + status_ASC + status_DESC + reason_ASC + reason_DESC + createdAt_ASC + createdAt_DESC + id_ASC + id_DESC + updatedAt_ASC + updatedAt_DESC +} + +type TeamStatusPreviousValues { + status: Status + reason: String + createdAt: DateTime! +} + +type TeamStatusSubscriptionPayload { + mutation: MutationType! + node: TeamStatus + updatedFields: [String!] + previousValues: TeamStatusPreviousValues +} + +input TeamStatusSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [TeamStatusSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamStatusSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamStatusSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: TeamStatusWhereInput +} + +input TeamStatusUpdateInput { + status: Status + reason: String +} + +input TeamStatusUpdateManyInput { + create: [TeamStatusCreateInput!] +} + +input TeamStatusWhereInput { + """Logical AND on all given filters.""" + AND: [TeamStatusWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamStatusWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamStatusWhereInput!] + status: Status + + """All values that are not equal to given value.""" + status_not: Status + + """All values that are contained in given list.""" + status_in: [Status!] + + """All values that are not contained in given list.""" + status_not_in: [Status!] + reason: String + + """All values that are not equal to given value.""" + reason_not: String + + """All values that are contained in given list.""" + reason_in: [String!] + + """All values that are not contained in given list.""" + reason_not_in: [String!] + + """All values less than the given value.""" + reason_lt: String + + """All values less than or equal the given value.""" + reason_lte: String + + """All values greater than the given value.""" + reason_gt: String + + """All values greater than or equal the given value.""" + reason_gte: String + + """All values containing the given string.""" + reason_contains: String + + """All values not containing the given string.""" + reason_not_contains: String + + """All values starting with the given string.""" + reason_starts_with: String + + """All values not starting with the given string.""" + reason_not_starts_with: String + + """All values ending with the given string.""" + reason_ends_with: String + + """All values not ending with the given string.""" + reason_not_ends_with: String + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime +} + +type TeamSubscriptionPayload { + mutation: MutationType! + node: Team + updatedFields: [String!] + previousValues: TeamPreviousValues +} + +input TeamSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [TeamSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: TeamWhereInput +} + +input TeamUpdateInput { + name: String + slug: String + owner: String + profile: TeamProfileUpdateOneInput + members: TeamMembersUpdateManyWithoutTeamInput + wallet: WalletUpdateOneInput + fb: FinancialBeingsUpdateManyWithoutTeamInput + status: TeamStatusUpdateManyInput +} + +input TeamUpdateOneWithoutFbInput { + create: TeamCreateWithoutFbInput + connect: TeamWhereUniqueInput + disconnect: Boolean + delete: Boolean + update: TeamUpdateWithoutFbDataInput + upsert: TeamUpsertWithoutFbInput +} + +input TeamUpdateOneWithoutMembersInput { + create: TeamCreateWithoutMembersInput + connect: TeamWhereUniqueInput + disconnect: Boolean + delete: Boolean + update: TeamUpdateWithoutMembersDataInput + upsert: TeamUpsertWithoutMembersInput +} + +input TeamUpdateWithoutFbDataInput { + name: String + slug: String + owner: String + profile: TeamProfileUpdateOneInput + members: TeamMembersUpdateManyWithoutTeamInput + wallet: WalletUpdateOneInput + status: TeamStatusUpdateManyInput +} + +input TeamUpdateWithoutMembersDataInput { + name: String + slug: String + owner: String + profile: TeamProfileUpdateOneInput + wallet: WalletUpdateOneInput + fb: FinancialBeingsUpdateManyWithoutTeamInput + status: TeamStatusUpdateManyInput +} + +input TeamUpsertWithoutFbInput { + update: TeamUpdateWithoutFbDataInput! + create: TeamCreateWithoutFbInput! +} + +input TeamUpsertWithoutMembersInput { + update: TeamUpdateWithoutMembersDataInput! + create: TeamCreateWithoutMembersInput! +} + +input TeamWhereInput { + """Logical AND on all given filters.""" + AND: [TeamWhereInput!] + + """Logical OR on all given filters.""" + OR: [TeamWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [TeamWhereInput!] + id: ID + + """All values that are not equal to given value.""" + id_not: ID + + """All values that are contained in given list.""" + id_in: [ID!] + + """All values that are not contained in given list.""" + id_not_in: [ID!] + + """All values less than the given value.""" + id_lt: ID + + """All values less than or equal the given value.""" + id_lte: ID + + """All values greater than the given value.""" + id_gt: ID + + """All values greater than or equal the given value.""" + id_gte: ID + + """All values containing the given string.""" + id_contains: ID + + """All values not containing the given string.""" + id_not_contains: ID + + """All values starting with the given string.""" + id_starts_with: ID + + """All values not starting with the given string.""" + id_not_starts_with: ID + + """All values ending with the given string.""" + id_ends_with: ID + + """All values not ending with the given string.""" + id_not_ends_with: ID + name: String + + """All values that are not equal to given value.""" + name_not: String + + """All values that are contained in given list.""" + name_in: [String!] + + """All values that are not contained in given list.""" + name_not_in: [String!] + + """All values less than the given value.""" + name_lt: String + + """All values less than or equal the given value.""" + name_lte: String + + """All values greater than the given value.""" + name_gt: String + + """All values greater than or equal the given value.""" + name_gte: String + + """All values containing the given string.""" + name_contains: String + + """All values not containing the given string.""" + name_not_contains: String + + """All values starting with the given string.""" + name_starts_with: String + + """All values not starting with the given string.""" + name_not_starts_with: String + + """All values ending with the given string.""" + name_ends_with: String + + """All values not ending with the given string.""" + name_not_ends_with: String + slug: String + + """All values that are not equal to given value.""" + slug_not: String + + """All values that are contained in given list.""" + slug_in: [String!] + + """All values that are not contained in given list.""" + slug_not_in: [String!] + + """All values less than the given value.""" + slug_lt: String + + """All values less than or equal the given value.""" + slug_lte: String + + """All values greater than the given value.""" + slug_gt: String + + """All values greater than or equal the given value.""" + slug_gte: String + + """All values containing the given string.""" + slug_contains: String + + """All values not containing the given string.""" + slug_not_contains: String + + """All values starting with the given string.""" + slug_starts_with: String + + """All values not starting with the given string.""" + slug_not_starts_with: String + + """All values ending with the given string.""" + slug_ends_with: String + + """All values not ending with the given string.""" + slug_not_ends_with: String + owner: String + + """All values that are not equal to given value.""" + owner_not: String + + """All values that are contained in given list.""" + owner_in: [String!] + + """All values that are not contained in given list.""" + owner_not_in: [String!] + + """All values less than the given value.""" + owner_lt: String + + """All values less than or equal the given value.""" + owner_lte: String + + """All values greater than the given value.""" + owner_gt: String + + """All values greater than or equal the given value.""" + owner_gte: String + + """All values containing the given string.""" + owner_contains: String + + """All values not containing the given string.""" + owner_not_contains: String + + """All values starting with the given string.""" + owner_starts_with: String + + """All values not starting with the given string.""" + owner_not_starts_with: String + + """All values ending with the given string.""" + owner_ends_with: String + + """All values not ending with the given string.""" + owner_not_ends_with: String + createdAt: DateTime + + """All values that are not equal to given value.""" + createdAt_not: DateTime + + """All values that are contained in given list.""" + createdAt_in: [DateTime!] + + """All values that are not contained in given list.""" + createdAt_not_in: [DateTime!] + + """All values less than the given value.""" + createdAt_lt: DateTime + + """All values less than or equal the given value.""" + createdAt_lte: DateTime + + """All values greater than the given value.""" + createdAt_gt: DateTime + + """All values greater than or equal the given value.""" + createdAt_gte: DateTime + profile: TeamProfileWhereInput + members_every: TeamMembersWhereInput + members_some: TeamMembersWhereInput + members_none: TeamMembersWhereInput + wallet: WalletWhereInput + fb_every: FinancialBeingsWhereInput + fb_some: FinancialBeingsWhereInput + fb_none: FinancialBeingsWhereInput + status_every: TeamStatusWhereInput + status_some: TeamStatusWhereInput + status_none: TeamStatusWhereInput +} + +input TeamWhereUniqueInput { + id: ID + name: String + slug: String +} + +type Wallet { + balance: Float + updatedAt: DateTime! +} + +"""A connection to a list of items.""" +type WalletConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [WalletEdge]! + aggregate: AggregateWallet! +} + +input WalletCreateInput { + balance: Float +} + +input WalletCreateOneInput { + create: WalletCreateInput +} + +"""An edge in a connection.""" +type WalletEdge { + """The item at the end of the edge.""" + node: Wallet! + + """A cursor for use in pagination.""" + cursor: String! +} + +enum WalletOrderByInput { + balance_ASC + balance_DESC + updatedAt_ASC + updatedAt_DESC + id_ASC + id_DESC + createdAt_ASC + createdAt_DESC +} + +type WalletPreviousValues { + balance: Float + updatedAt: DateTime! +} + +type WalletSubscriptionPayload { + mutation: MutationType! + node: Wallet + updatedFields: [String!] + previousValues: WalletPreviousValues +} + +input WalletSubscriptionWhereInput { + """Logical AND on all given filters.""" + AND: [WalletSubscriptionWhereInput!] + + """Logical OR on all given filters.""" + OR: [WalletSubscriptionWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [WalletSubscriptionWhereInput!] + + """ + The subscription event gets dispatched when it's listed in mutation_in + """ + mutation_in: [MutationType!] + + """ + The subscription event gets only dispatched when one of the updated fields names is included in this list + """ + updatedFields_contains: String + + """ + The subscription event gets only dispatched when all of the field names included in this list have been updated + """ + updatedFields_contains_every: [String!] + + """ + The subscription event gets only dispatched when some of the field names included in this list have been updated + """ + updatedFields_contains_some: [String!] + node: WalletWhereInput +} + +input WalletUpdateDataInput { + balance: Float +} + +input WalletUpdateInput { + balance: Float +} + +input WalletUpdateOneInput { + create: WalletCreateInput + disconnect: Boolean + delete: Boolean + update: WalletUpdateDataInput + upsert: WalletUpsertNestedInput +} + +input WalletUpsertNestedInput { + update: WalletUpdateDataInput! + create: WalletCreateInput! +} + +input WalletWhereInput { + """Logical AND on all given filters.""" + AND: [WalletWhereInput!] + + """Logical OR on all given filters.""" + OR: [WalletWhereInput!] + + """Logical NOT on all given filters combined by AND.""" + NOT: [WalletWhereInput!] + balance: Float + + """All values that are not equal to given value.""" + balance_not: Float + + """All values that are contained in given list.""" + balance_in: [Float!] + + """All values that are not contained in given list.""" + balance_not_in: [Float!] + + """All values less than the given value.""" + balance_lt: Float + + """All values less than or equal the given value.""" + balance_lte: Float + + """All values greater than the given value.""" + balance_gt: Float + + """All values greater than or equal the given value.""" + balance_gte: Float + updatedAt: DateTime + + """All values that are not equal to given value.""" + updatedAt_not: DateTime + + """All values that are contained in given list.""" + updatedAt_in: [DateTime!] + + """All values that are not contained in given list.""" + updatedAt_not_in: [DateTime!] + + """All values less than the given value.""" + updatedAt_lt: DateTime + + """All values less than or equal the given value.""" + updatedAt_lte: DateTime + + """All values greater than the given value.""" + updatedAt_gt: DateTime + + """All values greater than or equal the given value.""" + updatedAt_gte: DateTime +} diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/TeamsConnectionsFragment.js new file mode 100644 index 000000000..89a22a8c4 --- /dev/null +++ b/api/src/graphql/TeamsConnectionsFragment.js @@ -0,0 +1,33 @@ +const gql = require('graphql-tag') + +const TEAMS_CONNECTIONS_FRAGMENT = gql`{ + edges { + node { + id + name + slug + owner + status { + status + reason + createdAt + } + createdAt + profile { + avatar + description + motto + updatedAt + } + members { + role + member { + alias + authId + } + } + } + } +}` + +module.exports = TEAMS_CONNECTIONS_FRAGMENT diff --git a/api/src/graphql/TeamsFragment.js b/api/src/graphql/TeamsFragment.js new file mode 100644 index 000000000..e75ce8382 --- /dev/null +++ b/api/src/graphql/TeamsFragment.js @@ -0,0 +1,29 @@ +const gql = require('graphql-tag') + +const TEAMS_FRAGMENT = gql`{ + id + name + slug + owner + status { + status + reason + createdAt + } + createdAt + profile { + avatar + description + motto + updatedAt + } + members { + role + member { + alias + authId + } + } +}` + +module.exports = TEAMS_FRAGMENT diff --git a/api/src/index.js b/api/src/index.js index 33439b574..d640b8682 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -1,3 +1,4 @@ +require('dotenv').config(); const { GraphQLServer } = require('graphql-yoga') const { Prisma, @@ -6,51 +7,48 @@ const { } = require('prisma-binding') const { makeExecutableSchema } = require('graphql-tools') const { importSchema } = require('graphql-import') -const { checkJwt } = require('./auth/middleware/jwt') -const { getMember } = require('./auth/middleware/getMember') -const validateParseIdToken = require('./auth/helpers/validate-parse-id-token') -const { directiveResolvers } = require('./auth-directives') -const ctxMember = ctx => ctx.request.member +const checkJwt = require('./auth/middleware/jwt') +const { getMember } = require('./auth/middleware/getMember') +const { validateIdToken } = require('./auth/validateIdToken') +const { directiveResolvers } = require('./auth/authDirectives') + +const TEAMS_FRAGMENT = require('./graphql/TeamsFragment') +const TEAMS_CONNECTIONS_FRAGMENT = require('./graphql/TeamsConnectionsFragment') + +const createMember = async function (ctx, info, idToken) { + console.log('createMember', idToken) + const member = await ctx.db.mutation.upsertMember({ + where: { + authId: idToken.sub, + }, + create: { + authId: idToken.sub, + alias: idToken.nickname + }, + update: { + authId: idToken.sub, + alias: idToken.nickname + } + }, info) + return member +} + +const ctxMember = ctx => ctx.request.user const resolvers = { Query: { - async member(parent, { auth0id }, ctx, info) { - return ctx.db.query.member({ where: { auth0id } }, info) + async member(parent, arg, ctx, info) { + console.log('member: ', parent, arg) + return ctx.db.query.member({ where: { authId: arg.authId } }, info) + }, + async currentMember(parent, args, ctx, info) { + console.log('currentMember: ', ctx.request.res.req.user) + const authId = ctx.request.res.req.user.sub + return ctx.db.query.member({ where: { authId } }, info) }, teams(parent, args, ctx, info) { - return ctx.db.query.teamsConnection({}, - `{ - edges { - node { - id - name - slug - profile { - avatar - description - motto - } - owner { - nickname - visible - profile { - avatar - } - } - members { - role - member { - nickname - visible - profile { - avatar - } - } - } - } - } - }`) + return ctx.db.query.teamsConnection({}, TEAMS_CONNECTIONS_FRAGMENT) }, async teamById(parent, { id }, ctx, info) { return ctx.db.query.team({ where: { id } }, info) @@ -58,65 +56,33 @@ const resolvers = { async teamByName(parent, { name }, ctx, info) { return ctx.db.query.team({ where: { name: name } }, `{ name }`) }, - async teamByOwner(parent, { ownerId }, ctx, info) { - return ctx.db.query.teamsConnection({where: { owner: { auth0id: ownerId }}}, - `{ - edges { - node { - id - name - slug - profile { - avatar - description - motto - } - owner { - auth0id - nickname - visible - profile { - avatar - } - } - members { - role - member { - nickname - visible - profile { - avatar - } - } - } - } - } - }`) + async teamsByOwner(parent, { ownerId }, ctx, info) { + console.log('teamsByOwner: ', ctx.user) + return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) }, async owner(parent, args, ctx, info) { console.log('resolver.query.owner ctx: ', ctxMember(ctx)) - return ctx.db.query.member({ where: { id: ctxMember(ctx).auth0id } }, info) + return ctx.db.query.member({ where: { id: ctxMember(ctx).authId } }, info) .catch((res) => { console.log('createTeam error: ', res) const errors = res.graphQLErrors.map((error) => { return error.message }) }) - }, - members: forwardTo('db') + } }, Mutation: { async authenticate(parent, { idToken }, ctx, info) { let memberToken = null try { - memberToken = await validateAndParseIdToken(idToken) + memberToken = await validateIdToken(idToken) console.log('authenticate.memberToken: ', await memberToken) } catch (err) { - console.log('authenticat.validateAndParseIdToken err: ', err) + console.log('authenticat.validateIdToken err: ', err) throw new Error(err.message) } - const auth0id = memberToken.sub - let member = await ctx.db.query.member({ where: { auth0id } }, info) + const authId = memberToken.sub + let member = await ctx.db.query.member({ where: { authId } }, info) if (!member) { try { @@ -124,28 +90,25 @@ const resolvers = { } catch (error) { throw new Error(error.message) } - } } return member }, async createTeam(parent, { name, slug, owner }, ctx, info) { - return ctx.db.mutation.createTeam({ - data: { - name: name, - slug: slug, - owner: {connect:{auth0id: owner}}, - members: {create: {role: 'OWNER', member: {connect: {auth0id: owner}}}} - } - }, info) - .catch((res) => { - console.log('createTeam error: ', res) - const errors = res.graphQLErrors.map((error) => { - return error.message - }) + return ctx.db.mutation.createTeam({ data: { name: name, slug: slug, owner: owner, members: { create: { member:{ connect:{ authId: owner} }, role: 'OWNER' } }, profile:{create:{avatar:"a"} } } }, TEAMS_FRAGMENT) + .catch((err) => { + console.log('createTeam error: ', err) + return err }) }, - async deleteTeam(parent, { id }, ctx, info) { - return ctx.db.mutation.deleteTeam({ where: { id } }, info) + async updateTeamProfile(parent, { slug, owner, description, motto, avatar }, ctx, info) { + return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar}}}, where:{slug: slug}}, TEAMS_FRAGMENT) + .catch((err) => { + console.log('createTeam error: ', err) + return err + }) + }, + async deleteTeam(parent, { slug }, ctx, info) { + return ctx.db.mutation.deleteTeam({ where: { slug } }, info) .catch((res) => { console.log('deleteTeam error: ', res) const errors = res.graphQLErrors.map((error) => { @@ -174,27 +137,34 @@ const schema = makeExecutableSchema({ const server = new GraphQLServer({ schema, context: req => ({ - ...req, - db - }) + token: req.headers, + user: req.user, + db, + ...req + }), }) -server.express.post( +server.express.use( server.options.endpoint, checkJwt, - (err, req, res, next) => { - if (err) return res.status(401).send(err.message) - next() + function (err, req, res, next) { + console.log('checkJwt: ', err, req) + if (err) { + return res.status(201).send(err.message) + } else { + next() + } } ) -server.express.post(server.options.endpoint, (req, res, done) => - getMember(req, res, done, db) -) +server.express.post(server.options.endpoint, (req, res, done) => getUser(req, res, done, db)) const whitelist = [ - 'http://localhost:1337', - 'http://0.0.0.0:5000' + 'http://localhost:3000', + 'http://localhost:3001', + 'http://localhost:3002', + 'http://localhost:4000', + 'http://localhost:4002' ] const corsOptionsDelegate = (req, callback) => { var corsOptions @@ -207,12 +177,13 @@ const corsOptionsDelegate = (req, callback) => { } const options = { + port: 4001, cors: corsOptionsDelegate, endpoint: '/graphql', subscriptions: '/graphql', playground: '/playground' } -server.start(options, () => console.log(`Server is running on http://localhost:4000${server.options.endpoint}`)) +server.start(options, () => console.log(`Server is running on http://localhost:4001${server.options.endpoint}`)) -module.exports= { createPrismaMember: createPrismaMember } +module.exports= { createMember } diff --git a/api/src/schema.graphql b/api/src/schema.graphql new file mode 100644 index 000000000..1450c6000 --- /dev/null +++ b/api/src/schema.graphql @@ -0,0 +1,49 @@ +# import Query.TeamConnection, Team from "./generated/prisma.graphql" + +directive @isAuthenticated on QUERY | FIELD | MUTATION +directive @hasRole(roles: [String]) on QUERY | FIELD | MUTATION +directive @isOwner(type: String) on QUERY | MUTATION +directive @isOwnerOrHasRole(type: String, roles: [String]) on QUERY | MUTATION + +type Query { + teams: TeamConnection + teamDetails: [Team!]! @isAuthenticated + teamByName(name: String!): Team + teamById(teamId: String!): Team + teamsByOwner(ownerId: String!): [Team!]! @isAuthenticated + owner: Member @isAuthenticated + currentMember: Member @isAuthenticated + member(authId: String!): Member @isAuthenticated +} + +type Mutation { + authenticate(idToken: String!): Member + createTeam(name: String!, slug: String!, owner: String!): Team @isAuthenticated + updateTeamProfile(slug: String!, owner: String!, description: String, motto: String, avatar: String): Team @isAuthenticated + deleteTeam(slug: String!): Team @isAuthenticated @isOwner(type: "Team") +} + +input MemberProfileInput { + authId: String! + alias: String! +} + +fragment MemberInfo on Member { + id + authId + alias + isActive + profile { + ...MemberProfileInfo + } +} + +fragment MemberProfileInfo on MemberProfile { + firstName + lastName + email + avatar + role { + name + } +} diff --git a/api/src/schema.graphqls b/api/src/schema.graphqls deleted file mode 100644 index b61df8546..000000000 --- a/api/src/schema.graphqls +++ /dev/null @@ -1,35 +0,0 @@ -# import Query.TeamConnection, Team from "./generated/prisma.graphql" - -directive @isAuthenticated on QUERY | FIELD | MUTATION -directive @hasRole(roles: [String]) on QUERY | FIELD | MUTATION -directive @isOwner(type: String) on QUERY | MUTATION -directive @isOwnerOrHasRole(type: String, roles: [String]) on QUERY | MUTATION - -type Query { - teams: TeamConnection - teamDetails: [Team!]! @isAuthenticated - teamByName(name: String!): Team - teamById(teamId: String!): Team - teamByOwner(ownerId: String!): TeamConnection - owner: Member @isAuthenticated - member(auth0id: String!): Member @isAuthenticated -} - -type Mutation { - authenticate(idToken: String!): Member - createTeam(name: String!, slug: String, owner: String): Team @isAuthenticated - deleteTeam(id: ID!): Team @isAuthenticated @isOwner(type: "Team") - updateProfile(data: MemberProfileInput): Member @isAuthenticated -} - -input MemberProfileInput { - auth0id: String! - nickname: String! - anonymous: Boolean - visible: Boolean - avatar: String - email: String - repo: String - bio: String - status: String -} diff --git a/client/src/App.js b/client/src/App.js index 5dcdda261..652531151 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -20,7 +20,9 @@ import { Footer, Callback, Home, + About, Dashboard, + Teams, theme, globalStyles } from './views' @@ -148,7 +150,14 @@ class App extends Component { - + + + + } + /> { diff --git a/client/src/assets/logos/advanced-algos/aa-logo-dark.svg b/client/src/assets/logos/advanced-algos/aa-logo-dark.svg new file mode 100644 index 000000000..479feeb86 --- /dev/null +++ b/client/src/assets/logos/advanced-algos/aa-logo-dark.svg @@ -0,0 +1 @@ +aa-logo-dark \ No newline at end of file diff --git a/client/src/auth/index.js b/client/src/auth/index.js index 0b01b3e47..3c0971845 100644 --- a/client/src/auth/index.js +++ b/client/src/auth/index.js @@ -102,9 +102,13 @@ class Auth { this.signinOrCreateAccount({ ...data }) this.cb(data) console.log(authResult.idTokenPayload) - setItem('user', JSON.stringify(authResult.idTokenPayload)) + const user = { + authId: authResult.idTokenPayload.sub, + alias: authResult.idTokenPayload.nickname + } + setItem('user', JSON.stringify(user)) if (window.location.href.includes(`callback`)) { - window.location.href = '/dashboard' + // window.location.href = '/dashboard' } return true } @@ -126,11 +130,13 @@ class Auth { }) console.log('signinOrCreateAccount auth: ', await data) - setItem('user', JSON.stringify(data.data.authenticate)) + const user = { + authId: data.data.authenticate.authId, + alias: data.data.authenticate.alias + } + setItem('user', JSON.stringify(user)) if (window.location.href.includes(`callback`)) { window.location.href = '/dashboard' - } else { - window.location.reload() } return data } catch (err) { @@ -157,10 +163,16 @@ class Auth { async isAuthenticated () { // check session and run Auth0 SS0 const getUser = await getItem('user') + const getExpires = await getItem('expires_at') let user = JSON.parse(getUser) - if (validObject(user, 'exp') && new Date().getTime() < user.exp * 1000) { - console.log('handleAuth.user exp: ', user, window.location.href) + if (new Date().getTime() < getExpires * 1000) { + console.log( + 'handleAuth.user exp: ', + user, + getExpires, + window.location.href + ) return user } @@ -168,7 +180,11 @@ class Auth { const checkSSO = await this.checkSession() .then(result => { console.log('handleAuth.checksessions: ', result) - setItem('user', result.idTokenPayload.sub) + const user = { + authId: result.idTokenPayload.sub, + alias: result.idTokenPayload.nickname + } + setItem('user', JSON.stringify(user)) // user confirmed, log into client this.setSession(result) return result.idTokenPayload @@ -180,13 +196,6 @@ class Auth { } return err }) - if ( - (window.location.href.includes(`manage`) || - window.location.href.includes(`profile`)) && - !user - ) { - window.location.href = '/' - } if (/manage|profile|create|dashboard/.test(window.location.href) && !user) { this.login() diff --git a/client/src/graphql/teams/CreateTeamMutation.js b/client/src/graphql/teams/CreateTeamMutation.js index 752546daa..3d0324230 100644 --- a/client/src/graphql/teams/CreateTeamMutation.js +++ b/client/src/graphql/teams/CreateTeamMutation.js @@ -1,14 +1,14 @@ /* eslint-disable no-unused-vars */ import gql from 'graphql-tag' -export const CreateTeamMutation = gql` +const CREATE_TEAM = gql` mutation CreateTeamMutation($name: String!, $slug: String!, $owner: String!) { createTeam(name: $name, slug: $slug, owner: $owner) { id name slug owner - members{ + members { role member { authId @@ -18,3 +18,5 @@ export const CreateTeamMutation = gql` } } ` + +export default CREATE_TEAM diff --git a/client/src/graphql/teams/DeleteTeamMutation.js b/client/src/graphql/teams/DeleteTeamMutation.js index 91723348d..8e349c58a 100644 --- a/client/src/graphql/teams/DeleteTeamMutation.js +++ b/client/src/graphql/teams/DeleteTeamMutation.js @@ -1,10 +1,12 @@ /* eslint-disable no-unused-vars */ import gql from 'graphql-tag' -export const DeleteTeamMutation = gql` - mutation deleteTeam($id: ID!) { - deleteTeam(id: $id) { +export const DELETE_TEAM = gql` + mutation deleteTeam($slug: String!) { + deleteTeam(slug: $slug) { id } } ` + +export default DELETE_TEAM diff --git a/client/src/graphql/teams/GetAllTeamsQuery.js b/client/src/graphql/teams/GetAllTeamsQuery.js index 12acb8702..a2aab251a 100644 --- a/client/src/graphql/teams/GetAllTeamsQuery.js +++ b/client/src/graphql/teams/GetAllTeamsQuery.js @@ -1,7 +1,7 @@ /* eslint-disable no-unused-vars */ import gql from 'graphql-tag' -export const GetAllTeamsQuery = gql` +export const GET_ALL_TEAMS_QUERY = gql` query teamsQuery { teams { edges { @@ -9,26 +9,24 @@ export const GetAllTeamsQuery = gql` id name slug + owner + status { + status + reason + createdAt + } + createdAt profile { avatar description motto - } - owner { - nickname - visible - profile { - avatar - } + updatedAt } members { role member { - nickname - visible - profile { - avatar - } + alias + authId } } } @@ -37,4 +35,4 @@ export const GetAllTeamsQuery = gql` } ` -export default GetAllTeamsQuery +export default GET_ALL_TEAMS_QUERY diff --git a/client/src/graphql/teams/GetTeamQuery.js b/client/src/graphql/teams/GetTeamQuery.js deleted file mode 100644 index 8f97b6687..000000000 --- a/client/src/graphql/teams/GetTeamQuery.js +++ /dev/null @@ -1,40 +0,0 @@ -/* eslint-disable no-unused-vars */ -import gql from 'graphql-tag' - -export const GetTeamQuery = gql` - query teamDetailsQuery($auth0id: String!) { - teamByOwner(ownerId: $auth0id) { - edges { - node { - id - name - slug - profile { - avatar - description - motto - } - owner { - nickname - visible - profile { - avatar - } - } - members { - role - member { - nickname - visible - profile { - avatar - } - } - } - } - } - } - } -` - -export default GetTeamQuery diff --git a/client/src/graphql/teams/GetTeamsByOwnerQuery.js b/client/src/graphql/teams/GetTeamsByOwnerQuery.js new file mode 100644 index 000000000..d2f60c882 --- /dev/null +++ b/client/src/graphql/teams/GetTeamsByOwnerQuery.js @@ -0,0 +1,34 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +export const GET_TEAMS_BY_OWNER = gql` + query teamsByOwnerQuery($authId: String!) { + teamsByOwner(ownerId: $authId) { + id + name + slug + owner + status { + status + reason + createdAt + } + createdAt + profile { + avatar + description + motto + updatedAt + } + members { + role + member { + alias + authId + } + } + } + } +` + +export default GET_TEAMS_BY_OWNER diff --git a/client/src/graphql/teams/Teams.js b/client/src/graphql/teams/Teams.js new file mode 100644 index 000000000..76c60bbb8 --- /dev/null +++ b/client/src/graphql/teams/Teams.js @@ -0,0 +1,32 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +export const TEAMS_FRAGMENT = gql` + fragment TeamsResponse on teams { + id + name + slug + owner + status { + status + reason + createdAt + } + createdAt + profile { + avatar + description + motto + updatedAt + } + members { + role + member { + alias + authId + } + } + } +` + +export default TEAMS_FRAGMENT diff --git a/client/src/graphql/teams/UpdateTeamProfileMutation.js b/client/src/graphql/teams/UpdateTeamProfileMutation.js new file mode 100644 index 000000000..6da940d4c --- /dev/null +++ b/client/src/graphql/teams/UpdateTeamProfileMutation.js @@ -0,0 +1,34 @@ +/* eslint-disable no-unused-vars */ +import gql from 'graphql-tag' + +const UPDATE_TEAM_PROFILE = gql` + mutation UpdateTeamProfileMutation($slug: String!, $owner: String!, $description: String, $motto: String) { + updateTeamProfile(slug: $slug, owner: $owner, description: $description, motto: $motto) { + id + name + slug + owner + status { + status + reason + createdAt + } + createdAt + profile { + avatar + description + motto + updatedAt + } + members { + role + member { + alias + authId + } + } + } + } +` + +export default UPDATE_TEAM_PROFILE diff --git a/client/src/utils/graphql-errors.js b/client/src/utils/graphql-errors.js new file mode 100644 index 000000000..26b9c18ce --- /dev/null +++ b/client/src/utils/graphql-errors.js @@ -0,0 +1,13 @@ +const ErrorResponse = { + 'unique constraint': 'Sorry, already taken! Please create a unique ' +} + +export const checkGraphQLError = error => { + console.log('checkGraphQLError ', ErrorResponse) + if (error.search(/unique constraint/)) { + const field = error.substring(error.indexOf('=') + 1) + console.log('unique constraint', field.replace(/ +/g, '')) + } +} + +export default checkGraphQLError diff --git a/client/src/utils/js-helpers.js b/client/src/utils/js-helpers.js index eb7a0c3b6..7db4cb28a 100644 --- a/client/src/utils/js-helpers.js +++ b/client/src/utils/js-helpers.js @@ -19,6 +19,10 @@ export function isEmpty (obj) { return true } +export function isString (value) { + return typeof value === 'string' || value instanceof String +} + export function validObject (object, ...keys) { return keys.reduce((a, b) => (a || {})[b], object) !== undefined } diff --git a/client/src/views/about/index.js b/client/src/views/about/index.js new file mode 100644 index 000000000..5f4323210 --- /dev/null +++ b/client/src/views/about/index.js @@ -0,0 +1,61 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import CssBaseline from '@material-ui/core/CssBaseline' +import Typography from '@material-ui/core/Typography' +import { withStyles } from '@material-ui/core/styles' + +const styles = theme => ({ + '@global': { + body: { + backgroundColor: theme.palette.common.white + } + }, + layout: { + width: 'auto', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + [theme.breakpoints.up(900 + theme.spacing.unit * 3 * 2)]: { + width: 900, + marginLeft: 'auto', + marginRight: 'auto' + } + }, + heroContent: { + maxWidth: 600, + margin: '0 auto', + padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + } +}) + +const About = ({ classes }) => ( + + +
+
+ + Collaborate. Innovate. Evolve. + + + Welcome to the Advanced Algos Teams Module + +
+
+
+) + +About.propTypes = { + classes: PropTypes.object.isRequired +} + +export default withStyles(styles)(About) diff --git a/client/src/views/common/MessageCard.js b/client/src/views/common/MessageCard.js new file mode 100644 index 000000000..9c45891d0 --- /dev/null +++ b/client/src/views/common/MessageCard.js @@ -0,0 +1,43 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Typography from '@material-ui/core/Typography' +import { withStyles } from '@material-ui/core/styles' + +import Card from '@material-ui/core/Card' +import CardContent from '@material-ui/core/CardContent' + +const styles = theme => ({ + card: { + display: 'flex', + width: '100%', + margin: '1.5em', + padding: '3em' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + } +}) + +export const MessageCard = ({ classes, message, children }) => ( + +
+ + {message} + {children} + +
+
+) + +MessageCard.propTypes = { + children: PropTypes.node, + classes: PropTypes.object.isRequired, + message: PropTypes.string +} + +export default withStyles(styles)(MessageCard) diff --git a/client/src/views/common/index.js b/client/src/views/common/index.js new file mode 100644 index 000000000..fe07e8f49 --- /dev/null +++ b/client/src/views/common/index.js @@ -0,0 +1,3 @@ +import MessageCard from './MessageCard' + +export { MessageCard } diff --git a/client/src/views/dashboard/ManageTeams.js b/client/src/views/dashboard/ManageTeams.js new file mode 100644 index 000000000..7eceeb1ca --- /dev/null +++ b/client/src/views/dashboard/ManageTeams.js @@ -0,0 +1,28 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Typography from '@material-ui/core/Typography' +import { withStyles } from '@material-ui/core/styles' + +import ManageTeamsList from './components/ManageTeamsList' + +const styles = theme => ({ + tableContainer: { + height: 320 + } +}) + +const ManageTeams = ({ classes }) => ( +
+ + Manage Your Teams + + +
+) + +ManageTeams.propTypes = { + classes: PropTypes.object.isRequired +} + +export default withStyles(styles)(ManageTeams) diff --git a/client/src/views/dashboard/Overview.js b/client/src/views/dashboard/Overview.js new file mode 100644 index 000000000..87aa217bf --- /dev/null +++ b/client/src/views/dashboard/Overview.js @@ -0,0 +1,63 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' + +import Grid from '@material-ui/core/Grid' + +import DashTeam from './components/DashTeam' +import DashEditor from './components/DashEditor' + +const styles = theme => ({ + root: { + display: 'flex' + }, + card: { + display: 'flex', + width: '100%' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + }, + cardMedia: { + flex: 1, + width: 160, + height: 160, + justifyContent: 'flex-start' + }, + buttonRight: { + justifyContent: 'flex-end' + }, + aawebMedia: { + width: '100%', + height: 320 + }, + dashLink: { + float: 'right', + color: '#111', + fontSize: 16, + textDecoration: 'none', + paddingRight: '1em' + } +}) + +export class Overview extends Component { + render () { + const { classes } = this.props + return ( + + + + + ) + } +} + +Overview.propTypes = { + classes: PropTypes.object.isRequired +} + +export default withStyles(styles)(Overview) diff --git a/client/src/views/dashboard/TeamMembers.js b/client/src/views/dashboard/TeamMembers.js new file mode 100644 index 000000000..8b2b43aeb --- /dev/null +++ b/client/src/views/dashboard/TeamMembers.js @@ -0,0 +1,30 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Typography from '@material-ui/core/Typography' +import { withStyles } from '@material-ui/core/styles' + +import { MessageCard } from '../common/' + +const styles = theme => ({ + tableContainer: { + height: 320 + } +}) + +const TeamMembers = ({ classes }) => ( +
+ + Members + +
+ +
+
+) + +TeamMembers.propTypes = { + classes: PropTypes.object.isRequired +} + +export default withStyles(styles)(TeamMembers) diff --git a/client/src/views/dashboard/components/CreateTeam.js b/client/src/views/dashboard/components/CreateTeam.js index 2d0bda554..1dfb75850 100644 --- a/client/src/views/dashboard/components/CreateTeam.js +++ b/client/src/views/dashboard/components/CreateTeam.js @@ -1,135 +1,100 @@ -import React, { Component } from 'react' +import React from 'react' import PropTypes from 'prop-types' -import { withRouter } from 'react-router-dom' -import Button from '@material-ui/core/Button' -import AddIcon from '@material-ui/icons/Add' -import TextField from '@material-ui/core/TextField' -import Dialog from '@material-ui/core/Dialog' -import DialogActions from '@material-ui/core/DialogActions' -import DialogContent from '@material-ui/core/DialogContent' -import DialogContentText from '@material-ui/core/DialogContentText' -import DialogTitle from '@material-ui/core/DialogTitle' +import { Mutation } from 'react-apollo' + import Typography from '@material-ui/core/Typography' import { getItem } from '../../../utils/local-storage' +import { checkGraphQLError } from '../../../utils/graphql-errors' -export class CreateTeamDialog extends Component { - constructor (props) { - super(props) +import CREATE_TEAM from '../../../graphql/teams/CreateTeamMutation' +import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' - this.handleClickOpen = this.handleClickOpen.bind(this) - this.handleClose = this.handleClose.bind(this) - this.handlePost = this.handlePost.bind(this) - this.slugify = this.slugify.bind(this) - this.handleChange = this.handleChange.bind(this) +const CreateTeam = ({ authId }) => { + let input + console.log('CreateTeam', authId) - this.state = { - open: false, - name: '' - } - } - - handleClickOpen () { - this.setState({ open: true }) - } - - handleClose () { - this.setState({ open: false }) - } - - handleChange (e) { - this.setState({ name: e.target.value }) - } - - render () { - console.log(this.props) - return ( -
- - - Subscribe - - - Team Creation - - - Name your team! - - - - - - - - -
- ) - } - - slugify (string) { - const a = 'àáäâãåèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;' - const b = 'aaaaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------' - const p = new RegExp(a.split('').join('|'), 'g') - - return string.toString().toLowerCase() - .replace(/\s+/g, '-') // Replace spaces with - - .replace(p, c => b.charAt(a.indexOf(c))) // Replace special characters - .replace(/&/g, '-and-') // Replace & with 'and' - .replace(/[^\w-]+/g, '') // Remove all non-word characters - .replace(/--+/g, '-') // Replace multiple - with single - - .replace(/^-+/, '') // Trim - from start of text - .replace(/-+$/, '') // Trim - from end of text - } - - async handlePost (e) { - e.preventDefault() - const { name } = this.state - const slug = this.slugify(name) - const currentUser = await getItem('user') - let authId = JSON.parse(currentUser) - authId = authId.sub - console.log('createPage cu:', JSON.parse(currentUser)) - await this.props.createTeamMutation({ - variables: { - name, - slug, - owner: authId - } - }) - this.setState({ open: false }) - } + return ( + + {(createTeam, { loading, error, data }) => { + let errors + let loader + if (loading) { + loader = Submitting team... + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + const displayMessage = checkGraphQLError(message) + console.log('createTeam error:', displayMessage) + return ( + + {message} + + ) + }) + } + return ( +
+
{ + handleSubmit(e, createTeam, input) + }} + > + { + input = node + }} + /> + +
+ {loader} + {errors} +
+ ) + }} +
+ ) } -CreateTeamDialog.propTypes = { - classes: PropTypes.object.isRequired, - createTeamMutation: PropTypes.function +CreateTeam.propTypes = { + authId: PropTypes.any } -export default withRouter(CreateTeamDialog) +const handleSubmit = async (e, createTeam, input) => { + e.preventDefault() + const currentUser = await getItem('user') + let authId = JSON.parse(currentUser) + authId = authId.authId + console.log('createTeam submit: ', authId) + const name = input.value + const slug = slugify(name) + await createTeam({ variables: { name, slug, owner: authId } }) + input.value = '' +} + +const slugify = string => { + const a = 'àáäâãåèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;' + const b = 'aaaaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------' + const p = new RegExp(a.split('').join('|'), 'g') + + return string + .toString() + .toLowerCase() + .replace(/\s+/g, '-') // Replace spaces with - + .replace(p, c => b.charAt(a.indexOf(c))) // Replace special characters + .replace(/&/g, '-and-') // Replace & with 'and' + .replace(/[^\w-]+/g, '') // Remove all non-word characters + .replace(/--+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, '') // Trim - from end of text +} + +export default CreateTeam diff --git a/client/src/views/dashboard/components/CreateTeamDialog.js b/client/src/views/dashboard/components/CreateTeamDialog.js new file mode 100644 index 000000000..4db5b5638 --- /dev/null +++ b/client/src/views/dashboard/components/CreateTeamDialog.js @@ -0,0 +1,196 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Mutation } from 'react-apollo' +import { withStyles } from '@material-ui/core/styles' + +import Button from '@material-ui/core/Button' +import AddIcon from '@material-ui/icons/Add' +import TextField from '@material-ui/core/TextField' +import FormControl from '@material-ui/core/FormControl' +import FormHelperText from '@material-ui/core/FormHelperText' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogContentText from '@material-ui/core/DialogContentText' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' + +import { getItem } from '../../../utils/local-storage' +import { isEmpty } from '../../../utils/js-helpers' + +import CREATE_TEAM from '../../../graphql/teams/CreateTeamMutation' +import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' + +// import { checkGraphQLError } from '../../../utils/graphql-errors' + +const styles = theme => ({ + dialogStyle: { + padding: '3em' + }, + buttonRight: { + // position: 'absolute', + // right: '3em' + marginTop: '1em', + marginRight: 'auto', + marginLeft: 'auto', + display: 'block', + lineHeight: '1em' + } +}) + +export class CreateTeamDialog extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + this.validate = this.validate.bind(this) + this.slugify = this.slugify.bind(this) + + this.state = { + open: false, + name: '', + errors: { + name: '' + } + } + } + + render () { + const { classes, authId } = this.props + return ( + + {(createTeam, { loading, error, data }) => { + let errors + let loader = null + if (loading) { + loader = Submitting team... + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + // const displayMessage = checkGraphQLError(message) + return ( + + {message} + + ) + }) + } + return ( +
+ + + Create a Team + + Team Name: + (this.state.errors.name !== '' || error)}> + + {this.state.errors.name !== '' && ({this.state.errors.name})} + {error && ({errors})} + {loader} + + + + + + + +
+ ) + }} +
+ ) + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } + + handleChange (e) { + const errors = this.validate(e.target.value) + if (isEmpty(errors)) { + this.setState({ errors: { name: '' } }) + this.setState({ name: e.target.value }) + } else { + this.setState({ errors: errors }) + this.setState({ name: e.target.value }) + } + } + + async handleSubmit (e, createTeam, name) { + e.preventDefault() + const currentUser = await getItem('user') + let authId = JSON.parse(currentUser) + authId = authId.authId + console.log('createTeam submit: ', authId, name) + const slug = this.slugify(name) + await createTeam({ variables: { name, slug, owner: authId } }) + this.setState({ name: '' }) + } + + validate (data) { + const errors = {} + if (data === '') errors.name = 'Please enter a team name' + return errors + } + + slugify (string) { + const a = 'àáäâãåèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;' + const b = 'aaaaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------' + const p = new RegExp(a.split('').join('|'), 'g') + + return string + .toString() + .toLowerCase() + .replace(/\s+/g, '-') // Replace spaces with - + .replace(p, c => b.charAt(a.indexOf(c))) // Replace special characters + .replace(/&/g, '-and-') // Replace & with 'and' + .replace(/[^\w-]+/g, '') // Remove all non-word characters + .replace(/--+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, '') // Trim - from end of text + } +} + +CreateTeamDialog.propTypes = { + classes: PropTypes.object.isRequired, + authId: PropTypes.string +} + +export default withStyles(styles)(CreateTeamDialog) diff --git a/client/src/views/dashboard/components/CreateTeamForm.js b/client/src/views/dashboard/components/CreateTeamForm.js new file mode 100644 index 000000000..98e3aebc6 --- /dev/null +++ b/client/src/views/dashboard/components/CreateTeamForm.js @@ -0,0 +1,131 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { withRouter } from 'react-router-dom' +import Button from '@material-ui/core/Button' +import AddIcon from '@material-ui/icons/Add' +import TextField from '@material-ui/core/TextField' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogContentText from '@material-ui/core/DialogContentText' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' + +import { getItem } from '../../../utils/local-storage' + +export class CreateTeamDialog extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handlePost = this.handlePost.bind(this) + this.slugify = this.slugify.bind(this) + this.handleChange = this.handleChange.bind(this) + + this.state = { + open: false, + name: '' + } + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } + + handleChange (e) { + this.setState({ name: e.target.value }) + } + + render () { + console.log(this.props) + return ( +
+ + + Subscribe + + Team Creation + Name your team! + + + + + + + +
+ ) + } + + slugify (string) { + const a = 'àáäâãåèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;' + const b = 'aaaaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------' + const p = new RegExp(a.split('').join('|'), 'g') + + return string + .toString() + .toLowerCase() + .replace(/\s+/g, '-') // Replace spaces with - + .replace(p, c => b.charAt(a.indexOf(c))) // Replace special characters + .replace(/&/g, '-and-') // Replace & with 'and' + .replace(/[^\w-]+/g, '') // Remove all non-word characters + .replace(/--+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, '') // Trim - from end of text + } + + async handlePost (e) { + e.preventDefault() + const { name } = this.state + const slug = this.slugify(name) + const currentUser = await getItem('user') + let authId = JSON.parse(currentUser) + authId = authId.sub + console.log('createPage cu:', JSON.parse(currentUser)) + await this.props.createTeamMutation({ + variables: { + name, + slug, + owner: authId + } + }) + this.setState({ open: false }) + } +} + +CreateTeamDialog.propTypes = { + classes: PropTypes.object.isRequired, + createTeamMutation: PropTypes.function +} + +export default withRouter(CreateTeamDialog) diff --git a/client/src/views/dashboard/components/CreateTeamWrapper.js b/client/src/views/dashboard/components/CreateTeamWrapper.js new file mode 100644 index 000000000..68750bffc --- /dev/null +++ b/client/src/views/dashboard/components/CreateTeamWrapper.js @@ -0,0 +1,50 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' + +import Grid from '@material-ui/core/Grid' +import Card from '@material-ui/core/Card' +import CardContent from '@material-ui/core/CardContent' + +import CreateTeam from './CreateTeam' + +const styles = theme => ({ + card: { + display: 'flex', + width: '100%' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + }, + cardMedia: { + flex: 1, + width: 160, + height: 160, + justifyContent: 'flex-start' + } +}) + +const CreateTeamWrapper = ({ classes, authId }) => ( + + + +
+ + + +
+
+
+
+) + +CreateTeamWrapper.propTypes = { + classes: PropTypes.object.isRequired, + authId: PropTypes.string +} + +export default withStyles(styles)(CreateTeamWrapper) diff --git a/client/src/views/dashboard/components/DashEditor.js b/client/src/views/dashboard/components/DashEditor.js new file mode 100644 index 000000000..246a921f0 --- /dev/null +++ b/client/src/views/dashboard/components/DashEditor.js @@ -0,0 +1,42 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Typography from '@material-ui/core/Typography' + +import Grid from '@material-ui/core/Grid' +import Card from '@material-ui/core/Card' +import CardMedia from '@material-ui/core/CardMedia' +import CardActions from '@material-ui/core/CardActions' +import Button from '@material-ui/core/Button' + +import aaweb from '../../../assets/AlgonetWebPlatform.jpg' + +export const DashEditor = ({ classes }) => ( + + + Develop + + + + + + + + + + + + + + +) + +DashEditor.propTypes = { + classes: PropTypes.object.isRequired +} + +export default DashEditor diff --git a/client/src/views/dashboard/components/DashTeam.js b/client/src/views/dashboard/components/DashTeam.js new file mode 100644 index 000000000..1213f7eb1 --- /dev/null +++ b/client/src/views/dashboard/components/DashTeam.js @@ -0,0 +1,124 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { Query } from 'react-apollo' +import { withStateHandlers, lifecycle, compose } from 'recompose' +import { Link } from 'react-router-dom' + +import Typography from '@material-ui/core/Typography' +import Grid from '@material-ui/core/Grid' + +import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' + +import DashTeamItem from './DashTeamItem' +import CreateTeamDialog from './CreateTeamDialog' +import { MessageCard } from '../../common/' + +import { isDefined, isString } from '../../../utils/js-helpers' +import { getItem } from '../../../utils/local-storage' + +export const DashTeam = ({ classes, user = null }) => { + let owner + let loader + let authId = null + console.log('DashTeam: ', user) + if (user !== null && isString(user)) { + owner = JSON.parse(user) + if (isDefined(owner.authId)) authId = owner.authId + } else { + loader = Loading... + } + console.log('DashTeam 2: ', owner, authId) + + if (authId === undefined || authId === null) { + return ( + + + Teams + + Loading... + + ) + } + return ( + + + Teams Manage Teams + + + {({ loading, error, data }) => { + console.log('GET_TEAMS_BY_OWNER: ', loading, error, data) + let errors + let queryLoader + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + return ( + + {message} + + ) + }) + } + if (!loading && !error) { + if (data.teamsByOwner.length > 0) { + return ( + + {!loading && + data.teamsByOwner.map(team => ( + + ))} + {queryLoader} + {errors} + {loader} + + ) + } else { + return ( + + + + + + + + ) + } + } else { + return ( + + + Loading... + + {queryLoader} + {errors} + + ) + } + }} + + + ) +} + +DashTeam.propTypes = { + user: PropTypes.any, + classes: PropTypes.object.isRequired +} + +const getUserOnMount = lifecycle({ + componentDidMount () { + getItem('user').then(user => { + this.setState({ user }) + }) // Set user to state + } +}) + +const mapStateToProps = withStateHandlers(() => ({ user: null }), { + user: ({ user }) => () => ({ user }) +}) + +const DashTeamAuthId = compose( + mapStateToProps, + getUserOnMount +)(DashTeam) + +export default DashTeamAuthId diff --git a/client/src/views/dashboard/components/DashTeamItem.js b/client/src/views/dashboard/components/DashTeamItem.js new file mode 100644 index 000000000..b5210e73c --- /dev/null +++ b/client/src/views/dashboard/components/DashTeamItem.js @@ -0,0 +1,70 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Typography from '@material-ui/core/Typography' +import { withStyles } from '@material-ui/core/styles' + +import Grid from '@material-ui/core/Grid' +import Card from '@material-ui/core/Card' +import CardContent from '@material-ui/core/CardContent' +import CardMedia from '@material-ui/core/CardMedia' +import CardActions from '@material-ui/core/CardActions' +import Button from '@material-ui/core/Button' + +const styles = theme => ({ + card: { + display: 'flex', + width: '100%' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + }, + cardMedia: { + flex: 1, + width: 160, + height: 160, + justifyContent: 'flex-start' + }, + buttonRight: { + justifyContent: 'flex-end' + } +}) + +export const DashTeamItem = ({ classes, team }) => ( + + +
+ + + {team.name} + + {team.createdAt} + + + Members:6 + + + + + +
+
+
+) + +DashTeamItem.propTypes = { + classes: PropTypes.object.isRequired, + team: PropTypes.object.isRequired +} + +export default withStyles(styles)(DashTeamItem) diff --git a/client/src/views/dashboard/components/DrawerLinks.js b/client/src/views/dashboard/components/DrawerLinks.js index b2a178697..29321211e 100644 --- a/client/src/views/dashboard/components/DrawerLinks.js +++ b/client/src/views/dashboard/components/DrawerLinks.js @@ -13,9 +13,11 @@ import SentimentSatisfiedAltIcon from '@material-ui/icons/SentimentSatisfiedAlt' import AndroidIcon from '@material-ui/icons/Android' const DashboardLink = props => -const TeamsLink = props => -const MembersLink = props => +const TeamsLink = props => +const MembersLink = props => const SettingsLink = props => +const DocumentationLink = props => +const SupportLink = props => export const MainDrawerItems = (
@@ -35,7 +37,7 @@ export const MainDrawerItems = ( - + @@ -54,13 +56,13 @@ export const MainDrawerItems = ( export const SecondaryDrawerItems = (
- + - + diff --git a/client/src/views/dashboard/components/ManageTeamDelete.js b/client/src/views/dashboard/components/ManageTeamDelete.js new file mode 100644 index 000000000..4cb3e3204 --- /dev/null +++ b/client/src/views/dashboard/components/ManageTeamDelete.js @@ -0,0 +1,143 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' +import { Mutation } from 'react-apollo' + +import Button from '@material-ui/core/Button' +import DeleteIcon from '@material-ui/icons/Delete' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' + +import DELETE_TEAM from '../../../graphql/teams/DeleteTeamMutation' +import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' + +import { checkGraphQLError } from '../../../utils/graphql-errors' + +const styles = theme => ({ + dialogContainer: { + display: 'block', + margin: '3em', + minWidth: 400 + }, + buttonRight: { + justifyContent: 'flex-end' + } +}) + +export class ManageTeamDelete extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + + this.state = { + open: false, + motto: '', + description: '' + } + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } + + handleChange (e) { + this.setState({ name: e.target.value }) + } + + render () { + console.log(this.props, this.props.slug) + const { classes, authId } = this.props + return ( + { + const data = cache.readQuery({ query: GET_TEAMS_BY_OWNER }) + console.log('Mutation cache update: ', deleteTeam, data) + data.getTeamsByOwner.push(deleteTeam) + cache.writeQuery({ query: GET_TEAMS_BY_OWNER, data }) + }} + > + {(deleteTeam, { loading, error, data }) => { + let errors + let loader + if (loading) { + loader = Submitting team... + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + const displayMessage = checkGraphQLError(message) + console.log('createTeam error:', displayMessage) + return ( + + {message} + + ) + }) + } + return ( +
+ + +
+ Delete Team Team + + DANGER - Deleting your team cannot be undone + Are you sure you want to delete this team? + {loader} + {errors} + + + + + +
+
+
+ ) + }} +
+ ) + } + + async handleSubmit (e, deleteTeam, slug, authId) { + e.preventDefault() + await deleteTeam({ variables: { slug, owner: authId } }) + } +} + +ManageTeamDelete.propTypes = { + classes: PropTypes.object.isRequired, + slug: PropTypes.string.isRequired, + authId: PropTypes.string.isRequired +} + +export default withStyles(styles)(ManageTeamDelete) diff --git a/client/src/views/dashboard/components/ManageTeamDetails.js b/client/src/views/dashboard/components/ManageTeamDetails.js new file mode 100644 index 000000000..183db0ea8 --- /dev/null +++ b/client/src/views/dashboard/components/ManageTeamDetails.js @@ -0,0 +1,108 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { withStyles } from '@material-ui/core/styles' + +import Button from '@material-ui/core/Button' +import DetailsIcon from '@material-ui/icons/Details' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' + +const styles = theme => ({ + dialogContainer: { + display: 'block', + margin: '3em', + minWidth: 400 + }, + buttonRight: { + justifyContent: 'flex-end' + } +}) + +export class ManageTeamDetails extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + + this.state = { + open: false + } + } + + render () { + console.log(this.props.team) + const { name, members, profile, createdAt } = this.props.team + return ( +
+ + +
+ {name} Team Details + + + {name} + + + {createdAt} + + + Motto: {profile.motto} + + + Description: {profile.description} + + + Members: {members.length} + + + Team Admin: + { + members.map(member => { + if (member.role === 'OWNER' || member.role === 'ADMIN') { + return member.member.alias + } + }) + } + + + + + +
+
+
+ ) + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } +} + +ManageTeamDetails.propTypes = { + classes: PropTypes.object.isRequired, + team: PropTypes.any +} + +export default withStyles(styles)(ManageTeamDetails) diff --git a/client/src/views/dashboard/components/ManageTeamEdit.js b/client/src/views/dashboard/components/ManageTeamEdit.js new file mode 100644 index 000000000..7db8b46be --- /dev/null +++ b/client/src/views/dashboard/components/ManageTeamEdit.js @@ -0,0 +1,185 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Mutation } from 'react-apollo' +import { withStyles } from '@material-ui/core/styles' + +import Button from '@material-ui/core/Button' +import EditIcon from '@material-ui/icons/Edit' +import TextField from '@material-ui/core/TextField' +import Dialog from '@material-ui/core/Dialog' +import DialogActions from '@material-ui/core/DialogActions' +import DialogContent from '@material-ui/core/DialogContent' +import DialogContentText from '@material-ui/core/DialogContentText' +import DialogTitle from '@material-ui/core/DialogTitle' +import Typography from '@material-ui/core/Typography' + +import { getItem } from '../../../utils/local-storage' + +import UPDATE_TEAM_PROFILE from '../../../graphql/teams/UpdateTeamProfileMutation' +import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' + +import { checkGraphQLError } from '../../../utils/graphql-errors' + +const styles = theme => ({ + dialogContainer: { + display: 'block', + margin: '3em', + minWidth: 400 + }, + buttonRight: { + justifyContent: 'flex-end' + } +}) + +export class ManageTeamEdit extends Component { + constructor (props) { + super(props) + + this.handleClickOpen = this.handleClickOpen.bind(this) + this.handleClose = this.handleClose.bind(this) + this.handleChange = this.handleChange.bind(this) + this.handleSubmit = this.handleSubmit.bind(this) + console.log('ManageTeamEdit', props.team) + const motto = props.team.profile.motto || '' + const description = props.team.profile.description || '' + this.state = { + open: false, + motto: motto, + description: description + } + } + + render () { + console.log(this.props, this.props.slug) + const { classes, team, authId } = this.props + return ( + + {(updateTeamProfile, { loading, error, data }) => { + let errors + let loader + if (loading) { + loader = Submitting team... + } + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + const displayMessage = checkGraphQLError(message) + console.log('updateTeamProfile error:', displayMessage) + return ( + + {message} + + ) + }) + } + return ( +
+ + +
+ Edit Team Details + + + + Team description: + + {loader} + {errors} + + + + + +
+
+
+ ) + }} +
+ ) + } + + handleClickOpen () { + this.setState({ open: true }) + } + + handleClose () { + this.setState({ open: false }) + } + + handleChange (e) { + switch (e.target.id) { + case 'motto': + this.setState({ motto: e.target.value }) + break + case 'description': + this.setState({ description: e.target.value }) + break + default: + } + } + + async handleSubmit (e, updateTeamProfile, slug) { + e.preventDefault() + const currentUser = await getItem('user') + let authId = JSON.parse(currentUser) + authId = authId.authId + await updateTeamProfile({ variables: { slug, owner: authId, description: this.state.description, motto: this.state.motto } }) + } +} + +ManageTeamEdit.propTypes = { + classes: PropTypes.object.isRequired, + slug: PropTypes.string.isRequired, + authId: PropTypes.string, + team: PropTypes.object +} + +export default withStyles(styles)(ManageTeamEdit) diff --git a/client/src/views/dashboard/components/ManageTeamsItem.js b/client/src/views/dashboard/components/ManageTeamsItem.js new file mode 100644 index 000000000..378dde27e --- /dev/null +++ b/client/src/views/dashboard/components/ManageTeamsItem.js @@ -0,0 +1,48 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Grid from '@material-ui/core/Grid' +import Card from '@material-ui/core/Card' +import CardActions from '@material-ui/core/CardActions' +import CardContent from '@material-ui/core/CardContent' +import CardMedia from '@material-ui/core/CardMedia' +import Typography from '@material-ui/core/Typography' + +import ManageTeamDetails from './ManageTeamDetails' +import ManageTeamDelete from './ManageTeamDelete' +import ManageTeamEdit from './ManageTeamEdit' + +export const ManageTeamsItem = ({ classes, team, authId }) => ( + + +
+ + + + {team.name} + + + {team.createdAt} | Members: {team.members.length} + + + + + + + +
+
+
+) + +ManageTeamsItem.propTypes = { + classes: PropTypes.object.isRequired, + team: PropTypes.object.isRequired, + authId: PropTypes.string.isRequired +} + +export default ManageTeamsItem diff --git a/client/src/views/dashboard/components/ManageTeamsList.js b/client/src/views/dashboard/components/ManageTeamsList.js new file mode 100644 index 000000000..5a5349e86 --- /dev/null +++ b/client/src/views/dashboard/components/ManageTeamsList.js @@ -0,0 +1,151 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { Query } from 'react-apollo' +import { withStateHandlers, lifecycle, compose } from 'recompose' + +import Grid from '@material-ui/core/Grid' +import { withStyles } from '@material-ui/core/styles' + +import { isDefined, isString } from '../../../utils/js-helpers' +import { getItem } from '../../../utils/local-storage' + +import GET_TEAMS_BY_OWNER from '../../../graphql/teams/GetTeamsByOwnerQuery' + +import ManageTeamsItem from './ManageTeamsItem' +import CreateTeamDialog from './CreateTeamDialog' +import { MessageCard } from '../../common/' + +const styles = theme => ({ + heroContent: { + maxWidth: 600, + margin: '0 auto', + padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + }, + cardGrid: { + padding: `${theme.spacing.unit * 8}px 0` + }, + buttonRight: { + justifyContent: 'flex-end' + }, + card: { + display: 'flex', + width: '100%' + }, + cardDetails: { + display: 'flex', + flex: 1 + }, + cardContent: { + flex: 1 + }, + cardMedia: { + flex: 1, + width: 100, + height: 100, + maxWidth: 100, + justifyContent: 'flex-start' + } +}) + +export const ManageTeamsList = ({ classes, user = null }) => { + let owner + let authId = null + console.log('ManageTeamsList: ', user) + if (user !== null && isString(user)) { + owner = JSON.parse(user) + if (isDefined(owner.authId)) authId = owner.authId + } + console.log('ManageTeamsList 2: ', owner, authId) + if (authId === undefined || authId === null) { + return ( + + + + + + ) + } else { + return ( + + {({ loading, error, data }) => { + console.log('GET_TEAMS_BY_OWNER: ', loading, error, data) + + let errors = null + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + return ( + + ) + }) + } + if (!loading && !error) { + if (data.teamsByOwner.length > 0) { + return ( + + + {!loading && + data.teamsByOwner.map(team => ( + + ))} + + + + {errors} + + + ) + } else { + return ( + + + + + + + + ) + } + } else { + return ( + + + + if (errors !== null) () + + + ) + } + }} + + ) + } +} + +ManageTeamsList.propTypes = { + user: PropTypes.any, + classes: PropTypes.object.isRequired +} + +const getUserOnMount = lifecycle({ + componentDidMount () { + getItem('user').then(user => { + this.setState({ user }) + }) // Set user to state + } +}) + +const mapStateToProps = withStateHandlers(() => ({ user: null }), { + user: ({ user }) => () => ({ user }) +}) + +const ManageTeamsListAuthId = compose( + mapStateToProps, + getUserOnMount +)(ManageTeamsList) + +export default withStyles(styles)(ManageTeamsListAuthId) diff --git a/client/src/views/dashboard/index.js b/client/src/views/dashboard/index.js index 6392347ed..f62ac733a 100644 --- a/client/src/views/dashboard/index.js +++ b/client/src/views/dashboard/index.js @@ -1,25 +1,19 @@ import React from 'react' import PropTypes from 'prop-types' -import { graphql } from 'react-apollo' +import { Route, Switch } from 'react-router-dom' +import { lifecycle } from 'recompose' -import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' import Drawer from '@material-ui/core/Drawer' import List from '@material-ui/core/List' import Divider from '@material-ui/core/Divider' -import Grid from '@material-ui/core/Grid' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import CardMedia from '@material-ui/core/CardMedia' -import CardActions from '@material-ui/core/CardActions' -import Button from '@material-ui/core/Button' import { MainDrawerItems, SecondaryDrawerItems } from './components/DrawerLinks' -import { CreateTeamDialog } from './components/CreateTeam' -import { CreateTeamMutation } from '../../graphql/teams/CreateTeamMutation' - -import aaweb from '../../assets/AlgonetWebPlatform.jpg' +import Overview from './Overview' +import ManageTeams from './ManageTeams' +import TeamMembers from './TeamMembers' +import Settings from './Settings' const drawerWidth = 240 @@ -49,40 +43,10 @@ const styles = theme => ({ overflow: 'auto', width: `calc(90% - ${drawerWidth}px)` }, - appBarSpacer: theme.mixins.toolbar, - tableContainer: { - height: 320 - }, - card: { - display: 'flex', - width: '100%' - }, - cardDetails: { - display: 'flex', - flex: 1 - }, - cardContent: { - flex: 1 - }, - cardMedia: { - flex: 1, - width: 160, - height: 160, - justifyContent: 'flex-start' - }, - aawebMedia: { - width: '100%', - height: 320 - }, - buttonRight: { - justifyContent: 'flex-end' - }, - extendedIcon: { - marginRight: theme.spacing.unit - } + appBarSpacer: theme.mixins.toolbar }) -const Dashboard = ({ classes, createTeamMutation, ...props }) => ( +const Dashboard = ({ classes }) => (
(
- - - - Teams - - - - -
- - - -
-
-
- - -
- - - Goodester - - 09.15.2018 - - - Members:6 - - - Now recruiting! - - - - - -
-
-
- - -
- - - Goodester - - 09.15.2018 - - - Members:6 - - - Now recruiting! - - - - - -
-
-
-
-
- - - Develop - - - - - - - - - - - - - - -
+ + + + + +
) Dashboard.propTypes = { - classes: PropTypes.object.isRequired, - createTeamMutation: PropTypes.function + classes: PropTypes.object.isRequired } -const CreateTeamDialogWithMutation = graphql(CreateTeamMutation, { - name: 'createTeamMutation' // name of the injected prop: this.props.createTeamMutation... +const DashboardCheckSession = lifecycle({ + async componentDidMount () { + try { + const authenticated = await this.props.auth.isAuthenticated() + console.log('DashboardCheckSession res: ', authenticated) + } catch (err) { + console.log('DashboardCheckSession err: ', err) + } + } })(Dashboard) -export default withStyles(styles)(CreateTeamDialogWithMutation) +export default withStyles(styles)(DashboardCheckSession) diff --git a/client/src/views/dashboard/members.js b/client/src/views/dashboard/members.js deleted file mode 100644 index da5524ed0..000000000 --- a/client/src/views/dashboard/members.js +++ /dev/null @@ -1,111 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { graphql } from 'react-apollo' - -import Typography from '@material-ui/core/Typography' -import { withStyles } from '@material-ui/core/styles' -import Drawer from '@material-ui/core/Drawer' -import List from '@material-ui/core/List' -import Divider from '@material-ui/core/Divider' - -import { MainDrawerItems, SecondaryDrawerItems } from './components/DrawerLinks' -import { TeamTable } from './components/TeamTable' - -import { CreateTeamMutation } from '../../graphql/teams/CreateTeamMutation' - -const drawerWidth = 240 - -const styles = theme => ({ - root: { - display: 'flex' - }, - '@global': { - body: { - backgroundColor: theme.palette.common.white - } - }, - appBar: { - zIndex: theme.zIndex.drawer + 1 - }, - drawerPaper: { - position: 'relative', - width: drawerWidth, - whiteSpace: 'nowrap', - zIndex: 1000 - }, - content: { - flexGrow: 1, - backgroundColor: theme.palette.background.default, - padding: theme.spacing.unit * 3, - height: '100vh', - overflow: 'auto', - width: `calc(90% - ${drawerWidth}px)` - }, - appBarSpacer: theme.mixins.toolbar, - tableContainer: { - height: 320 - }, - card: { - display: 'flex', - width: '100%' - }, - cardDetails: { - display: 'flex', - flex: 1 - }, - cardContent: { - flex: 1 - }, - cardMedia: { - flex: 1, - width: 160, - height: 160, - justifyContent: 'flex-start' - }, - aawebMedia: { - width: '100%', - height: 320 - }, - buttonRight: { - justifyContent: 'flex-end' - }, - extendedIcon: { - marginRight: theme.spacing.unit - } -}) - -const Dashboard = ({ classes, createTeamMutation, ...props }) => ( -
- -
- {MainDrawerItems} - - {SecondaryDrawerItems} - -
-
- - Members - -
- -
-
-
-) - -Dashboard.propTypes = { - classes: PropTypes.object.isRequired, - createTeamMutation: PropTypes.function -} - -const CreateTeamDialogWithMutation = graphql(CreateTeamMutation, { - name: 'createTeamMutation' // name of the injected prop: this.props.createTeamMutation... -})(Dashboard) - -export default withStyles(styles)(CreateTeamDialogWithMutation) diff --git a/client/src/views/dashboard/settings.js b/client/src/views/dashboard/settings.js index 0fac8c47e..d28df4d79 100644 --- a/client/src/views/dashboard/settings.js +++ b/client/src/views/dashboard/settings.js @@ -1,210 +1,30 @@ import React from 'react' import PropTypes from 'prop-types' -import { graphql } from 'react-apollo' import Typography from '@material-ui/core/Typography' import { withStyles } from '@material-ui/core/styles' -import Drawer from '@material-ui/core/Drawer' -import List from '@material-ui/core/List' -import Divider from '@material-ui/core/Divider' -import Grid from '@material-ui/core/Grid' -import Card from '@material-ui/core/Card' -import CardContent from '@material-ui/core/CardContent' -import CardMedia from '@material-ui/core/CardMedia' -import CardActions from '@material-ui/core/CardActions' -import Button from '@material-ui/core/Button' -import { MainDrawerItems, SecondaryDrawerItems } from './components/DrawerLinks' -import { TeamTable } from './components/TeamTable' -import { CreateTeamDialog } from './components/CreateTeam' - -import { CreateTeamMutation } from '../../graphql/teams/CreateTeamMutation' - -const drawerWidth = 240 +import { MessageCard } from '../common/' const styles = theme => ({ - root: { - display: 'flex' - }, - '@global': { - body: { - backgroundColor: theme.palette.common.white - } - }, - appBar: { - zIndex: theme.zIndex.drawer + 1 - }, - drawerPaper: { - position: 'relative', - width: drawerWidth, - whiteSpace: 'nowrap', - zIndex: 1000 - }, - content: { - flexGrow: 1, - backgroundColor: theme.palette.background.default, - padding: theme.spacing.unit * 3, - height: '100vh', - overflow: 'auto', - width: `calc(90% - ${drawerWidth}px)` - }, - appBarSpacer: theme.mixins.toolbar, tableContainer: { height: 320 - }, - card: { - display: 'flex', - width: '100%' - }, - cardDetails: { - display: 'flex', - flex: 1 - }, - cardContent: { - flex: 1 - }, - cardMedia: { - flex: 1, - width: 160, - height: 160, - justifyContent: 'flex-start' - }, - aawebMedia: { - width: '100%', - height: 320 - }, - buttonRight: { - justifyContent: 'flex-end' - }, - extendedIcon: { - marginRight: theme.spacing.unit } }) -const Dashboard = ({ classes, createTeamMutation, ...props }) => ( -
- -
- {MainDrawerItems} - - {SecondaryDrawerItems} - -
-
- - - - Settings - - - - -
- - - -
-
-
- - -
- - - Goodester - - 09.15.2018 - - - Members:6 - - - Now recruiting! - - - - - -
-
-
- - -
- - - Goodester - - 09.15.2018 - - - Members:6 - - - Now recruiting! - - - - - -
-
-
-
-
- - - Notifications - - - - - - - - - - - - - -
-
+export const Settings = ({ classes }) => ( +
+ + Settings + +
+ +
) -Dashboard.propTypes = { - classes: PropTypes.object.isRequired, - createTeamMutation: PropTypes.function +Settings.propTypes = { + classes: PropTypes.object.isRequired } -const CreateTeamDialogWithMutation = graphql(CreateTeamMutation, { - name: 'createTeamMutation' // name of the injected prop: this.props.createTeamMutation... -})(Dashboard) - -export default withStyles(styles)(CreateTeamDialogWithMutation) +export default withStyles(styles)(Settings) diff --git a/client/src/views/dashboard/teams.js b/client/src/views/dashboard/teams.js deleted file mode 100644 index 9d223e372..000000000 --- a/client/src/views/dashboard/teams.js +++ /dev/null @@ -1,111 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { graphql } from 'react-apollo' - -import Typography from '@material-ui/core/Typography' -import { withStyles } from '@material-ui/core/styles' -import Drawer from '@material-ui/core/Drawer' -import List from '@material-ui/core/List' -import Divider from '@material-ui/core/Divider' - -import { MainDrawerItems, SecondaryDrawerItems } from './components/DrawerLinks' -import { TeamTable } from './components/TeamTable' - -import { CreateTeamMutation } from '../../graphql/teams/CreateTeamMutation' - -const drawerWidth = 240 - -const styles = theme => ({ - root: { - display: 'flex' - }, - '@global': { - body: { - backgroundColor: theme.palette.common.white - } - }, - appBar: { - zIndex: theme.zIndex.drawer + 1 - }, - drawerPaper: { - position: 'relative', - width: drawerWidth, - whiteSpace: 'nowrap', - zIndex: 1000 - }, - content: { - flexGrow: 1, - backgroundColor: theme.palette.background.default, - padding: theme.spacing.unit * 3, - height: '100vh', - overflow: 'auto', - width: `calc(90% - ${drawerWidth}px)` - }, - appBarSpacer: theme.mixins.toolbar, - tableContainer: { - height: 320 - }, - card: { - display: 'flex', - width: '100%' - }, - cardDetails: { - display: 'flex', - flex: 1 - }, - cardContent: { - flex: 1 - }, - cardMedia: { - flex: 1, - width: 160, - height: 160, - justifyContent: 'flex-start' - }, - aawebMedia: { - width: '100%', - height: 320 - }, - buttonRight: { - justifyContent: 'flex-end' - }, - extendedIcon: { - marginRight: theme.spacing.unit - } -}) - -const Dashboard = ({ classes, createTeamMutation, ...props }) => ( -
- -
- {MainDrawerItems} - - {SecondaryDrawerItems} - -
-
- - Teams - -
- -
-
-
-) - -Dashboard.propTypes = { - classes: PropTypes.object.isRequired, - createTeamMutation: PropTypes.function -} - -const CreateTeamDialogWithMutation = graphql(CreateTeamMutation, { - name: 'createTeamMutation' // name of the injected prop: this.props.createTeamMutation... -})(Dashboard) - -export default withStyles(styles)(CreateTeamDialogWithMutation) diff --git a/client/src/views/footer/index.js b/client/src/views/footer/index.js index a7abff6b7..ecbd0c594 100644 --- a/client/src/views/footer/index.js +++ b/client/src/views/footer/index.js @@ -55,26 +55,28 @@ const Footer = ({ classes }) => { Home - + Teams - + About - - Support - - +
+ + Support + + + Documentation - + Telegram diff --git a/client/src/views/index.js b/client/src/views/index.js index a1e5c1f8a..8ff455e85 100644 --- a/client/src/views/index.js +++ b/client/src/views/index.js @@ -2,8 +2,10 @@ import NavBar from './navbar' import Footer from './footer' import Callback from './callback' import Home from './home' +import About from './about' import Dashboard from './dashboard' +import Teams from './teams' import { theme, globalStyles } from './theme' -export { NavBar, Footer, Callback, Home, Dashboard, theme, globalStyles } +export { NavBar, Footer, Callback, Home, About, Dashboard, Teams, theme, globalStyles } diff --git a/client/src/views/navbar/index.js b/client/src/views/navbar/index.js index 34fd52f5f..ca33893d2 100644 --- a/client/src/views/navbar/index.js +++ b/client/src/views/navbar/index.js @@ -20,6 +20,8 @@ import { getItem } from '../../utils/local-storage' import { LoggedIn } from './LoggedIn' import { LoggedOut } from './LoggedOut' +import AALogo from '../../assets/logos/advanced-algos/aa-logo-dark.svg' + const AboutLink = props => const TeamsLink = props => const DashboardLink = props => @@ -34,6 +36,9 @@ const styles = theme => ({ root: { flexGrow: 1 }, + colorDefault: { + color: '#000' + }, flex: { flexGrow: 1 }, @@ -46,6 +51,12 @@ const styles = theme => ({ }, appBar: { position: 'relative' + }, + img: { + margin: 20, + display: 'block', + maxWidth: 240, + maxHeight: 48 } }) @@ -71,8 +82,9 @@ class NavBar extends Component { return (
- + + Advanced Algos - - + + {this.state.user !== undefined && this.state.user !== null ? ( diff --git a/client/src/views/teams/components/TeamsDetails.js b/client/src/views/teams/components/TeamsDetails.js new file mode 100644 index 000000000..5c590a54b --- /dev/null +++ b/client/src/views/teams/components/TeamsDetails.js @@ -0,0 +1,89 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import CssBaseline from '@material-ui/core/CssBaseline' +import Typography from '@material-ui/core/Typography' +import Paper from '@material-ui/core/Paper' +import { withStyles } from '@material-ui/core/styles' + +import { Link } from 'react-router-dom' + +const styles = theme => ({ + root: { + flexGrow: 1 + }, + '@global': { + body: { + backgroundColor: theme.palette.common.white + } + }, + layout: { + width: 'auto', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + marginBottom: theme.spacing.unit * 6, + [theme.breakpoints.up(900 + theme.spacing.unit * 3 * 2)]: { + width: 900, + marginLeft: 'auto', + marginRight: 'auto' + } + }, + paper: { + marginTop: theme.spacing.unit * 3, + marginBottom: theme.spacing.unit * 3, + padding: theme.spacing.unit * 2, + [theme.breakpoints.up(600 + theme.spacing.unit * 3 * 2)]: { + marginTop: theme.spacing.unit * 6, + marginBottom: theme.spacing.unit * 6, + padding: theme.spacing.unit * 3 + } + }, + heroContent: { + maxWidth: 600, + margin: '0 auto', + padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + } +}) + +const TeamsDetails = ({ classes, team }) => ( + + +
+ +
+ ← Back to all teams + + {team.name} + + + {team.createdAt} + + + Members: {team.members.length} + + + Team Admin: + { + team.members.map(member => { + if (member.role === 'OWNER' || member.role === 'ADMIN') { + return member.member.alias + } + }) + } + +
+
+
+
+) +TeamsDetails.propTypes = { + classes: PropTypes.object.isRequired, + team: PropTypes.object.isRequired +} + +export default withStyles(styles)(TeamsDetails) diff --git a/client/src/views/teams/components/TeamsItem.js b/client/src/views/teams/components/TeamsItem.js new file mode 100644 index 000000000..6b2e20bb1 --- /dev/null +++ b/client/src/views/teams/components/TeamsItem.js @@ -0,0 +1,55 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Grid from '@material-ui/core/Grid' +import Card from '@material-ui/core/Card' +import CardActionArea from '@material-ui/core/CardActionArea' +import CardActions from '@material-ui/core/CardActions' +import CardContent from '@material-ui/core/CardContent' +import CardMedia from '@material-ui/core/CardMedia' +import Button from '@material-ui/core/Button' +import Typography from '@material-ui/core/Typography' + +import { Link } from 'react-router-dom' + +export const TeamsItem = ({ classes, team }) => ( + + + + + + + {team.name} + + + {team.createdAt} + + + Members: 1 + + + Now recruiting! + + + + + + + + + + +) + +TeamsItem.propTypes = { + classes: PropTypes.object.isRequired, + team: PropTypes.object.isRequired +} + +export default TeamsItem diff --git a/client/src/views/teams/components/TeamsList.js b/client/src/views/teams/components/TeamsList.js new file mode 100644 index 000000000..64493cdff --- /dev/null +++ b/client/src/views/teams/components/TeamsList.js @@ -0,0 +1,128 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { Query } from 'react-apollo' + +import Typography from '@material-ui/core/Typography' +import Grid from '@material-ui/core/Grid' +import { withStyles } from '@material-ui/core/styles' + +import GET_ALL_TEAMS_QUERY from '../../../graphql/teams/GetAllTeamsQuery' + +import { isEmpty } from '../../../utils/js-helpers' + +import TeamsItem from './TeamsItem' +import TeamsDetails from './TeamsDetails' + +const styles = theme => ({ + heroContent: { + maxWidth: 600, + margin: '0 auto', + padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px` + }, + cardGrid: { + padding: `${theme.spacing.unit * 8}px 0` + }, + card: { + height: '100%', + display: 'flex', + flexDirection: 'column' + }, + cardMedia: { + paddingTop: '56.25%' // 16:9 + }, + cardContent: { + flexGrow: 1 + }, + buttonRight: { + justifyContent: 'flex-end' + } +}) + +export const TeamsList = ({ classes, match }) => ( + + {({ loading, error, data }) => { + console.log('GET_ALL_TEAMS: ', loading, error, data, match) + let slug = null + if (!isEmpty(match.params) && match.params.slug) slug = match.params.slug + + let errors + let queryLoader + if (error) { + errors = error.graphQLErrors.map(({ message }, i) => { + return ( + + {message} + + ) + }) + } + if (!loading && !error) { + if (data.teams.edges.length > 0) { + if (slug !== null) { + return ( + + {!loading && + data.teams.edges.map(team => { + if (team.node.slug === slug) { + return ( + + ) + } + })} + {queryLoader} + {errors} + + ) + } else { + return ( + +
+ + Teams + +
+ + {!loading && + data.teams.edges.map(team => ( + + ))} + {queryLoader} + {errors} + +
+ ) + } + } + } else { + return ( + + + Loading... + + {queryLoader} + {errors} + + ) + } + }} +
+) + +TeamsList.propTypes = { + classes: PropTypes.object.isRequired, + match: PropTypes.object +} + +export default withStyles(styles)(TeamsList) diff --git a/client/src/views/teams/index.js b/client/src/views/teams/index.js new file mode 100644 index 000000000..3c2087d83 --- /dev/null +++ b/client/src/views/teams/index.js @@ -0,0 +1,45 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import CssBaseline from '@material-ui/core/CssBaseline' +import { withStyles } from '@material-ui/core/styles' + +import TeamsList from './components/TeamsList' + +const styles = theme => ({ + root: { + flexGrow: 1 + }, + '@global': { + body: { + backgroundColor: theme.palette.common.white + } + }, + layout: { + width: 'auto', + marginLeft: theme.spacing.unit * 3, + marginRight: theme.spacing.unit * 3, + marginBottom: theme.spacing.unit * 6, + [theme.breakpoints.up(900 + theme.spacing.unit * 3 * 2)]: { + width: 900, + marginLeft: 'auto', + marginRight: 'auto' + } + } +}) + +const Teams = ({ classes, match }) => ( + + +
+ +
+
+) + +Teams.propTypes = { + classes: PropTypes.object.isRequired, + match: PropTypes.object +} + +export default withStyles(styles)(Teams) diff --git a/client/src/views/theme.js b/client/src/views/theme.js index 4a2e00ee8..26043dfc0 100644 --- a/client/src/views/theme.js +++ b/client/src/views/theme.js @@ -1,10 +1,10 @@ import { createMuiTheme } from '@material-ui/core/styles' import indigo from '@material-ui/core/colors/indigo' -import red from '@material-ui/core/colors/red' +// import red from '@material-ui/core/colors/red' export const theme = createMuiTheme({ palette: { - primary: red, + primary: { main: '#303036' }, secondary: indigo }, typography: { diff --git a/docs/CodeStyle.md b/docs/CodeStyle.md new file mode 100644 index 000000000..483afda50 --- /dev/null +++ b/docs/CodeStyle.md @@ -0,0 +1,5 @@ +# Code Styling and Formatting + +``` +npm install --save-dev eslint-config-standard eslint-config-standard-react eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node eslint-plugin-react +```