케릭터에서부터 숙제지정까지
This commit is contained in:
parent
ea1f515f9a
commit
455887f5ea
@ -6,8 +6,10 @@ import Layout from './components/Layout'
|
||||
import Home from './pages/Home'
|
||||
import LoginPage from './pages/Login'
|
||||
import RegisterCharacter from './pages/RegisterCharacter'
|
||||
import RegisterHomework from './pages/RegisterHomework'
|
||||
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'
|
||||
|
||||
const darkTheme = createTheme({
|
||||
@ -29,7 +31,9 @@ function App() {
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/characters/register" element={<RegisterCharacter />} />
|
||||
<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 />} />
|
||||
</Routes>
|
||||
</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 { Link } from 'react-router-dom'
|
||||
import { Box, Card, CardContent, Typography, Container, Grid } from '@mui/material'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useNavigate, Link } from 'react-router-dom'
|
||||
import api from '../lib/api'
|
||||
|
||||
const dummyCharacters = [
|
||||
{ id: 1, name: '한울이', level: 45, job: '궁수', server: '라사', power: 21000 },
|
||||
{ id: 2, name: '별이', level: 32, job: '사제', server: '라사', power: 18000 },
|
||||
]
|
||||
interface Character {
|
||||
id: number
|
||||
name: string
|
||||
server?: string
|
||||
job?: string
|
||||
combat_power?: number
|
||||
}
|
||||
|
||||
export default function CharacterList() {
|
||||
return (
|
||||
<Box sx={{ p: 4 }}>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
캐릭터 목록
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
{dummyCharacters.map((char) => (
|
||||
<Grid item key={char.id} xs={12} sm={6} md={4}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h6">{char.name}</Typography>
|
||||
<Typography>레벨: {char.level}</Typography>
|
||||
<Typography>직업: {char.job}</Typography>
|
||||
<Typography>서버: {char.server}</Typography>
|
||||
<Typography>전투력: {char.power}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
<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>
|
||||
</Box>
|
||||
)
|
||||
const [characters, setCharacters] = useState<Character[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCharacters = async () => {
|
||||
try {
|
||||
const response = await api.get('/characters')
|
||||
setCharacters(response.data)
|
||||
} catch (err) {
|
||||
console.error('캐릭터 목록을 불러오는 중 오류 발생:', err)
|
||||
}
|
||||
}
|
||||
|
||||
fetchCharacters()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Container sx={{ mt: 4 }}>
|
||||
<Typography variant="h5" gutterBottom>
|
||||
캐릭터 목록
|
||||
</Typography>
|
||||
<Grid container spacing={2}>
|
||||
{characters.map((char) => (
|
||||
<Grid item xs={12} sm={6} md={4} key={char.id}>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="h6">{char.name}</Typography>
|
||||
<Typography color="text.secondary">서버: {char.server || '-'}</Typography>
|
||||
<Typography color="text.secondary">직업: {char.job || '-'}</Typography>
|
||||
<Typography color="text.secondary">전투력: {char.combat_power?.toLocaleString() || '-'}</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
<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 { useState } from 'react'
|
||||
import api from '../lib/api'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
export default function RegisterCharacter() {
|
||||
const [name, setName] = useState('')
|
||||
const [server, setServer] = useState('')
|
||||
const [job, setJob] = useState('')
|
||||
const [power, setPower] = useState('')
|
||||
const navigate = useNavigate()
|
||||
|
||||
const handleSubmit = () => {
|
||||
console.log('캐릭터 등록:', { name, server })
|
||||
// 추후 API 연동 예정
|
||||
const handleSubmit = async () => {
|
||||
if (!name) {
|
||||
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 (
|
||||
@ -18,10 +39,11 @@ export default function RegisterCharacter() {
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
<TextField
|
||||
label="캐릭터명"
|
||||
label="캐릭터명 *"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
fullWidth
|
||||
required
|
||||
/>
|
||||
<TextField
|
||||
label="서버"
|
||||
@ -29,6 +51,19 @@ export default function RegisterCharacter() {
|
||||
onChange={(e) => setServer(e.target.value)}
|
||||
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>
|
||||
|
||||
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