Completed quests widget (#1085)

* quest-roles-demo init

feat: backend changes

* fix: more lint

* fix: even more lint

* fix: add field to update quest

* chore: consolidate migrations

* chore: rename

* chore: update schema

* quest-roles-demo init

* feat: add role selector to create quest form

* fix: after rebase

* fix: bug when clicking the only selected value in filter

* feat: save roles to backend

* feat: display roles on /quest/id card

* feat: add megamenu demo link

* feat: add additional roles: Bridgebuilding, Rainmaking, Videomaking

* feat: support for editing roles on quests

* chore: revert changes that allowed me to bypass creating quests

* fix: hide quests demo app link

* fix: alec's corrections

* chore: roles_id -> roleIds

* quest-roles-demo init

* feat: save roles to backend

* feat: add additional roles: Bridgebuilding, Rainmaking, Videomaking

* feat: support for editing roles on quests

* chore: revert changes that allowed me to bypass creating quests

* feat: completed quests section

* feat: display completed quest name + link

* fix: rebasing changes

submitted_at -> submittedAt, etc...

* style: add proof link and style

* style: all quests content and subheader

* style: all quests modal footer

* chore: remove mock data

* fix: schema

* fix: schema

* chore: delete console.log

* chore: dysbulic's suggestions

* fix: final fixes

Co-authored-by: dan13ram <dan13ram@gmail.com>
This commit is contained in:
vidvidvid
2022-02-03 10:02:39 +01:00
committed by Scott Stevenson
parent 514de8fe8e
commit c684a22ce4
25 changed files with 318 additions and 116 deletions

View File

@@ -823,6 +823,14 @@
completed_by_player_id: completedByPlayerId
submitted_at: submittedAt
object_relationships:
- name: completed
using:
manual_configuration:
remote_table:
schema: public
name: quest
column_mapping:
quest_id: id
- name: player
using:
foreign_key_constraint_on: completed_by_player_id
@@ -871,6 +879,10 @@
- table:
schema: public
name: quest_role
configuration:
custom_root_fields: {}
custom_column_names:
quest_id: questId
object_relationships:
- name: PlayerRole
using:

View File

@@ -1,4 +1,4 @@
INSERT INTO "public"."PlayerRole" (role, label, description, basic)
VALUES ('BRIDGEBUILDING', 'Bridgebuilding', 'All about fostering connections and enabling the flow of people & resources between different organizations.', false),
('VIDEOMAKING', 'Videomaking', 'As the name suggest - it''s about production of videos no matter the kind; educational, entertaining or both.', false),
('VIDEOMAKING', 'Videomaking', 'As the name suggests - it''s about production of videos no matter the kind: educational, entertaining, or both.', false),
('RAINMAKING', 'Rainmaking', 'Making sure value is recognized & project tokens get bought or liquidity added by long term holders.', false);

View File

@@ -42,10 +42,10 @@ export async function createQuest(
repetition: questRepetition,
createdByPlayerId: playerId,
quest_roles: {
data: roleIds.map((r, i) => ({ role: r, rank: i + 1 })),
data: roleIds.map((role, i) => ({ role, rank: i + 1 })),
},
quest_skills: {
data: skillIds.map((s) => ({ skillId: s })),
data: skillIds.map((skillId) => ({ skillId })),
},
};

View File

@@ -68,7 +68,6 @@ export const Leaderboard: React.FC = () => {
</Text>
}
styles={metaFilterSelectStyles}
tagLabel=""
hasValue={sortOption.value !== SortOption.SEASON_XP}
value={[
{ label: sortOption.label ?? '', value: sortOption.value ?? '' },

View File

@@ -66,13 +66,13 @@ export const DesktopFilters: React.FC<Props> = ({
return (
<Wrap
transition="all 0.25s"
py="6"
py={6}
style={{ backdropFilter: 'blur(7px)' }}
ref={filterRef}
position="sticky"
top="-1px"
borderTop="1px solid transparent"
zIndex="1"
zIndex={1}
justifyContent="center"
w={isSticky ? 'calc(100% + 6rem)' : '100%'}
maxW={isSticky ? 'auto' : '79rem'}

View File

@@ -0,0 +1,100 @@
import { Box, Button, ExternalLinkIcon, Link, Stack, Text } from '@metafam/ds';
import { ProfileSection } from 'components/Profile/ProfileSection';
import {
PlayerFragmentFragment,
QuestCompletionFragmentFragment,
QuestCompletionStatus_Enum,
} from 'graphql/autogen/types';
import { getAcceptedQuestsByPlayerQuery } from 'graphql/getQuests';
import React, { useEffect, useState } from 'react';
import { BoxType } from 'utils/boxTypes';
// TODO Fake data
type Props = {
player: PlayerFragmentFragment;
isOwnProfile?: boolean;
canEdit?: boolean;
};
export const PlayerCompletedQuests: React.FC<Props> = ({
player,
isOwnProfile,
canEdit,
}) => {
const [quests, setQuests] = useState<Array<QuestCompletionFragmentFragment>>(
[],
);
useEffect(() => {
const loadQuests = async () => {
const response = await getAcceptedQuestsByPlayerQuery(player?.id);
if (response.length) {
setQuests(
response.filter(
(quest) => quest.status === QuestCompletionStatus_Enum.Accepted,
),
);
}
};
loadQuests();
}, [player?.id]);
return (
<ProfileSection
title="Completed Quests"
{...{ isOwnProfile, canEdit }}
boxType={BoxType.PLAYER_ACHIEVEMENTS}
modalTitle={`Completed Quests (${quests.length})`}
modalText="Show All"
modal={<AllQuests quests={quests} />}
subheader='A quest is considered "complete" when it is accepted by the
quest owner.'
>
{quests.length ? (
<Stack>
<QuestList quests={quests.slice(0, 4)} />
</Stack>
) : (
<Text>No completed quests yet</Text>
)}
</ProfileSection>
);
};
interface QuestProps {
quests: Array<QuestCompletionFragmentFragment>;
mb?: number;
}
const QuestList: React.FC<QuestProps> = ({ quests, mb = 2 }) => (
<>
{quests.map((quest) => (
<Box mb={mb}>
<Link href={`/quest/${quest.questId}`} color="white">
<Text fontSize="xl">{quest.completed?.title}</Text>
</Link>
{quest.submissionLink && (
<Link href={quest.submissionLink} isExternal>
<Button
mt={2}
variant="ghost"
h={8}
p="xs"
color="#D59BD5"
backgroundColor={'rgba(255, 255, 255, 0.04);'}
_hover={{ bg: '#FFFFFF11' }}
_active={{ bg: '#FF000011' }}
rightIcon={<ExternalLinkIcon boxSize={3} />}
>
<Text fontSize="xs">See proof of delivery</Text>
</Button>
</Link>
)}
</Box>
))}
</>
);
const AllQuests: React.FC<QuestProps> = ({ quests }) => (
<QuestList {...{ quests }} mb={6} />
);

View File

@@ -12,6 +12,7 @@ export const ALL_BOXES = [
BoxType.PLAYER_DAO_MEMBERSHIPS,
BoxType.PLAYER_ROLES,
BoxType.EMBEDDED_URL,
BoxType.PLAYER_COMPLETED_QUESTS,
// BoxType.PLAYER_ACHIEVEMENTS,
// TODO: Add more types of sections
];
@@ -25,6 +26,7 @@ export const DEFAULT_BOXES = [
BoxType.PLAYER_TYPE,
BoxType.PLAYER_NFT_GALLERY,
BoxType.PLAYER_DAO_MEMBERSHIPS,
BoxType.PLAYER_COMPLETED_QUESTS,
];
export type LayoutMetadata = {

View File

@@ -1,6 +1,7 @@
import { Flex, IconButton } from '@metafam/ds';
import { PlayerAchievements } from 'components/Player/Section/PlayerAchievements';
import { PlayerColorDisposition } from 'components/Player/Section/PlayerColorDisposition';
import { PlayerCompletedQuests } from 'components/Player/Section/PlayerCompletedQuests';
import { PlayerGallery } from 'components/Player/Section/PlayerGallery';
import { PlayerHero } from 'components/Player/Section/PlayerHero';
import { PlayerMemberships } from 'components/Player/Section/PlayerMemberships';
@@ -53,6 +54,8 @@ const PlayerSectionInner: React.FC<Props> = ({
return <PlayerRoles {...{ player, isOwnProfile, canEdit }} />;
case BoxType.PLAYER_ACHIEVEMENTS:
return <PlayerAchievements {...{ player, isOwnProfile, canEdit }} />;
case BoxType.PLAYER_COMPLETED_QUESTS:
return <PlayerCompletedQuests {...{ player, isOwnProfile, canEdit }} />;
case BoxType.EMBEDDED_URL: {
const url = boxMetadata?.url as string;
return url ? <EmbeddedUrl {...{ url, canEdit }} /> : <></>;

View File

@@ -1,5 +1,6 @@
import {
Box,
Button,
EditIcon,
Flex,
HStack,
@@ -8,11 +9,13 @@ import {
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
useDisclosure,
} from '@metafam/ds';
import { Maybe } from '@metafam/utils';
import BackgroundImage from 'assets/main-background.jpg';
import { SetupPersonalityType } from 'components/Setup/SetupPersonalityType';
import { SetupPlayerType } from 'components/Setup/SetupPlayerType';
@@ -27,6 +30,10 @@ export type ProfileSectionProps = {
boxType?: BoxType;
title?: string;
withoutBG?: boolean;
modalText?: string;
modalTitle?: string;
modal?: React.ReactNode;
subheader?: string;
};
export const ProfileSection: React.FC<ProfileSectionProps> = ({
@@ -36,6 +43,10 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
boxType,
title,
withoutBG = false,
modalText,
modal,
modalTitle,
subheader,
}) => {
const { isOpen, onOpen, onClose } = useDisclosure();
@@ -60,7 +71,7 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
>
{title}
</Text>
{isOwnProfile && !canEdit && boxType && isBoxDataEditable(boxType) && (
{!modal && isOwnProfile && !canEdit && isBoxDataEditable(boxType) && (
<IconButton
aria-label="Edit Profile Info"
size="lg"
@@ -80,6 +91,24 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
isRound
/>
)}
{modal && modalText && (
<Button
color="pinkShadeOne"
background="transparent"
_hover={{ color: 'white' }}
onClick={onOpen}
_focus={{
boxShadow: 'none',
backgroundColor: 'transparent',
}}
_active={{
transform: 'scale(0.8)',
backgroundColor: 'transparent',
}}
>
{modalText}
</Button>
)}
</HStack>
</Box>
)}
@@ -88,6 +117,7 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
borderBottomRadius="lg"
borderTopRadius={!title ? 'lg' : 0}
p={boxType === BoxType.EMBEDDED_URL ? 0 : 8}
boxShadow="md"
css={{ backdropFilter: 'blur(8px)' }}
w="100%"
pos="relative"
@@ -100,6 +130,7 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
<ModalOverlay />
<ModalContent
maxW="80%"
maxH="80%"
backgroundImage={`url(${BackgroundImage})`}
bgSize="cover"
bgAttachment="fixed"
@@ -110,8 +141,22 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
fontSize="4xl"
alignSelf="center"
fontWeight="normal"
textAlign="center"
>
{title}
{modalTitle || title}
{subheader && (
<Text
fontStyle="italic"
color="gray.400"
textAlign="center"
fontSize="md"
mt={3}
mb={10}
>
{subheader}
</Text>
)}
</ModalHeader>
<ModalCloseButton
color="pinkShadeOne"
@@ -119,9 +164,27 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
p={4}
_focus={{ boxShadow: 'none' }}
/>
<ModalBody>
<EditSectionBox {...{ boxType, onClose }} />
<ModalBody overflowY="scroll">
{!modal && !modalText && (
<EditSectionBox {...{ boxType, onClose }} />
)}
{modalText && modal}
</ModalBody>
{/* we should figure out how to unify modal footers (edit sections have their own,
look into EditSectionBox components - they have footers with 'save' and 'cancel' buttons) */}
{modalText && modal && (
<ModalFooter mt={6} justifyContent="center">
<Button
variant="ghost"
onClick={onClose}
color="magenta"
_hover={{ bg: '#FFFFFF11' }}
_active={{ bg: '#FF000011' }}
>
Go Back to Profile
</Button>
</ModalFooter>
)}
</ModalContent>
</Modal>
)}
@@ -129,7 +192,8 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
);
};
const isBoxDataEditable = (boxType: BoxType) =>
const isBoxDataEditable = (boxType?: Maybe<BoxType>) =>
!!boxType &&
[
BoxType.PLAYER_TYPE,
BoxType.PLAYER_COLOR_DISPOSITION,

View File

@@ -115,7 +115,7 @@ export const QuestDetails: React.FC<Props> = ({ quest }) => {
SKILLS
</Text>
<SkillsTags
skills={quest.quest_skills.map((s) => s.skill) as Skill[]}
skills={quest.quest_skills.map(({ skill }) => skill) as Skill[]}
maxSkills={4}
/>
</Box>
@@ -124,7 +124,9 @@ export const QuestDetails: React.FC<Props> = ({ quest }) => {
ROLES
</Text>
<RolesTags
roles={quest.quest_roles.map((s) => s.PlayerRole) as PlayerRole[]}
roles={
quest.quest_roles.map(({ PlayerRole: r }) => r) as PlayerRole[]
}
/>
</Box>
</VStack>

View File

@@ -28,7 +28,7 @@ type Props = {
roleChoices: Array<PlayerRole>;
};
type ValueType = { value: string; label: string };
type FilterString = { value: string; label: string };
/* TODO
- text search
@@ -44,20 +44,7 @@ export const QuestFilter: React.FC<Props> = ({
const { user } = useUser();
const myId = user?.id;
const limitOptions = [
{
label: '10',
value: '10',
},
{
label: '20',
value: '20',
},
{
label: '50',
value: '50',
},
];
const limitOptions = ['10', '20', '50'].map((n) => ({ label: n, value: n }));
const orderOptions = [
{
@@ -98,22 +85,22 @@ export const QuestFilter: React.FC<Props> = ({
value: role,
}));
const [limit, setLimit] = useState<ValueType>(limitOptions[0]);
const [order, setOrder] = useState<ValueType>(orderOptions[0]);
const [status, setStatus] = useState<ValueType>(statusOptions[0]);
const [guild, setGuild] = useState<ValueType>(guildOptions[0]);
const [roles, setRoles] = useState<ValueType[]>([]);
const [limit, setLimit] = useState<FilterString>(limitOptions[0]);
const [order, setOrder] = useState<FilterString>(orderOptions[0]);
const [status, setStatus] = useState<FilterString>(statusOptions[0]);
const [guild, setGuild] = useState<FilterString>(guildOptions[0]);
const [roles, setRoles] = useState<FilterString[]>([]);
return (
<Wrap justifyContent="center">
<Wrap
transition="all 0.25s"
py="6"
py={6}
style={{ backdropFilter: 'blur(7px)' }}
position="sticky"
top="-1px"
borderTop="1px solid transparent"
zIndex="1"
zIndex={1}
justifyContent="center"
w="100%"
maxW="79rem"
@@ -123,13 +110,12 @@ export const QuestFilter: React.FC<Props> = ({
>
<MetaFilterSelectSearch
title={`Limit: ${limit.label}`}
tagLabel=""
styles={metaFilterSelectStyles}
hasValue={false}
value={limit}
onChange={(value) => {
const values = value as ValueType[];
const v = values[values.length - 1];
const values = value as FilterString[];
const [v] = values.slice(-1);
if (v) {
setLimit(v);
setQueryVariable('limit', Number(v.value));
@@ -140,13 +126,12 @@ export const QuestFilter: React.FC<Props> = ({
/>
<MetaFilterSelectSearch
title={`Order: ${order.label}`}
tagLabel=""
styles={metaFilterSelectStyles}
hasValue={false}
value={order}
onChange={(value) => {
const values = value as ValueType[];
const o = values[values.length - 1];
const values = value as FilterString[];
const [o] = values.slice(-1);
if (o) {
setOrder(o);
setQueryVariable('order', o.value);
@@ -157,13 +142,12 @@ export const QuestFilter: React.FC<Props> = ({
/>
<MetaFilterSelectSearch
title={`Status: ${status.label}`}
tagLabel=""
styles={metaFilterSelectStyles}
hasValue={false}
value={status}
onChange={(value) => {
const values = value as ValueType[];
const s = values[values.length - 1];
const values = value as FilterString[];
const [s] = values.slice(-1);
if (s) {
setStatus(s);
setQueryVariable('status', s.value);
@@ -175,16 +159,15 @@ export const QuestFilter: React.FC<Props> = ({
{aggregates.guilds.length && (
<MetaFilterSelectSearch
title={`Guild: ${guild.label}`}
tagLabel=""
styles={metaFilterSelectStyles}
hasValue={false}
value={guild}
onChange={(value) => {
const values = value as ValueType[];
const g = values[values.length - 1];
const values = value as FilterString[];
const [g] = values.slice(-1);
if (g) {
setGuild(g);
setQueryVariable('guild_id', g.value);
setQueryVariable('guildId', g.value);
}
}}
options={guildOptions}
@@ -194,23 +177,22 @@ export const QuestFilter: React.FC<Props> = ({
{roleChoices.length && (
<MetaFilterSelectSearch
title="Roles"
tagLabel={roles.length > 0 ? roles.length.toString() : ''}
styles={metaFilterSelectStyles}
hasValue={false}
value={roles}
onChange={(value) => {
const values = value as ValueType[];
const values = value as FilterString[];
const selectedRoles = values;
if (selectedRoles.length) {
setRoles(selectedRoles);
setQueryVariable(
'quest_roles',
'questRoles',
selectedRoles.map((x) => x.value),
);
} else {
setRoles([]);
setQueryVariable('quest_roles', '');
setQueryVariable('questRoles', '');
}
}}
options={roleOptions}
@@ -236,7 +218,7 @@ export const QuestFilter: React.FC<Props> = ({
)
}
>
<Text mr={2}>Created by me</Text>
<Text mr={2}>Created By Me</Text>
<Switch
isChecked={myId && queryVariables.createdByPlayerId === myId}
/>

View File

@@ -20,9 +20,9 @@ import {
import { useRouter } from 'next/router';
import React, { useMemo, useState } from 'react';
import { Controller, FieldError, useForm } from 'react-hook-form';
import { RoleOption } from 'utils/roleHelpers';
import { QuestRepetitionHint, URIRegexp } from '../../utils/questHelpers';
import { RoleOption } from '../../utils/roleHelpers';
import { CategoryOption, SkillOption } from '../../utils/skillHelpers';
import { FlexContainer } from '../Container';
import { RolesSelect } from '../Roles';
@@ -93,9 +93,9 @@ const getDefaultFormValues = (
roles: base
? base.quest_roles
.map((s) => s.PlayerRole)
.map((s) => ({
value: s.role,
label: s.label,
.map(({ role, label }) => ({
label,
value: role,
}))
: [],
});
@@ -149,7 +149,6 @@ export const QuestForm: React.FC<Props> = ({
loadingLabel,
editQuest,
}) => {
console.log('roleChoices', roleChoices);
const defaultValues = useMemo(() => getDefaultFormValues(editQuest, guilds), [
editQuest,
guilds,
@@ -343,11 +342,11 @@ export const QuestForm: React.FC<Props> = ({
<FlexContainer w="100%" align="stretch" maxW="50rem">
<Controller
name="roles"
control={control}
{...{ control }}
defaultValue={[]}
render={({ field: { onChange, value } }) => (
<RolesSelect
roleChoices={roleChoices}
{...{ roleChoices }}
roles={value}
setRoles={onChange}
placeHolder="Select required roles"

View File

@@ -81,7 +81,7 @@ export const QuestTile: React.FC<Props> = ({ quest }) => (
SKILLS
</Text>
<SkillsTags
skills={quest.quest_skills.map((s) => s.skill) as Skill[]}
skills={quest.quest_skills.map(({ skill }) => skill) as Skill[]}
maxSkills={4}
/>
</Box>
@@ -91,7 +91,9 @@ export const QuestTile: React.FC<Props> = ({ quest }) => (
</Text>
{quest.quest_roles.length ? (
<RolesTags
roles={quest.quest_roles.map((s) => s.PlayerRole) as PlayerRole[]}
roles={
quest.quest_roles.map(({ PlayerRole: r }) => r) as PlayerRole[]
}
/>
) : (
<Text>/</Text>

View File

@@ -39,11 +39,11 @@ interface RolesProps {
}
export const RolesTags: React.FC<RolesProps> = ({ roles }) => (
<Wrap>
{roles.map((role) => (
<WrapItem key={role.role}>
<Tooltip label={role.description}>
{roles.map(({ role, label, description }) => (
<WrapItem key={role}>
<Tooltip label={description}>
<MetaTag size="md" fontWeight="normal" backgroundColor="purpleTag">
{role.label}
{label}
</MetaTag>
</Tooltip>
</WrapItem>

View File

@@ -40,7 +40,9 @@ export const SetupRoles: React.FC<SetupRolesProps> = ({
const toast = useToast();
const computeAvailableRoles = (playerRoles: string[]) =>
roleChoices.filter((r) => !playerRoles.includes(r.role) && r.basic);
roleChoices.filter(
({ role, basic }) => !playerRoles.includes(role) && basic,
);
const [updateRolesResult, updateRoles] = useUpdatePlayerRolesMutation();
const [loading, setLoading] = useState(false);

View File

@@ -182,6 +182,10 @@ export const QuestCompletionFragment = gql`
submissionLink
submissionText
submittedAt
questId
completed {
title
}
}
`;

View File

@@ -2,6 +2,9 @@ import gql from 'fake-tag';
import { Client } from 'urql';
import {
GetAcceptedQuestsByPlayerDocument,
GetAcceptedQuestsByPlayerQuery,
GetAcceptedQuestsByPlayerQueryVariables,
GetQuestIdsDocument,
GetQuestIdsQuery,
GetQuestIdsQueryVariables,
@@ -10,6 +13,7 @@ import {
GetQuestsQueryVariables,
Order_By,
QuestStatus_Enum,
Scalars,
} from './autogen/types';
import { client as defaultClient } from './client';
import { QuestFragment } from './fragments';
@@ -31,7 +35,7 @@ gql`
$guildId: uuid
$order: order_by
$createdByPlayerId: uuid
$quest_roles: [String!]
$questRoles: [String!]
) {
quest(
limit: $limit
@@ -40,7 +44,7 @@ gql`
status: { _eq: $status }
guildId: { _eq: $guildId }
createdByPlayerId: { _eq: $createdByPlayerId }
quest_roles: { role: { _in: $quest_roles } }
quest_roles: { role: { _in: $questRoles } }
}
) {
...QuestFragment
@@ -50,6 +54,21 @@ gql`
${QuestFragment}
`;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
/* GraphQL */ `
query GetAcceptedQuestsByPlayer(
$completedByPlayerId: uuid
$order: order_by
) {
quest_completion(
order_by: { submittedAt: $order }
where: { completedByPlayerId: { _eq: $completedByPlayerId } }
) {
...QuestCompletionFragment
}
}
`;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
gql`
query GetQuestGuilds {
@@ -64,17 +83,6 @@ gql`
}
`;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
gql`
query GetQuestsByRoles($roles: [String!]) {
quest(where: { quest_roles: { role: { _in: $roles } } }) {
title
id
description
}
}
`;
export const defaultQueryVariables: GetQuestsQueryVariables = {
limit: 10,
status: QuestStatus_Enum.Open,
@@ -116,3 +124,29 @@ export const getQuests = async (
return data.quest;
};
export const getAcceptedQuestsByPlayerQuery = async (
playerId: Scalars['uuid'],
order: Order_By = Order_By.Desc,
client: Client = defaultClient,
) => {
const { data, error } = await client
.query<
GetAcceptedQuestsByPlayerQuery,
GetAcceptedQuestsByPlayerQueryVariables
>(GetAcceptedQuestsByPlayerDocument, {
order,
completedByPlayerId: playerId,
})
.toPromise();
if (!data) {
if (error) {
throw error;
}
return [];
}
return data.quest_completion;
};

View File

@@ -20,23 +20,13 @@ export const UpdateQuestMutation = gql`
skillId
}
}
delete_quest_role(where: { quest_id: { _eq: $id } }) {
delete_quest_role(where: { questId: { _eq: $id } }) {
affected_rows
}
insert_quest_role(objects: $roles) {
affected_rows
returning {
quest_id
role
}
}
delete_quest_role(where: { quest_id: { _eq: $id } }) {
affected_rows
}
insert_quest_role(objects: $roles) {
affected_rows
returning {
quest_id
questId
role
}
}

View File

@@ -11,7 +11,7 @@ export const getStaticProps = async () => {
return {
props: {
roleChoices: roleChoices.filter((r) => r.basic),
roleChoices: roleChoices.filter(({ basic }) => basic),
hideTopMenu: true,
},
};
@@ -38,7 +38,7 @@ const PlayerRolesSetup: React.FC<Props> = (props) => {
<SetupContextProvider>
<SetupProfile>
<SetupRoles
roleChoices={roleChoices}
{...{ roleChoices }}
roles={roles}
fetchingExistingRoles={loadingRoles}
setRoles={setRoles}

View File

@@ -56,9 +56,9 @@ const EditQuestPage: React.FC<Props> = ({
skillId: s.id,
}));
const rolesObjects = data.roles.map((s) => ({
quest_id: quest.id,
role: s.value,
const rolesObjects = data.roles.map(({ value }) => ({
questId: quest.id,
role: value,
}));
updateQuest({
@@ -103,7 +103,7 @@ const EditQuestPage: React.FC<Props> = ({
<QuestForm
guilds={guilds}
skillChoices={skillChoices}
roleChoices={roleChoices}
{...{ roleChoices }}
onSubmit={onSubmit}
success={!!updateQuestResult.data}
fetching={updateQuestResult.fetching}

View File

@@ -42,8 +42,8 @@ const CreateQuestPage: React.FC<Props> = ({
...createQuestInputs,
repetition: (data.repetition as unknown) as QuestRepetition_ActionEnum,
cooldown: transformCooldownForBackend(cooldown, repetition),
skillIds: skills.map((s) => s.id),
roleIds: roles.map((r) => r.value),
skillIds: skills.map(({ id }) => id),
roleIds: roles.map(({ value }) => value),
};
createQuest({

View File

@@ -98,7 +98,7 @@ const QuestsPage: React.FC<Props> = ({ roleChoices }) => {
queryVariables={queryVariables}
setQueryVariable={setQueryVariable}
quests={quests || []}
roleChoices={roleChoices}
{...{ roleChoices }}
/>
</Box>
<Box mt={8} w="100%">

View File

@@ -10,6 +10,7 @@ export enum BoxType {
PLAYER_TYPE = 'player-type',
PLAYER_COLOR_DISPOSITION = 'player-color-disposition',
PLAYER_ROLES = 'player-roles',
PLAYER_COMPLETED_QUESTS = 'player-completed-quests',
PLAYER_ADD_BOX = 'player-add-box',
// Guild Profile Boxes
GUILD_SKILLS = 'guild-skills',

View File

@@ -2947,7 +2947,7 @@ type mutation_root {
"""
delete single row from the table: "quest_role"
"""
delete_quest_role_by_pk(quest_id: uuid!, role: String!): quest_role
delete_quest_role_by_pk(questId: uuid!, role: String!): quest_role
"""
delete data from the table: "quest_skill"
@@ -7662,7 +7662,7 @@ type query_root {
): quest_role_aggregate!
"""fetch data from the table: "quest_role" using primary key columns"""
quest_role_by_pk(quest_id: uuid!, role: String!): quest_role
quest_role_by_pk(questId: uuid!, role: String!): quest_role
"""
fetch data from the table: "quest_skill"
@@ -7980,6 +7980,9 @@ columns and relationships of "quest_completion"
type quest_completion {
"""An object relationship"""
QuestCompletionStatus: QuestCompletionStatus!
"""An object relationship"""
completed: quest
completedByPlayerId: uuid!
id: uuid!
@@ -8037,6 +8040,7 @@ input quest_completion_bool_exp {
_and: [quest_completion_bool_exp]
_not: quest_completion_bool_exp
_or: [quest_completion_bool_exp]
completed: quest_bool_exp
completedByPlayerId: uuid_comparison_exp
id: uuid_comparison_exp
player: player_bool_exp
@@ -8061,6 +8065,7 @@ input type for inserting data into table "quest_completion"
"""
input quest_completion_insert_input {
QuestCompletionStatus: QuestCompletionStatus_obj_rel_insert_input
completed: quest_obj_rel_insert_input
completedByPlayerId: uuid
id: uuid
player: player_obj_rel_insert_input
@@ -8149,6 +8154,7 @@ ordering options when selecting data from "quest_completion"
"""
input quest_completion_order_by {
QuestCompletionStatus: QuestCompletionStatus_order_by
completed: quest_order_by
completedByPlayerId: order_by
id: order_by
player: player_order_by
@@ -8389,7 +8395,7 @@ type quest_role {
"""An object relationship"""
quest: quest!
quest_id: uuid!
questId: uuid!
rank: Int
role: String!
}
@@ -8465,7 +8471,7 @@ input quest_role_bool_exp {
_not: quest_role_bool_exp
_or: [quest_role_bool_exp]
quest: quest_bool_exp
quest_id: uuid_comparison_exp
questId: uuid_comparison_exp
rank: Int_comparison_exp
role: String_comparison_exp
}
@@ -8491,14 +8497,14 @@ input type for inserting data into table "quest_role"
input quest_role_insert_input {
PlayerRole: PlayerRole_obj_rel_insert_input
quest: quest_obj_rel_insert_input
quest_id: uuid
questId: uuid
rank: Int
role: String
}
"""aggregate max on columns"""
type quest_role_max_fields {
quest_id: uuid
questId: uuid
rank: Int
role: String
}
@@ -8507,14 +8513,14 @@ type quest_role_max_fields {
order by max() on columns of table "quest_role"
"""
input quest_role_max_order_by {
quest_id: order_by
questId: order_by
rank: order_by
role: order_by
}
"""aggregate min on columns"""
type quest_role_min_fields {
quest_id: uuid
questId: uuid
rank: Int
role: String
}
@@ -8523,7 +8529,7 @@ type quest_role_min_fields {
order by min() on columns of table "quest_role"
"""
input quest_role_min_order_by {
quest_id: order_by
questId: order_by
rank: order_by
role: order_by
}
@@ -8562,7 +8568,7 @@ ordering options when selecting data from "quest_role"
input quest_role_order_by {
PlayerRole: PlayerRole_order_by
quest: quest_order_by
quest_id: order_by
questId: order_by
rank: order_by
role: order_by
}
@@ -8571,7 +8577,7 @@ input quest_role_order_by {
primary key columns input for table: "quest_role"
"""
input quest_role_pk_columns_input {
quest_id: uuid!
questId: uuid!
role: String!
}
@@ -8580,7 +8586,7 @@ select columns of table "quest_role"
"""
enum quest_role_select_column {
"""column name"""
quest_id
questId
"""column name"""
rank
@@ -8593,7 +8599,7 @@ enum quest_role_select_column {
input type for updating data in table "quest_role"
"""
input quest_role_set_input {
quest_id: uuid
questId: uuid
rank: Int
role: String
}
@@ -8651,7 +8657,7 @@ update columns of table "quest_role"
"""
enum quest_role_update_column {
"""column name"""
quest_id
questId
"""column name"""
rank
@@ -11177,7 +11183,7 @@ type subscription_root {
): quest_role_aggregate!
"""fetch data from the table: "quest_role" using primary key columns"""
quest_role_by_pk(quest_id: uuid!, role: String!): quest_role
quest_role_by_pk(questId: uuid!, role: String!): quest_role
"""
fetch data from the table: "quest_skill"