mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-24 03:00:09 -04:00
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:
committed by
Scott Stevenson
parent
514de8fe8e
commit
c684a22ce4
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 })),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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 ?? '' },
|
||||
|
||||
@@ -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'}
|
||||
|
||||
100
packages/web/components/Player/Section/PlayerCompletedQuests.tsx
Normal file
100
packages/web/components/Player/Section/PlayerCompletedQuests.tsx
Normal 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} />
|
||||
);
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 }} /> : <></>;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -182,6 +182,10 @@ export const QuestCompletionFragment = gql`
|
||||
submissionLink
|
||||
submissionText
|
||||
submittedAt
|
||||
questId
|
||||
completed {
|
||||
title
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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%">
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user