케릭터에서부터 숙제지정까지

This commit is contained in:
SR07 2025-05-15 18:52:18 +09:00
parent ea1f515f9a
commit 455887f5ea
6 changed files with 305 additions and 47 deletions

View File

@ -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>

View 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>
)
}

View File

@ -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>
)
} }

View 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>
)
}

View File

@ -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>

View 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>
)
}