mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-24 23:19:39 -05:00
user selector ver 1
This commit is contained in:
parent
b063675ee3
commit
cb398c8d07
|
|
@ -12,14 +12,16 @@ interface SelectProps {
|
|||
options?:
|
||||
| OptionsType<{
|
||||
label: string;
|
||||
value: string;
|
||||
value: any;
|
||||
data?: any;
|
||||
}>
|
||||
| GroupedOptionsType<{
|
||||
label: string;
|
||||
value: string;
|
||||
data?: any;
|
||||
}>;
|
||||
width?: string;
|
||||
value: ValueType<OptionTypeBase, boolean>;
|
||||
value?: ValueType<OptionTypeBase, boolean>;
|
||||
setValue: (value: any) => void;
|
||||
autoFocus?: boolean;
|
||||
components?: Partial<SelectComponents<OptionTypeBase, boolean>>;
|
||||
|
|
@ -37,13 +39,13 @@ const MySelect: React.FC<SelectProps> = ({
|
|||
components,
|
||||
value,
|
||||
setValue,
|
||||
isClearable,
|
||||
autoFocus,
|
||||
isMulti,
|
||||
isLoading,
|
||||
isDisabled,
|
||||
isSearchable,
|
||||
menuIsOpen,
|
||||
isClearable = false,
|
||||
autoFocus = false,
|
||||
isMulti = false,
|
||||
isLoading = false,
|
||||
isDisabled = false,
|
||||
isSearchable = false,
|
||||
menuIsOpen = false,
|
||||
hideMenuBeforeTyping,
|
||||
}) => {
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ const UserAvatar: React.FC<Props & AvatarProps> = ({
|
|||
isSmall,
|
||||
...props
|
||||
}) => {
|
||||
console.log({ user });
|
||||
return (
|
||||
<Avatar
|
||||
name={user.username}
|
||||
|
|
|
|||
96
components/common/UserSelector.tsx
Normal file
96
components/common/UserSelector.tsx
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { Box, Center, Flex } from "@chakra-ui/react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { GetAllUsersLeanData } from "prisma/queries/getAllUsersLean";
|
||||
import { components } from "react-select";
|
||||
import useSWR from "swr";
|
||||
import MySelect from "./MySelect";
|
||||
import UserAvatar from "./UserAvatar";
|
||||
|
||||
interface SingleSelectorProps {
|
||||
value?: number;
|
||||
setValue: (value: number) => void;
|
||||
isMulti: false | undefined;
|
||||
maxMultiCount: undefined;
|
||||
}
|
||||
|
||||
interface MultiSelectorProps {
|
||||
value: number[];
|
||||
setValue: (value: number[]) => void;
|
||||
isMulti: true;
|
||||
maxMultiCount: number;
|
||||
}
|
||||
|
||||
const UserSelector: React.FC<SingleSelectorProps | MultiSelectorProps> = ({
|
||||
value,
|
||||
setValue,
|
||||
isMulti,
|
||||
maxMultiCount,
|
||||
}) => {
|
||||
const { data } = useSWR<GetAllUsersLeanData>("/api/users");
|
||||
|
||||
const singleOption = (props: any) => {
|
||||
return (
|
||||
<components.Option {...props}>
|
||||
<Flex alignItems="center">
|
||||
<Box mr="0.5em">
|
||||
<UserAvatar user={props.data.data} />
|
||||
</Box>
|
||||
{props.label}
|
||||
</Flex>
|
||||
</components.Option>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<MySelect
|
||||
options={getUsersArray().map((user) => ({
|
||||
label: `${user.username}#${user.discriminator}`,
|
||||
value: "" + user.id,
|
||||
data: user,
|
||||
}))}
|
||||
setValue={(newValue) => {
|
||||
if (isMulti) {
|
||||
setValue(newValue.map((value: string) => parseInt(value)));
|
||||
} else {
|
||||
setValue(newValue ? parseInt(newValue) : newValue);
|
||||
}
|
||||
}}
|
||||
isSearchable
|
||||
isMulti={isMulti}
|
||||
components={{
|
||||
IndicatorSeparator: () => null,
|
||||
Option: singleOption,
|
||||
NoOptionsMessage: () => (
|
||||
<Center p={4}>
|
||||
<>
|
||||
{isTooManyItems() ? (
|
||||
<Trans>Only {maxMultiCount} users allowed</Trans>
|
||||
) : (
|
||||
<Trans>No results with this filter</Trans>
|
||||
)}
|
||||
</>
|
||||
</Center>
|
||||
),
|
||||
}}
|
||||
hideMenuBeforeTyping
|
||||
/>
|
||||
);
|
||||
|
||||
function getUsersArray() {
|
||||
console.log({ value, maxMultiCount });
|
||||
if (!data) return [];
|
||||
if (isTooManyItems()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function isTooManyItems() {
|
||||
return (
|
||||
maxMultiCount && Array.isArray(value) && maxMultiCount <= value.length
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default UserSelector;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { Box, Flex } from "@chakra-ui/react";
|
||||
import { Box, Center, Flex } from "@chakra-ui/react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import { useLingui } from "@lingui/react";
|
||||
import {
|
||||
salmonRunWeapons,
|
||||
|
|
@ -44,7 +45,7 @@ const WeaponSelector: React.FC<SingleSelectorProps | MultiSelectorProps> = ({
|
|||
const { i18n } = useLingui();
|
||||
const singleOption = (props: any) => (
|
||||
<components.Option {...props}>
|
||||
<Flex alignItems="center" color={props.isFocused ? "black" : undefined}>
|
||||
<Flex alignItems="center">
|
||||
<Box mr="0.5em">
|
||||
<WeaponImage size={32} name={props.value} />
|
||||
</Box>
|
||||
|
|
@ -71,6 +72,17 @@ const WeaponSelector: React.FC<SingleSelectorProps | MultiSelectorProps> = ({
|
|||
components={{
|
||||
IndicatorSeparator: () => null,
|
||||
Option: singleOption,
|
||||
NoOptionsMessage: () => (
|
||||
<Center p={4}>
|
||||
<>
|
||||
{isTooManyItems() ? (
|
||||
<Trans>Only {maxMultiCount} weapons allowed</Trans>
|
||||
) : (
|
||||
<Trans>No results with this filter</Trans>
|
||||
)}
|
||||
</>
|
||||
</Center>
|
||||
),
|
||||
}}
|
||||
autoFocus={autoFocus}
|
||||
isDisabled={isDisabled}
|
||||
|
|
@ -93,7 +105,7 @@ const WeaponSelector: React.FC<SingleSelectorProps | MultiSelectorProps> = ({
|
|||
}
|
||||
|
||||
function getWeaponArray() {
|
||||
if (maxMultiCount && maxMultiCount <= (value ?? []).length) return [];
|
||||
if (isTooManyItems()) return [];
|
||||
if (pool === "WITH_ALTS") return weaponsWithHeroCategorized;
|
||||
if (pool === "SALMON_RUN")
|
||||
return weaponsWithHeroCategorized.map((category) => ({
|
||||
|
|
@ -108,6 +120,10 @@ const WeaponSelector: React.FC<SingleSelectorProps | MultiSelectorProps> = ({
|
|||
),
|
||||
}));
|
||||
}
|
||||
|
||||
function isTooManyItems() {
|
||||
return maxMultiCount && maxMultiCount <= (value ?? []).length;
|
||||
}
|
||||
};
|
||||
|
||||
export default WeaponSelector;
|
||||
|
|
|
|||
9
pages/api/users/index.ts
Normal file
9
pages/api/users/index.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getAllUsersLean } from "prisma/queries/getAllUsersLean";
|
||||
|
||||
const usersHandler = async (_req: NextApiRequest, res: NextApiResponse) => {
|
||||
const users = await getAllUsersLean();
|
||||
res.status(200).json(users);
|
||||
};
|
||||
|
||||
export default usersHandler;
|
||||
|
|
@ -17,6 +17,7 @@ import { t, Trans } from "@lingui/macro";
|
|||
import { useLingui } from "@lingui/react";
|
||||
import { SalmonRunRecordCategory } from "@prisma/client";
|
||||
import Breadcrumbs from "components/common/Breadcrumbs";
|
||||
import UserSelector from "components/common/UserSelector";
|
||||
import RotationSelector from "components/sr/RotationSelector";
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
|
|
@ -54,7 +55,7 @@ const salmonRunCategoryToNatural = {
|
|||
const AddRecordModal = () => {
|
||||
const { i18n } = useLingui();
|
||||
const [sending, setSending] = useState(false);
|
||||
const [form, setForm] = useState<Partial<RecordFormData>>({});
|
||||
const [form, setForm] = useState<Partial<RecordFormData>>({ rotationId: 1 });
|
||||
|
||||
return (
|
||||
<Container maxWidth="75ch">
|
||||
|
|
@ -124,6 +125,21 @@ const AddRecordModal = () => {
|
|||
</NumberInputStepper>
|
||||
</NumberInput>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel mt={4}>
|
||||
<Trans>Players</Trans>
|
||||
</FormLabel>
|
||||
<UserSelector
|
||||
value={form.userIds ?? []}
|
||||
setValue={(userIds: number[]) => setForm({ ...form, userIds })}
|
||||
isMulti={true}
|
||||
maxMultiCount={3}
|
||||
/>
|
||||
<FormHelperText>
|
||||
Add up to three people you played with when you got the result.
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel mt={4}>
|
||||
<Trans>Links</Trans>
|
||||
|
|
@ -140,6 +156,9 @@ const AddRecordModal = () => {
|
|||
onChange={(e) => setForm({ ...form, links: e.target.value })}
|
||||
rows={4}
|
||||
resize="none"
|
||||
placeholder={
|
||||
"https://twitter.com/BrianTheDrumer/status/1338469066797953024\nhttps://www.youtube.com/watch?v=6evFXzxrTfU"
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
|
|
|
|||
17
prisma/queries/getAllUsersLean.ts
Normal file
17
prisma/queries/getAllUsersLean.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Prisma } from "@prisma/client";
|
||||
import prisma from "prisma/client";
|
||||
|
||||
export type GetAllUsersLeanData = Prisma.PromiseReturnType<
|
||||
typeof getAllUsersLean
|
||||
>;
|
||||
|
||||
export const getAllUsersLean = async () =>
|
||||
prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
discriminator: true,
|
||||
discordAvatar: true,
|
||||
discordId: true,
|
||||
},
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user