mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-07 11:46:09 -05:00
fix profile modal
This commit is contained in:
@@ -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) => (
|
||||
|
||||
36
components/common/MySelect.tsx
Normal file
36
components/common/MySelect.tsx
Normal 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;
|
||||
@@ -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)}>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user