From 63ab1cd0eae7b1d05de494f3edd8a4295f02898a Mon Sep 17 00:00:00 2001 From: Hammad Jutt Date: Fri, 16 Oct 2020 11:04:22 -0600 Subject: [PATCH] Setup Player Skills and complete setup flow (#161) --- hasura/metadata/tables.yaml | 6 +-- packages/codegen/schema.graphql | 6 --- .../web/components/Player/PlayerFeatures.tsx | 10 +++-- packages/web/components/Player/PlayerHero.tsx | 5 +++ .../components/Setup/SetupAvailability.tsx | 40 ++++++++++++++++++- packages/web/graphql/fragments.ts | 18 +++++++++ packages/web/graphql/getSkills.ts | 15 ++++--- .../web/graphql/mutations/updateSkills.ts | 31 ++++++++++++++ packages/web/pages/player/[username].tsx | 30 +++++++++++--- packages/web/utils/skillHelpers.ts | 8 ++-- 10 files changed, 142 insertions(+), 27 deletions(-) create mode 100644 packages/web/graphql/mutations/updateSkills.ts diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 29ff1f10..7837e835 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -167,6 +167,7 @@ - role: player permission: columns: + - availability_hours - enneagram - playerTypeId - role @@ -176,6 +177,8 @@ id: _eq: X-Hasura-User-Id check: {} + set: + id: x-hasura-User-Id event_triggers: - name: fetchBoxVerifiedAccounts definition: @@ -218,9 +221,6 @@ schema: public name: Player_Skill object_relationships: - - name: Player - using: - foreign_key_constraint_on: player_id - name: Skill using: foreign_key_constraint_on: skill_id diff --git a/packages/codegen/schema.graphql b/packages/codegen/schema.graphql index 0246e2de..fc118083 100644 --- a/packages/codegen/schema.graphql +++ b/packages/codegen/schema.graphql @@ -2503,9 +2503,6 @@ input Player_set_input { columns and relationships of "Player_Skill" """ type Player_Skill { - """An object relationship""" - Player: Player! - """An object relationship""" Skill: Skill! player_id: uuid! @@ -2550,7 +2547,6 @@ input Player_Skill_arr_rel_insert_input { Boolean expression to filter rows from the table "Player_Skill". All fields are combined with a logical 'AND'. """ input Player_Skill_bool_exp { - Player: Player_bool_exp Skill: Skill_bool_exp _and: [Player_Skill_bool_exp] _not: Player_Skill_bool_exp @@ -2571,7 +2567,6 @@ enum Player_Skill_constraint { input type for inserting data into table "Player_Skill" """ input Player_Skill_insert_input { - Player: Player_obj_rel_insert_input Skill: Skill_obj_rel_insert_input player_id: uuid skill_id: uuid @@ -2637,7 +2632,6 @@ input Player_Skill_on_conflict { ordering options when selecting data from "Player_Skill" """ input Player_Skill_order_by { - Player: Player_order_by Skill: Skill_order_by player_id: order_by skill_id: order_by diff --git a/packages/web/components/Player/PlayerFeatures.tsx b/packages/web/components/Player/PlayerFeatures.tsx index 0a351396..9205fe72 100644 --- a/packages/web/components/Player/PlayerFeatures.tsx +++ b/packages/web/components/Player/PlayerFeatures.tsx @@ -35,11 +35,15 @@ export const PlayerFeatures: React.FC = ({ player }) => { /> )} - - + {/* */} + {/* */} diff --git a/packages/web/components/Player/PlayerHero.tsx b/packages/web/components/Player/PlayerHero.tsx index 5af3b9ab..230d02e1 100644 --- a/packages/web/components/Player/PlayerHero.tsx +++ b/packages/web/components/Player/PlayerHero.tsx @@ -27,6 +27,11 @@ export const PlayerHero: React.FC = ({ player }) => { {getPlayerName(player)} + {player.playerType?.title ? ( + + {player.playerType?.title.toUpperCase()} + + ) : null} diff --git a/packages/web/components/Setup/SetupAvailability.tsx b/packages/web/components/Setup/SetupAvailability.tsx index e9bd9dd8..d4a7698d 100644 --- a/packages/web/components/Setup/SetupAvailability.tsx +++ b/packages/web/components/Setup/SetupAvailability.tsx @@ -6,24 +6,56 @@ import { MetaButton, MetaHeading, Text, + useToast, } from '@metafam/ds'; import { FlexContainer } from 'components/Container'; import { useSetupFlow } from 'contexts/SetupContext'; import React, { useEffect, useState } from 'react'; +import { useUpdatePlayerSkillsMutation } from '../../graphql/autogen/types'; +import { useUser } from '../../lib/hooks'; + export const SetupAvailability: React.FC = () => { const { onNextPress, nextButtonLabel, availability, setAvailability, + skills, } = useSetupFlow(); const [invalid, setInvalid] = useState(false); + const { user } = useUser({ redirectTo: '/' }); + const toast = useToast(); + useEffect(() => { const value = Number(availability); setInvalid(value < 0 || value > 168); }, [availability]); + const [updateSkillsRes, updateSkills] = useUpdatePlayerSkillsMutation(); + + const handleNextPress = async () => { + if (!user) return; + + const { error } = await updateSkills({ + availability_hours: Number(availability), + skills: skills.map((s) => ({ skill_id: s.id })), + }); + + if (error) { + console.warn(error); + toast({ + title: 'Error', + description: 'Unable to update Player Skills. The octo is sad 😢', + status: 'error', + isClosable: true, + }); + return; + } + + onNextPress(); + }; + return ( @@ -51,7 +83,13 @@ export const SetupAvailability: React.FC = () => { hr/week - + {nextButtonLabel} diff --git a/packages/web/graphql/fragments.ts b/packages/web/graphql/fragments.ts index 5de22822..5da128ba 100644 --- a/packages/web/graphql/fragments.ts +++ b/packages/web/graphql/fragments.ts @@ -7,6 +7,24 @@ export const PlayerFragment = gql` totalXp rank ethereum_address + availability_hours + EnneagramType { + description + name + } + playerType { + description + id + imageUrl + title + } + Player_Skills { + Skill { + category + id + name + } + } Accounts(where: { type: { _in: [TWITTER, GITHUB] } }) { identifier type diff --git a/packages/web/graphql/getSkills.ts b/packages/web/graphql/getSkills.ts index e4d9ecff..58da9a87 100644 --- a/packages/web/graphql/getSkills.ts +++ b/packages/web/graphql/getSkills.ts @@ -1,21 +1,24 @@ import gql from 'fake-tag'; -import { GetSkillsQuery } from 'graphql/autogen/types'; +import { GetSkillsQuery, PlayerSkillFragment } from 'graphql/autogen/types'; import { client } from 'graphql/client'; -import { Skill } from 'graphql/types'; const skillsQuery = gql` query GetSkills { Skill( order_by: { Player_Skills_aggregate: { count: desc }, category: asc } ) { - id - name - category + ...PlayerSkill } } + + fragment PlayerSkill on Skill { + id + name + category + } `; -export const getSkills = async (): Promise => { +export const getSkills = async (): Promise => { const { data, error } = await client .query(skillsQuery) .toPromise(); diff --git a/packages/web/graphql/mutations/updateSkills.ts b/packages/web/graphql/mutations/updateSkills.ts new file mode 100644 index 00000000..bcfcd05f --- /dev/null +++ b/packages/web/graphql/mutations/updateSkills.ts @@ -0,0 +1,31 @@ +import gql from 'fake-tag'; + +export const UpdateSkillsMutation = gql` + mutation UpdatePlayerSkills( + $availability_hours: Int! + $skills: [Player_Skill_insert_input!]! + ) { + delete_Player_Skill(where: {}) { + affected_rows + } + insert_Player_Skill(objects: $skills) { + affected_rows + } + update_Player( + _set: { availability_hours: $availability_hours } + where: {} + ) { + returning { + id + availability_hours + Player_Skills { + Skill { + id + category + name + } + } + } + } + } +`; diff --git a/packages/web/pages/player/[username].tsx b/packages/web/pages/player/[username].tsx index be7bc492..be5eac6a 100644 --- a/packages/web/pages/player/[username].tsx +++ b/packages/web/pages/player/[username].tsx @@ -2,9 +2,9 @@ import { Container, MetaBox, MetaTag, + MetaTheme, P, SimpleGrid, - Text, Wrap, } from '@metafam/ds'; import { PlayerFeatures } from 'components/Player/PlayerFeatures'; @@ -20,8 +20,19 @@ import Error from 'next/error'; import React from 'react'; import { getPlayerDescription } from 'utils/playerHelpers'; +import { SkillCategory_Enum } from '../../graphql/autogen/types'; + type Props = InferGetStaticPropsType; +const SkillColors: Record = { + [SkillCategory_Enum.Community]: MetaTheme.colors.green['700'], + [SkillCategory_Enum.Design]: MetaTheme.colors.pink['700'], + [SkillCategory_Enum.Dev]: MetaTheme.colors.cyan['700'], + [SkillCategory_Enum.Engineering]: MetaTheme.colors.blue['700'], + [SkillCategory_Enum.Technologies]: MetaTheme.colors.gray['600'], + [SkillCategory_Enum.Strategy]: MetaTheme.colors.yellow['700'], +}; + const PlayerPage: React.FC = ({ player }) => { if (!player) { return ; @@ -37,9 +48,18 @@ const PlayerPage: React.FC = ({ player }) => {

{getPlayerDescription(player)}

- - Unavailable - + + {player.Player_Skills.map(({ Skill }) => ( + + {Skill.name} + + ))} + @@ -67,7 +87,7 @@ export const getStaticPaths: GetStaticPaths = async () => { paths: players.map(({ username }) => ({ params: { username }, })), - fallback: false, + fallback: true, }; }; diff --git a/packages/web/utils/skillHelpers.ts b/packages/web/utils/skillHelpers.ts index 2117aaf9..de53786e 100644 --- a/packages/web/utils/skillHelpers.ts +++ b/packages/web/utils/skillHelpers.ts @@ -1,10 +1,10 @@ -import { Skill } from 'graphql/types'; +import { PlayerSkillFragment } from '../graphql/autogen/types'; export type SkillMap = { [category: string]: CategoryOption; }; -export type SkillOption = Skill & { +export type SkillOption = PlayerSkillFragment & { value: string; label: string; }; @@ -14,7 +14,9 @@ export type CategoryOption = { options: Array; }; -export const parseSkills = (skills: Array): Array => { +export const parseSkills = ( + skills: Array, +): Array => { const skillsMap: SkillMap = {}; skills.map((skill) => { if (!(skill.category in skillsMap)) {