diff --git a/components/common/MultiWeaponSelector.tsx b/components/common/MultiWeaponSelector.tsx index 916852482..03cf35ed6 100644 --- a/components/common/MultiWeaponSelector.tsx +++ b/components/common/MultiWeaponSelector.tsx @@ -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 = ({ name, value, onChange }) => { if (!!e.target.value && !value.includes(e.target.value)) onChange(value.concat(e.target.value)); }} + defaultValue="NO_VALUE" > + {weaponsWithHeroCategorizedLocalized.map((wpnCategory) => ( {wpnCategory.weapons.map((wpn) => ( diff --git a/components/common/MySelect.tsx b/components/common/MySelect.tsx new file mode 100644 index 000000000..b60089ecd --- /dev/null +++ b/components/common/MySelect.tsx @@ -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 = ({ + value, + setValue, + options, + placeholder, +}) => { + return ( + + ); +}; + +export default MySelect; diff --git a/components/common/WeaponSelector.tsx b/components/common/WeaponSelector.tsx index bb1b57973..57bb08d61 100644 --- a/components/common/WeaponSelector.tsx +++ b/components/common/WeaponSelector.tsx @@ -30,7 +30,7 @@ const WeaponSelector: React.FC = ({ size={isHeader ? "lg" : undefined} > {weaponsWithHeroCategorizedLocalized.map((wpnCategory) => ( diff --git a/components/u/ProfileModal.tsx b/components/u/ProfileModal.tsx index bad2b9575..092bd723c 100644 --- a/components/u/ProfileModal.tsx +++ b/components/u/ProfileModal.tsx @@ -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 = ({ 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 = ({ onClose, user }) => { onClose(); }; - // FIXME: modal seems slow to popup at least in dev? return ( @@ -223,16 +222,24 @@ const ProfileModal: React.FC = ({ onClose, user }) => { Country - {/* FIXME: placeholders for dropdowns */} - + /> diff --git a/lib/i18n.ts b/lib/i18n.ts index 50ee9e472..b82ea9283 100644 --- a/lib/i18n.ts +++ b/lib/i18n.ts @@ -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`); diff --git a/lib/postData.ts b/lib/postData.ts index 596267c6d..70be0a9be 100644 --- a/lib/postData.ts +++ b/lib/postData.ts @@ -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; diff --git a/lib/validators/profile.ts b/lib/validators/profile.ts index 1267bf6ee..544ff84a4 100644 --- a/lib/validators/profile.ts +++ b/lib/validators/profile.ts @@ -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 diff --git a/pages/api/me/profile.ts b/pages/api/me/profile.ts index ab2ad08a4..fc9981b4d 100644 --- a/pages/api/me/profile.ts +++ b/pages/api/me/profile.ts @@ -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;