Files
TheGame/packages/web/components/Quest/QuestForm.tsx
dan13ram 8517a26048 Upgrade dependencies (#486)
* upgraded storybook dependencies

* upgraded web dependencies

* updated timezone selector

* upgrade chakra in metamaps

* upgraded react-dnd in metamaps

* upgraded framer-motion

* fixed types in metamaps

* upgraded eslint

* upgraded lerna, husky and graphql

* upgraded node version

* removed metamaps package

* fixed all eslint issues

* ran yarn format to prettier format all files

* updated lint-staged & husky scripts

* add executable perms to pre-push scripts

* updated yarn.lock

* fixed eslint and moved chakra icons to ds

* fixed emotion errors

* removed extra useContext

* update yarn.lock

* upgraded more packages

* removed unnecessary .huskyrc.json

* lint fix
2021-05-01 12:46:48 +05:30

319 lines
8.3 KiB
TypeScript

import {
Box,
ConfirmModal,
Flex,
HStack,
Input,
MetaButton,
MetaTag,
Select,
Text,
Textarea,
VStack,
} from '@metafam/ds';
import {
GuildFragmentFragment,
QuestFragmentFragment,
QuestRepetition_Enum,
QuestStatus_Enum,
} from 'graphql/autogen/types';
import { useRouter } from 'next/router';
import React, { useMemo, useState } from 'react';
import { Controller, FieldError, useForm } from 'react-hook-form';
import { QuestRepetitionHint, UriRegexp } from '../../utils/questHelpers';
import { CategoryOption, SkillOption } from '../../utils/skillHelpers';
import { FlexContainer } from '../Container';
import { SkillsSelect } from '../Skills';
import { RepetitionColors } from './QuestTags';
const validations = {
title: {
required: true,
minLength: 4,
maxLength: 50,
},
description: {
required: true,
minLength: 4,
},
repetition: {
required: true,
},
guild_id: {
required: true,
},
external_link: {
pattern: UriRegexp,
},
cooldown: {
valueAsNumber: true,
min: 1,
},
};
export interface CreateQuestFormInputs {
title: string;
description: string | undefined | null;
repetition: QuestRepetition_Enum;
status: QuestStatus_Enum;
guild_id: string | null;
external_link: string | undefined | null;
cooldown: number | undefined | null;
skills: SkillOption[];
}
const MetaFamGuildId = 'f94b7cd4-cf29-4251-baa5-eaacab98a719';
const getDefaultFormValues = (
editQuest: QuestFragmentFragment | undefined,
guilds: GuildFragmentFragment[],
): CreateQuestFormInputs => ({
title: editQuest?.title || '',
repetition: editQuest?.repetition || QuestRepetition_Enum.Unique,
description: editQuest?.description || '',
external_link: editQuest?.external_link || '',
guild_id:
editQuest?.guild_id ||
guilds.find((g) => g.id === MetaFamGuildId)?.id ||
guilds[0].id,
status: editQuest?.status || QuestStatus_Enum.Open,
cooldown: editQuest?.cooldown || null,
skills: editQuest
? editQuest.quest_skills
.map((s) => s.skill)
.map((s) => ({
value: s.id,
label: s.name,
...s,
}))
: [],
});
type FieldProps = {
children: React.ReactNode;
label: string;
error?: FieldError;
};
const Field: React.FC<FieldProps> = ({ children, error, label }) => (
<Flex mb={2} w="100%" align="center" direction="column">
<Flex justify="space-between" w="100%" mb={2}>
<Text textStyle="caption" textAlign="left" ml={4}>
{label}
</Text>
<Text textStyle="caption" textAlign="left" color="red.400" mr={4}>
{error?.type === 'required' && 'Required'}
{error?.type === 'pattern' && 'Invalid URL'}
{error?.type === 'minLength' && 'Too short'}
{error?.type === 'maxLength' && 'Too long'}
{error?.type === 'min' && 'Too small'}
</Text>
</Flex>
{children}
</Flex>
);
type Props = {
guilds: GuildFragmentFragment[];
editQuest?: QuestFragmentFragment;
skillChoices: Array<CategoryOption>;
onSubmit: (data: CreateQuestFormInputs) => void;
success?: boolean;
fetching?: boolean;
submitLabel: string;
loadingLabel: string;
};
export const QuestForm: React.FC<Props> = ({
guilds,
skillChoices,
onSubmit,
success,
fetching,
submitLabel,
loadingLabel,
editQuest,
}) => {
const defaultValues = useMemo<CreateQuestFormInputs>(
() => getDefaultFormValues(editQuest, guilds),
[editQuest, guilds],
);
const {
register,
control,
errors,
watch,
handleSubmit,
} = useForm<CreateQuestFormInputs>({
defaultValues,
});
const router = useRouter();
const [exitAlert, setExitAlert] = useState<boolean>(false);
const createQuestInput = watch();
return (
<Box w="100%" maxW="30rem">
<VStack>
<Field label="Title" error={errors.title}>
<Input
background="dark"
placeholder="Buidl stuff"
isRequired
name="title"
ref={register(validations.title)}
isInvalid={!!errors.title}
minLength={validations.title.minLength}
maxLength={validations.title.maxLength}
/>
</Field>
<Field label="Description" error={errors.description}>
<Textarea
background="dark"
placeholder="Please describe in details what needs to be done"
isRequired
name="description"
ref={register(validations.description)}
isInvalid={!!errors.description}
/>
</Field>
<Field label="Link" error={errors.external_link}>
<Input
background="dark"
placeholder="External link"
name="external_link"
ref={register(validations.external_link)}
isInvalid={!!errors.external_link}
/>
</Field>
<Field label="Repetition">
<Select
isRequired
name="repetition"
ref={register(validations.repetition)}
isInvalid={!!errors.repetition}
bg="dark"
color="white"
>
{Object.entries(QuestRepetition_Enum).map(([key, value]) => (
<option key={value} value={value}>
{key}
</option>
))}
</Select>
<MetaTag
size="md"
fontWeight="normal"
p={2}
mt={2}
backgroundColor={RepetitionColors[createQuestInput.repetition]}
>
{QuestRepetitionHint[createQuestInput.repetition]}
</MetaTag>
</Field>
{createQuestInput.repetition === QuestRepetition_Enum.Recurring && (
<Field label="Cooldown (hours)" error={errors.cooldown}>
<Input
isRequired
background="dark"
placeholder="3600"
name="cooldown"
type="number"
ref={register(validations.cooldown)}
isInvalid={!!errors.cooldown}
/>
</Field>
)}
<Field label="Guild">
<Select
isRequired
name="guild_id"
ref={register(validations.guild_id)}
isInvalid={!!errors.guild_id}
bg="dark"
color="white"
>
{guilds.map((guild) => (
<option key={guild.id} value={guild.id}>
{guild.name}
</option>
))}
</Select>
</Field>
{editQuest && (
<Field label="Status">
<Select
isRequired
name="status"
bg="dark"
color="white"
ref={register}
isInvalid={!!errors.status}
>
{Object.entries(QuestStatus_Enum).map(([key, value]) => (
<option key={value} value={value}>
{key}
</option>
))}
</Select>
</Field>
)}
<Field label="Skills">
<FlexContainer w="100%" align="stretch" maxW="50rem">
<Controller
name="skills"
control={control}
defaultValue={[]}
render={({ onChange, value }) => (
<SkillsSelect
skillChoices={skillChoices}
skills={value}
setSkills={onChange}
placeHolder="Select required skills"
/>
)}
/>
</FlexContainer>
</Field>
<HStack justify="space-between" mt={4} w="100%">
<MetaButton
variant="outline"
colorScheme="pink"
onClick={() => setExitAlert(true)}
isDisabled={fetching || success}
>
Cancel
</MetaButton>
<MetaButton
mt={10}
isLoading={fetching}
loadingText={loadingLabel}
onClick={handleSubmit(onSubmit)}
isDisabled={success}
>
{submitLabel}
</MetaButton>
</HStack>
</VStack>
<ConfirmModal
isOpen={exitAlert}
onNope={() => setExitAlert(false)}
onYep={() =>
router.push(editQuest ? `/quest/${editQuest.id}` : '/quests')
}
header="Are you sure you want to leave ?"
/>
</Box>
);
};