fix profile modal

This commit is contained in:
Kalle (Sendou)
2020-11-19 19:13:09 +02:00
parent bb42eca932
commit a4f2a91bb1
8 changed files with 76 additions and 20 deletions

View File

@@ -1,4 +1,5 @@
import { Select, Tag, TagCloseButton, TagLabel } from "@chakra-ui/react";
import { t } from "@lingui/macro";
import { useLingui } from "@lingui/react";
import { weaponsWithHeroCategorizedLocalized } from "lib/lists/weaponsWithHero";
import WeaponImage from "./WeaponImage";
@@ -19,7 +20,11 @@ const WeaponSelector: React.FC<Props> = ({ name, value, onChange }) => {
if (!!e.target.value && !value.includes(e.target.value))
onChange(value.concat(e.target.value));
}}
defaultValue="NO_VALUE"
>
<option hidden value="NO_VALUE">
{t`Select weapon`}
</option>
{weaponsWithHeroCategorizedLocalized.map((wpnCategory) => (
<optgroup key={wpnCategory.name} label={i18n._(wpnCategory.name)}>
{wpnCategory.weapons.map((wpn) => (

View File

@@ -0,0 +1,36 @@
import { Select } from "@chakra-ui/react";
interface Props {
value: string;
setValue: (value?: string) => void;
options: {
label: string;
value: string;
}[];
placeholder?: string;
}
const MySelect: React.FC<Props> = ({
value,
setValue,
options,
placeholder,
}) => {
return (
<Select
value={value}
onChange={(e) =>
setValue(e.target.value !== "" ? undefined : e.target.value)
}
>
{placeholder && <option value="">{placeholder}</option>}
{options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</Select>
);
};
export default MySelect;

View File

@@ -30,7 +30,7 @@ const WeaponSelector: React.FC<Props> = ({
size={isHeader ? "lg" : undefined}
>
<option hidden value="NO_VALUE">
{t`Choose weapon`}
{t`Select weapon`}
</option>
{weaponsWithHeroCategorizedLocalized.map((wpnCategory) => (
<optgroup key={wpnCategory.name} label={i18n._(wpnCategory.name)}>

View File

@@ -22,6 +22,7 @@ import { t, Trans } from "@lingui/macro";
import { useLingui } from "@lingui/react";
import MarkdownTextarea from "components/common/MarkdownTextarea";
import WeaponSelector from "components/common/MultiWeaponSelector";
import MySelect from "components/common/MySelect";
import { countries } from "countries-list";
import { getToastOptions } from "lib/getToastOptions";
import { sendData } from "lib/postData";
@@ -112,7 +113,6 @@ const ProfileModal: React.FC<Props> = ({ onClose, user }) => {
}
}
// FIXME: error handling
const success = await sendData("PUT", "/api/me/profile", mutationData);
if (!success) return;
@@ -122,7 +122,6 @@ const ProfileModal: React.FC<Props> = ({ onClose, user }) => {
onClose();
};
// FIXME: modal seems slow to popup at least in dev?
return (
<Modal isOpen onClose={onClose} size="xl" closeOnOverlayClick={false}>
<ModalOverlay>
@@ -223,16 +222,24 @@ const ProfileModal: React.FC<Props> = ({ onClose, user }) => {
<FormLabel htmlFor="country" mt={4}>
<Trans>Country</Trans>
</FormLabel>
{/* FIXME: placeholders for dropdowns */}
<Select ref={register} name="country">
{(Object.keys(countries) as Array<keyof typeof countries>).map(
(countryCode) => (
<option key={countryCode} value={countryCode}>
{countries[countryCode].name}
</option>
)
<Controller
name="country"
control={control}
defaultValue={""}
render={({ onChange, value }) => (
<MySelect
value={value}
setValue={onChange}
options={(Object.keys(countries) as Array<
keyof typeof countries
>).map((countryCode) => ({
label: countries[countryCode].name,
value: countryCode,
}))}
placeholder={t`Select country`}
/>
)}
</Select>
/>
<FormControl isInvalid={!!errors.weaponPool}>
<FormLabel htmlFor="weaponPool" mt={4}>

View File

@@ -2,12 +2,9 @@ import { i18n } from "@lingui/core";
import { en } from "make-plural/plurals";
i18n.loadLocaleData("en", { plurals: en });
//i18n.loadLocaleData("cs", { plurals: cs });
/**
* Load messages for requested locale and activate it.
* This function isn't part of the LinguiJS library because there're
* many ways how to load messages — from REST API, from file, from cache, etc.
*/
export async function activate(locale: string) {
const { messages } = await import(`locale/${locale}/messages.js`);

View File

@@ -14,12 +14,21 @@ export async function sendData(method = "POST", url = "", data = {}) {
if (response.status < 200 || response.status > 299) {
const toast = createStandaloneToast();
let description = t`An error occurred`;
try {
const error = await response.json();
console.log({ error });
if (error.message) description = error.message;
} catch {}
toast({
duration: null,
isClosable: true,
position: "top-right",
status: "error",
description: t`An error occurred`,
description,
});
return false;

View File

@@ -8,7 +8,7 @@ const profileRootSchema = z.object({
bio: z.string().max(PROFILE_CHARACTER_LIMIT).optional().nullable(),
country: z
.string()
.refine((val) => Object.keys(countries).includes(val))
.refine((val) => !val || Object.keys(countries).includes(val))
.optional()
.nullable(),
customUrlPath: z

View File

@@ -34,8 +34,8 @@ const profileHandler = async (req: NextApiRequest, res: NextApiResponse) => {
return res.status(400).end();
}
if (isDuplicateCustomUrl(argsForDb.customUrlPath, user.id)) {
return res.status(400).json({ message: "custom url already in use" });
if (await isDuplicateCustomUrl(argsForDb.customUrlPath, user.id)) {
return res.status(400).json({ message: "Custom URL already in use" });
}
await prisma.profile.upsert({
@@ -64,9 +64,11 @@ async function isDuplicateCustomUrl(customUrlPath: string, userId: number) {
},
});
if (profileWithSameCustomUrl && profileWithSameCustomUrl.userId !== userId) {
if (!profileWithSameCustomUrl || profileWithSameCustomUrl.userId === userId) {
return false;
}
return true;
}
export default profileHandler;