케릭터에서부터 숙제지정까지
This commit is contained in:
parent
ea1f515f9a
commit
455887f5ea
@ -6,8 +6,10 @@ import Layout from './components/Layout'
|
|||||||
import Home from './pages/Home'
|
import Home from './pages/Home'
|
||||||
import LoginPage from './pages/Login'
|
import LoginPage from './pages/Login'
|
||||||
import RegisterCharacter from './pages/RegisterCharacter'
|
import RegisterCharacter from './pages/RegisterCharacter'
|
||||||
|
import RegisterHomework from './pages/RegisterHomework'
|
||||||
import CharacterList from './pages/CharacterList'
|
import CharacterList from './pages/CharacterList'
|
||||||
import CharacterHomeworks from './pages/CharacterHomeworks'
|
import HomeworkList from './pages/HomeworkList'
|
||||||
|
import CharacterHomeworkSelect from './pages/CharacterHomeworkSelect' // 정확한 경로로 수정
|
||||||
import Signup from './pages/Signup'
|
import Signup from './pages/Signup'
|
||||||
|
|
||||||
const darkTheme = createTheme({
|
const darkTheme = createTheme({
|
||||||
@ -29,7 +31,9 @@ function App() {
|
|||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="/characters/register" element={<RegisterCharacter />} />
|
<Route path="/characters/register" element={<RegisterCharacter />} />
|
||||||
<Route path="/characters" element={<CharacterList />} />
|
<Route path="/characters" element={<CharacterList />} />
|
||||||
<Route path="/characters/:characterId/homeworks" element={<CharacterHomeworks />} />
|
<Route path="/homeworks" element={<HomeworkList />} />
|
||||||
|
<Route path="/homeworks/register" element={<RegisterHomework />} />
|
||||||
|
<Route path="/characters/:characterId/homeworks" element={<CharacterHomeworkSelect />} />
|
||||||
<Route path="/signup" element={<Signup />} />
|
<Route path="/signup" element={<Signup />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
53
src/pages/CharacterHomeworkSelect.tsx
Normal file
53
src/pages/CharacterHomeworkSelect.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { Box, Card, CardContent, Typography, Grid } from '@mui/material'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import api from '../lib/api'
|
||||||
|
|
||||||
|
interface Character {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
server: string
|
||||||
|
job?: string
|
||||||
|
combat_power?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CharacterHoCharacterHomeworkSelectmeworkSelect() {
|
||||||
|
const [characters, setCharacters] = useState<Character[]>([])
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchCharacters = async () => {
|
||||||
|
try {
|
||||||
|
const res = await api.get('/characters')
|
||||||
|
setCharacters(res.data)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('캐릭터 목록을 불러오는 데 실패했습니다.', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchCharacters()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleClick = (id: number) => {
|
||||||
|
navigate(`/characters/${id}/homeworks`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ p: 4 }}>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
내 숙제 관리
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{characters.map((char) => (
|
||||||
|
<Grid item key={char.id} xs={12} sm={6} md={4}>
|
||||||
|
<Card onClick={() => handleClick(char.id)} sx={{ cursor: 'pointer' }}>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h6">{char.name}</Typography>
|
||||||
|
{/* 숙제 목록이 생기면 여기에 출력 */}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,45 +1,64 @@
|
|||||||
import { Box, Card, CardContent, Typography, Grid, Button } from '@mui/material'
|
import { Box, Card, CardContent, Typography, Container, Grid } from '@mui/material'
|
||||||
import { Link } from 'react-router-dom'
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useNavigate, Link } from 'react-router-dom'
|
||||||
|
import api from '../lib/api'
|
||||||
|
|
||||||
const dummyCharacters = [
|
interface Character {
|
||||||
{ id: 1, name: '한울이', level: 45, job: '궁수', server: '라사', power: 21000 },
|
id: number
|
||||||
{ id: 2, name: '별이', level: 32, job: '사제', server: '라사', power: 18000 },
|
name: string
|
||||||
]
|
server?: string
|
||||||
|
job?: string
|
||||||
|
combat_power?: number
|
||||||
|
}
|
||||||
|
|
||||||
export default function CharacterList() {
|
export default function CharacterList() {
|
||||||
return (
|
const [characters, setCharacters] = useState<Character[]>([])
|
||||||
<Box sx={{ p: 4 }}>
|
|
||||||
<Typography variant="h5" gutterBottom>
|
useEffect(() => {
|
||||||
캐릭터 목록
|
const fetchCharacters = async () => {
|
||||||
</Typography>
|
try {
|
||||||
<Grid container spacing={2}>
|
const response = await api.get('/characters')
|
||||||
{dummyCharacters.map((char) => (
|
setCharacters(response.data)
|
||||||
<Grid item key={char.id} xs={12} sm={6} md={4}>
|
} catch (err) {
|
||||||
<Card>
|
console.error('캐릭터 목록을 불러오는 중 오류 발생:', err)
|
||||||
<CardContent>
|
}
|
||||||
<Typography variant="h6">{char.name}</Typography>
|
}
|
||||||
<Typography>레벨: {char.level}</Typography>
|
|
||||||
<Typography>직업: {char.job}</Typography>
|
fetchCharacters()
|
||||||
<Typography>서버: {char.server}</Typography>
|
}, [])
|
||||||
<Typography>전투력: {char.power}</Typography>
|
|
||||||
</CardContent>
|
return (
|
||||||
</Card>
|
<Container sx={{ mt: 4 }}>
|
||||||
</Grid>
|
<Typography variant="h5" gutterBottom>
|
||||||
))}
|
캐릭터 목록
|
||||||
<Grid item xs={12} sm={6} md={4}>
|
</Typography>
|
||||||
<Card
|
<Grid container spacing={2}>
|
||||||
component={Link}
|
{characters.map((char) => (
|
||||||
to="/characters/register"
|
<Grid item xs={12} sm={6} md={4} key={char.id}>
|
||||||
sx={{ height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', textDecoration: 'none' }}
|
<Card>
|
||||||
>
|
<CardContent>
|
||||||
<CardContent>
|
<Typography variant="h6">{char.name}</Typography>
|
||||||
<Typography variant="h6" align="center">
|
<Typography color="text.secondary">서버: {char.server || '-'}</Typography>
|
||||||
+ 캐릭터 추가
|
<Typography color="text.secondary">직업: {char.job || '-'}</Typography>
|
||||||
</Typography>
|
<Typography color="text.secondary">전투력: {char.combat_power?.toLocaleString() || '-'}</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
))}
|
||||||
</Box>
|
<Grid item xs={12} sm={6} md={4}>
|
||||||
)
|
<Card
|
||||||
|
component={Link}
|
||||||
|
to="/characters/register"
|
||||||
|
sx={{ height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h6" align="center">
|
||||||
|
+ 캐릭터 추가
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
62
src/pages/HomeworkList.tsx
Normal file
62
src/pages/HomeworkList.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { Box, Card, CardContent, Typography, Grid, Button } from '@mui/material'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import api from '../lib/api'
|
||||||
|
|
||||||
|
interface HomeworkType {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
reset_type: string
|
||||||
|
clear_count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function HomeworkList() {
|
||||||
|
const [homeworks, setHomeworks] = useState<HomeworkType[]>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchHomeworks = async () => {
|
||||||
|
try {
|
||||||
|
const res = await api.get('/homeworks')
|
||||||
|
setHomeworks(res.data)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('숙제 목록을 불러오는 데 실패했습니다.', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchHomeworks()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ p: 4 }}>
|
||||||
|
<Typography variant="h5" gutterBottom>
|
||||||
|
숙제 목록
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{homeworks.map((hw) => (
|
||||||
|
<Grid item key={hw.id} xs={12} sm={6} md={4}>
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h6">{hw.title}</Typography>
|
||||||
|
<Typography>주기: {hw.reset_type}</Typography>
|
||||||
|
<Typography>횟수: {hw.clear_count}</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
<Grid item xs={12} sm={6} md={4}>
|
||||||
|
<Card
|
||||||
|
component={Link}
|
||||||
|
to="/homeworks/register"
|
||||||
|
sx={{ height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', textDecoration: 'none' }}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h6" align="center">
|
||||||
|
+ 숙제 추가
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1,13 +1,34 @@
|
|||||||
import { Box, Button, Container, Paper, TextField, Typography } from '@mui/material'
|
import { Box, Button, Container, Paper, TextField, Typography } from '@mui/material'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import api from '../lib/api'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
export default function RegisterCharacter() {
|
export default function RegisterCharacter() {
|
||||||
const [name, setName] = useState('')
|
const [name, setName] = useState('')
|
||||||
const [server, setServer] = useState('')
|
const [server, setServer] = useState('')
|
||||||
|
const [job, setJob] = useState('')
|
||||||
|
const [power, setPower] = useState('')
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = async () => {
|
||||||
console.log('캐릭터 등록:', { name, server })
|
if (!name) {
|
||||||
// 추후 API 연동 예정
|
alert('캐릭터명을 입력해주세요.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.post('/characters', {
|
||||||
|
name,
|
||||||
|
server: server || undefined,
|
||||||
|
job: job || undefined,
|
||||||
|
combat_power: power ? parseInt(power, 10) : undefined,
|
||||||
|
})
|
||||||
|
alert('캐릭터가 성공적으로 등록되었습니다.')
|
||||||
|
navigate('/characters')
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err)
|
||||||
|
alert('캐릭터 등록 중 오류가 발생했습니다.')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -18,10 +39,11 @@ export default function RegisterCharacter() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
<TextField
|
<TextField
|
||||||
label="캐릭터명"
|
label="캐릭터명 *"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextField
|
||||||
label="서버"
|
label="서버"
|
||||||
@ -29,6 +51,19 @@ export default function RegisterCharacter() {
|
|||||||
onChange={(e) => setServer(e.target.value)}
|
onChange={(e) => setServer(e.target.value)}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
<TextField
|
||||||
|
label="직업"
|
||||||
|
value={job}
|
||||||
|
onChange={(e) => setJob(e.target.value)}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="전투력"
|
||||||
|
type="number"
|
||||||
|
value={power}
|
||||||
|
onChange={(e) => setPower(e.target.value)}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
<Button variant="contained" onClick={handleSubmit}>
|
<Button variant="contained" onClick={handleSubmit}>
|
||||||
등록
|
등록
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
85
src/pages/RegisterHomework.tsx
Normal file
85
src/pages/RegisterHomework.tsx
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { Box, TextField, Button, Typography, MenuItem, Container } from '@mui/material'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import axios from 'axios'
|
||||||
|
import api from '../lib/api'
|
||||||
|
|
||||||
|
export default function RegisterHomework() {
|
||||||
|
const [title, setTitle] = useState('')
|
||||||
|
const [description, setDescription] = useState('')
|
||||||
|
const [resetType, setResetType] = useState('')
|
||||||
|
const [clearCount, setClearCount] = useState(0)
|
||||||
|
const [error, setError] = useState('')
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
setError('')
|
||||||
|
try {
|
||||||
|
await axios({
|
||||||
|
method: 'post',
|
||||||
|
url: 'http://api.biryu2000.kr:8000/homeworks',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${localStorage.getItem('access_token')}`,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
reset_type: resetType,
|
||||||
|
clear_count: clearCount,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
navigate('/homeworks')
|
||||||
|
} catch (err: any) {
|
||||||
|
setError('숙제 등록에 실패했습니다.')
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container maxWidth="sm">
|
||||||
|
<Typography variant="h5" sx={{ mt: 4, mb: 2 }}>
|
||||||
|
숙제 등록
|
||||||
|
</Typography>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="숙제명"
|
||||||
|
margin="normal"
|
||||||
|
value={title}
|
||||||
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
label="설명"
|
||||||
|
margin="normal"
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
fullWidth
|
||||||
|
label="주기"
|
||||||
|
margin="normal"
|
||||||
|
value={resetType}
|
||||||
|
onChange={(e) => setResetType(e.target.value)}
|
||||||
|
>
|
||||||
|
<MenuItem value="daily">매일</MenuItem>
|
||||||
|
<MenuItem value="weekly">매주</MenuItem>
|
||||||
|
<MenuItem value="monthly">매달</MenuItem>
|
||||||
|
</TextField>
|
||||||
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
type="number"
|
||||||
|
label="횟수"
|
||||||
|
margin="normal"
|
||||||
|
inputProps={{ min: 0, max: 10 }}
|
||||||
|
value={clearCount}
|
||||||
|
onChange={(e) => setClearCount(parseInt(e.target.value) || 0)}
|
||||||
|
/>
|
||||||
|
{error && <Typography color="error">{error}</Typography>}
|
||||||
|
<Button fullWidth variant="contained" sx={{ mt: 2 }} onClick={handleSubmit}>
|
||||||
|
등록하기
|
||||||
|
</Button>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user