diff --git a/src/components/setsCreate/AbilityAutocomplete.tsx b/src/components/setsCreate/AbilityAutocomplete.tsx new file mode 100644 index 0000000..15255b0 --- /dev/null +++ b/src/components/setsCreate/AbilityAutocomplete.tsx @@ -0,0 +1,39 @@ +import Autocomplete from '@mui/material/Autocomplete' +import TextField from '@mui/material/TextField' +import React from 'react' + +interface Props { + abilities: { label: string }[] + ability: string + setAbility: (ability: string) => void + disabled: boolean +} + +export const AbilityAutocomplete: React.FC = ({ + abilities, + ability, + setAbility, + disabled, +}) => { + return ( + ( + p': { color: 'red' } }} + label="특성" + {...params} + helperText={ability || disabled ? '' : '* 올바른 특성을 선택해주세요'} + /> + )} + onChange={(e, v) => { + if (!v) return + setAbility(v.label) + }} + /> + ) +} diff --git a/src/routes/sets/createSets copy.tsx b/src/routes/sets/createSets copy.tsx deleted file mode 100644 index e91e04e..0000000 --- a/src/routes/sets/createSets copy.tsx +++ /dev/null @@ -1,432 +0,0 @@ -import LoadingButton from '@mui/lab/LoadingButton' -import { Avatar, Grid } from '@mui/material' -import Box from '@mui/material/Box' -import TextField from '@mui/material/TextField' -import Typography from '@mui/material/Typography' -import { VariantType, useSnackbar } from 'notistack' -import React from 'react' -import { useNavigate } from 'react-router-dom' -import { getCookie } from 'react-use-cookie' -import { ItemAutocomplete } from '~/components/setsCreate/ItemAutocomplete' -import { NautreAutocomplete } from '~/components/setsCreate/NatureAutocomplete' -import { PokemonAutocomplete } from '~/components/setsCreate/PokemonAutocomplete' -import { StatTable } from '~/components/setsCreate/RealStatsTable' -import { RuleSelector } from '~/components/setsCreate/ruleSelector' -import { StatSlider } from '~/components/setsCreate/StatSlider' -import { TeraTypeAutoComplete } from '~/components/setsCreate/teraTypeAutoComplete' -import { Spinner } from '~/components/Spinner' -import { useMainContext } from '~/context' -import { useAutoCompleteContext } from '~/layouts/sets/context' -import { apiUrl, fetcher, put } from '~/util' -import { - Nature, - PokemonSetCreateType, - natureInit, - pokemonInit, -} from './initialize' - -const statKeys = ['HP', '공격', '방어', '특공', '특방', '스핏'] -const styles = { - pokemonAvatar: { - width: 100, - height: 100, - margin: 'auto', - marginTop: '20px', - }, -} -export const CreateSets: React.FC = () => { - const navigate = useNavigate() - const { user } = useMainContext() - const { data } = useAutoCompleteContext() - const { enqueueSnackbar } = useSnackbar() - - const [pokemons] = React.useState(pokemonInit) - const [natures] = React.useState(natureInit) - const [items] = React.useState<{ label: string; id: number }[]>([ - { label: '', id: 0 }, - ]) - const [moves] = React.useState<[{ label: string; type: string }]>([ - { label: '테라버스트', type: 'Normal' }, - ]) - const [moveList, setMove] = React.useState<{ label: string; type: string }[]>( - [{ label: '', type: '' }], - ) - const [teraTypes] = React.useState<{ label: string; type: string }[]>([ - { label: '노말', type: 'Normal' }, - ]) - - const [disabled, setDisabled] = React.useState(true) - const [pokemonDict] = React.useState([]) - const [abilities] = React.useState<[{ label: string }]>([{ label: '' }]) - const [pokemon, setPokemon] = React.useState('미싱노') - const [item, setItem] = React.useState('') - const [nature, setNature] = React.useState('') - const [ability, setAbility] = React.useState('') - const [setsName, setName] = React.useState('') - const [Effort, setEffort] = React.useState([0, 0, 0, 0, 0, 0]) - const [Ivs, setIvs] = React.useState([31, 31, 31, 31, 31, 31]) - const [type, setType] = React.useState<'single' | 'double'>('single') - const [teratype, setTeraType] = React.useState() - const [waitUpload, setWaitUpload] = React.useState(false) - const [description, setDescription] = React.useState('') - const [realStats, setRealStats] = React.useState([0, 0, 0, 0, 0, 0]) - const [tableRows, setTableRows] = React.useState< - { label: string; value: number }[] - >([]) - - React.useEffect(() => { - const token = getCookie('Authorization') - if (!token) { - navigate('/auth') - } - }, []) - - const handleStat = (index: number, value: number) => { - if (value > 252) { - value = 252 - } else if (value < 0) { - value = 0 - } - - const totalEffort = Effort.reduce((a, b) => a + b) - if (totalEffort > 508) { - const over = 508 - totalEffort - value += over - } - - setEffort((v) => { - const updatedEffort = [...v] - updatedEffort[index] = value - return updatedEffort - }) - } - - const handleChange = ( - index: number, - event: React.ChangeEvent, - ) => { - const value = Number(event.target.value) - handleStat(index, value) - } - - const handleSlider = (index: number, newValue: number | number[]) => { - handleStat(index, newValue as number) - } - - const handleIvBlur = (index: number) => { - const updatedIvs = [...Ivs] - if (updatedIvs[index] > 31) { - updatedIvs[index] = 31 - } else if (updatedIvs[index] < 0) { - updatedIvs[index] = 0 - } - setIvs(updatedIvs) - } - - const handleClickVariant = (variant: VariantType, message: string) => () => { - enqueueSnackbar(message, { variant }) - } - - const Upload = async () => { - if (waitUpload) return - setWaitUpload(true) - if (!pokemon || !item || !nature || !ability || !teratype) { - setWaitUpload(false) - return handleClickVariant('error', '입력되지 않은 항목이 있습니다')() - } - if (Effort.reduce((a, b) => a + b) > 508) { - setWaitUpload(false) - return handleClickVariant('error', '노력치 총합이 508을 초과합니다')() - } - - await put(apiUrl('/v1/sets/create'), { - name: setsName, - pokemonId: pokemons.find((v) => v.label == pokemon)?.id, - description: description, - itemId: items.find((v) => v.label == item)?.id, - nature: nature, - ability: ability, - moves: moveList, - evs: Effort, - ivs: Ivs, - type, - teratype: teratype, - author: user?.uid, - }) - .then(() => { - handleClickVariant('success', '업로드 완료')() - navigate('/sample') - }) - .catch(handleClickVariant('error', '에러가 발생했습니다')) - } - - React.useEffect(() => { - if (!data.pokemon) return - if (pokemons.length > 2) return - - pokemons.pop() - for (const pokemon of data.pokemon) { - if (!pokemon.name) continue - pokemons.push({ - label: pokemon.name, - id: pokemon.id, - stats: pokemon.stats, - }) - pokemonDict.push(pokemon.name) - } - }, [data.pokemon]) - - React.useEffect(() => { - if (!data.items) return - if (items.length > 2) return - - items.pop() - for (const item of data.items) { - if (!item.name) continue - items.push({ - label: item.name, - id: Number(item.id), - }) - } - }, [data.items]) - - React.useEffect(() => { - if (!data.natures) return - - natures.pop() - for (const nature of data.natures) { - if (!nature.name) continue - natures.push({ - label: nature.name, - name: nature.name, - correction: nature.correction, - }) - } - }, [data.natures]) - - React.useEffect(() => { - if (!data.types) return - - teraTypes.pop() - for (const type of data.types) { - if (!type.name) continue - teraTypes.push({ - label: type.name, - type: type.en, - }) - } - }, [data.types]) - - React.useEffect(() => { - const fetchData = async () => { - const { pokemon: data } = await fetcher( - apiUrl(`/v1/autocomplete/pokemon/${pokemon}`), - ) - if (!data) return - - moves.splice(0, moves.length) - for (const move of data.moves) { - if (moves.find((m) => m.label === move.locales.ko)) { - continue - } else { - moves.push({ - label: move.locales.ko, - type: move.Mtype, - }) - } - } - } - fetchData().catch(console.error) - }, [pokemon]) - - React.useEffect(() => { - if (!pokemon || pokemon === '미싱노') return - - const { stats } = pokemons.find((v) => v.label === pokemon) || {} - const correction = - natures.find((v) => v.label === nature)?.correction || - natureInit[0].correction - if (!stats) return - - const calculateStat = ( - base: number, - iv: number, - effort: number, - statCorr: number, - index: number, - ) => { - if (index === 0) - return Math.floor((base * 2 + iv + effort / 4) * 0.5 + 60) - else - return Math.floor(((base * 2 + iv + effort / 4) * 0.5 + 5) * statCorr) - } - - const realStats = Object.keys(stats).map((key, index) => { - const stat = stats[key as keyof typeof stats] - if (index === -1) return 0 - return calculateStat( - stat, - Ivs[index], - Effort[index], - correction[key as keyof typeof correction], - index, - ) - }) - - setRealStats(realStats) - }, [pokemon, nature, Ivs, Effort]) - - React.useEffect(() => { - const physicalEndurance = - item === '진화의휘석' - ? Math.round((realStats[0] * (realStats[2] * 1.5)) / 0.411) - : Math.round((realStats[0] * realStats[2]) / 0.411) - - const specialEndurance = - item === '돌격조끼' || item === '진화의휘석' - ? Math.round((realStats[0] * (realStats[4] * 1.5)) / 0.411) - : Math.round((realStats[0] * realStats[4]) / 0.411) - - setTableRows([ - { label: 'hp', value: realStats[0] }, - { label: '공격', value: realStats[1] }, - { label: '방어', value: realStats[2] }, - { label: '특공', value: realStats[3] }, - { label: '특방', value: realStats[4] }, - { label: '스핏', value: realStats[5] }, - { label: '물리내구', value: physicalEndurance }, - { label: '특수내구', value: specialEndurance }, - ]) - }, [realStats, item]) - - React.useEffect(() => { - setDisabled(pokemon === '미싱노' ? true : false) - }, [pokemon]) - - React.useEffect(() => { - const fetchData = async () => { - const { abilities: data } = await fetcher( - apiUrl(`/v1/autocomplete/abilities/${pokemon}`), - ) - if (!data) return - abilities.splice(0, abilities.length) - for (const ability of data) { - abilities.push({ - label: ability.name, - }) - } - } - if (pokemon === '미싱노') return - fetchData().catch(console.error) - }, [pokemon]) - - React.useEffect(() => { - setMove([]) - setAbility('') - }, [pokemon]) - - return ( - - {!data.pokemon || !data.items ? ( - - - - ) : ( - - - - 샘플 등록하기 - - - { - setName(e.target.value) - }} - variant="standard" - sx={{ width: '100%' }} - /> - - - - - - - - - - - - - - setDescription(e.target.value)} - /> - - - 항목 - - - 노력치 - - - 개체값 - - {statKeys.map((stat, index) => ( - { - const updatedIvs = [...Ivs] - updatedIvs[index] = value - setIvs(updatedIvs) - }} - /> - ))} - - - - - 샘플 올리기 - - - )} - - ) -} diff --git a/src/routes/sets/createSets.tsx b/src/routes/sets/createSets.tsx index 2fb5623..632b8e6 100644 --- a/src/routes/sets/createSets.tsx +++ b/src/routes/sets/createSets.tsx @@ -1,36 +1,44 @@ import LoadingButton from '@mui/lab/LoadingButton' -import { - Autocomplete, - Avatar, - FormControl, - FormControlLabel, - FormLabel, - Grid, - Radio, - RadioGroup, -} from '@mui/material' +import { Avatar, Grid } from '@mui/material' import Box from '@mui/material/Box' -import Chip from '@mui/material/Chip' import TextField from '@mui/material/TextField' import Typography from '@mui/material/Typography' import { VariantType, useSnackbar } from 'notistack' import React from 'react' import { useNavigate } from 'react-router-dom' import { getCookie } from 'react-use-cookie' +import { AbilityAutocomplete } from '~/components/setsCreate/AbilityAutocomplete' +import { ItemAutocomplete } from '~/components/setsCreate/ItemAutocomplete' +import { NautreAutocomplete } from '~/components/setsCreate/NatureAutocomplete' +import { PokemonAutocomplete } from '~/components/setsCreate/PokemonAutocomplete' import { StatTable } from '~/components/setsCreate/RealStatsTable' +import { RuleSelector } from '~/components/setsCreate/ruleSelector' import { StatSlider } from '~/components/setsCreate/StatSlider' +import { TeraTypeAutoComplete } from '~/components/setsCreate/teraTypeAutoComplete' import { Spinner } from '~/components/Spinner' import { useMainContext } from '~/context' import { useAutoCompleteContext } from '~/layouts/sets/context' import { apiUrl, fetcher, put } from '~/util' import { + Item, + Move, Nature, PokemonSetCreateType, + itemsInit, + movesInit, natureInit, pokemonInit, } from './initialize' const statKeys = ['HP', '공격', '방어', '특공', '특방', '스핏'] +const styles = { + pokemonAvatar: { + width: 100, + height: 100, + margin: 'auto', + marginTop: '20px', + }, +} export const CreateSets: React.FC = () => { const navigate = useNavigate() const { user } = useMainContext() @@ -39,12 +47,8 @@ export const CreateSets: React.FC = () => { const [pokemons] = React.useState(pokemonInit) const [natures] = React.useState(natureInit) - const [items] = React.useState<[{ label: string; id: number }]>([ - { label: '', id: 0 }, - ]) - const [moves] = React.useState<[{ label: string; type: string }]>([ - { label: '테라버스트', type: 'Normal' }, - ]) + const [items] = React.useState(itemsInit) + const [moves] = React.useState(movesInit) const [moveList, setMove] = React.useState<{ label: string; type: string }[]>( [{ label: '', type: '' }], ) @@ -54,19 +58,20 @@ export const CreateSets: React.FC = () => { const [disabled, setDisabled] = React.useState(true) const [pokemonDict] = React.useState([]) - const [abilities] = React.useState<[{ label: string }]>([{ label: '' }]) const [pokemon, setPokemon] = React.useState('미싱노') const [item, setItem] = React.useState('') const [nature, setNature] = React.useState('') + const [abilities] = React.useState<[{ label: string }]>([{ label: '' }]) const [ability, setAbility] = React.useState('') const [setsName, setName] = React.useState('') - const [Effort, setEffort] = React.useState([0, 0, 0, 0, 0, 0]) - const [Ivs, setIvs] = React.useState([31, 31, 31, 31, 31, 31]) const [type, setType] = React.useState<'single' | 'double'>('single') const [teratype, setTeraType] = React.useState() - const [waitUpload, setWaitUpload] = React.useState(false) + const [description, setDescription] = React.useState('') + const [Effort, setEffort] = React.useState([0, 0, 0, 0, 0, 0]) + const [Ivs, setIvs] = React.useState([31, 31, 31, 31, 31, 31]) const [realStats, setRealStats] = React.useState([0, 0, 0, 0, 0, 0]) + const [waitUpload, setWaitUpload] = React.useState(false) const [tableRows, setTableRows] = React.useState< { label: string; value: number }[] >([]) @@ -341,33 +346,8 @@ export const CreateSets: React.FC = () => { 샘플 등록하기 - - - 샘플 유형 - - { - setType(e.target.value as 'single' | 'double') - }} - aria-labelledby="demo-row-radio-buttons-group-label" - name="row-radio-buttons-group" - > - } - label="싱글" - /> - } - label="더블" - /> - - + { @@ -379,222 +359,42 @@ export const CreateSets: React.FC = () => { - ( - p': { color: 'red' } }} - label="포켓몬" - {...params} - helperText={ - pokemon !== '미싱노' ? '' : '* 올바른 포켓몬을 선택해주세요' - } - /> - )} - onChange={(e, v) => { - if (!v) return - setPokemon(v.label) - }} - /> - ( - img': { mr: 2, flexShrink: 0 } }} - {...props} - > - - {option.label} - - )} - renderInput={(params) => ( - p': { color: 'red' } }} - label="지닌물건" - {...params} - helperText={item ? '' : '* 올바른 지닌물건을 선택해주세요'} - /> - )} - onChange={(e, v) => { - if (!v) return - setItem(v.label) - }} - /> + + - ( - p': { color: 'red' } }} - label="성격" - {...params} - helperText={nature ? '' : '* 올바른 성격을 선택해주세요'} - /> - )} - onChange={(e, v) => { - if (!v) return - setNature(v.name) - }} - /> + - ( - p': { color: 'red' } }} - label="특성" - {...params} - helperText={ - ability || disabled ? '' : '* 올바른 특성을 선택해주세요' - } - /> - )} - onChange={(e, v) => { - if (!v) return - setAbility(v.label) - }} /> - (moveList.length > 3 ? true : false)} - renderOption={(props, option) => ( - img': { mr: 2, flexShrink: 0 } }} - {...props} - > - - {option.label} - - )} - renderInput={(params) => ( - p': { color: 'red' } }} - label="기술" - {...params} - helperText={ - moves[0].label !== '쪼리핑펀치' || disabled - ? '' - : '* 올바른 기술을 선택해주세요' - } - /> - )} - renderTags={( - value: readonly { label: string; type: string }[], - getTagProps, - ) => - value.map( - (option: { label: string; type: string }, index: number) => ( - - } - {...getTagProps({ index })} - /> - ), - ) - } - onChange={(e, v) => { - if (!v) return - setMove(v) - }} - /> - ( - img': { mr: 2, flexShrink: 0 } }} - {...props} - > - - {option.label} - - )} - renderInput={(params) => ( - p': { color: 'red' } }} - label="테라스탈 타입" - {...params} - helperText={ - teratype ? '' : '* 올바른 테라스탈 타입을 선택해주세요' - } - /> - )} - onChange={(e, v) => { - if (!v) return - setTeraType(v.type) - }} + setDescription(e.target.value)} sx={{ width: '100%' }} + inputProps={{ maxLength: 256 }} + placeholder="샘플 설명을 입력해주세요" + onChange={(e) => setDescription(e.target.value)} /> diff --git a/src/routes/sets/index.tsx b/src/routes/sets/index.tsx index 3f6f545..64ea21b 100644 --- a/src/routes/sets/index.tsx +++ b/src/routes/sets/index.tsx @@ -6,7 +6,7 @@ import { useNavigate, useParams } from 'react-router-dom' import Swal from 'sweetalert2' import { wrapError } from '~/components/ErrorBoundary' import { useMainContext } from '~/context' -import { CreateSets } from './createSets copy' +import { CreateSets } from './createSets' import { SetsList } from './setsList' export const SetsView: React.FC = wrapError(() => { diff --git a/src/routes/sets/initialize.ts b/src/routes/sets/initialize.ts index 2094fec..cca8ead 100644 --- a/src/routes/sets/initialize.ts +++ b/src/routes/sets/initialize.ts @@ -23,6 +23,30 @@ export interface PokemonSetCreateType { } } +export interface Item { + label: string + id: number +} + +export interface Move { + label: string + type: string +} + +export interface Ability { + label: string + name: string +} + +export const movesInit = [{ label: '테라버스트', type: 'Normal' }] + +export const itemsInit = [ + { + label: '', + id: 9999, + }, +] + export const natureInit = [ { label: '',