-
Notifications
You must be signed in to change notification settings - Fork 0
관리자 메인 페이지 구현 ( issue #318 ) #328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
0e66543
8945a90
eef03c4
e74714c
b222ea3
722cc8a
c9bda6d
6fcbc1d
eb77540
90e5e32
b327076
31a4c93
3a5e87c
0b30826
7b09a7c
e2c113d
d667d0b
9b42294
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import type { ApiPostContent } from '../models/report'; | ||
| import { ApiAllReports, type ApiPostContent } from '../models/report'; | ||
|
||
| import { httpClient } from './http.api'; | ||
|
|
||
| export const postReport = async (formData: ApiPostContent) => { | ||
|
|
@@ -13,3 +13,13 @@ export const postReport = async (formData: ApiPostContent) => { | |
| throw error; | ||
| } | ||
| }; | ||
|
|
||
| export const getAllReports = async () => { | ||
| try { | ||
| const response = await httpClient.get<ApiAllReports>(`/reports`); | ||
| return response.data.data; | ||
| } catch (e) { | ||
| console.error(e); | ||
| throw e; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| import styled from 'styled-components'; | ||
|
|
||
| export const Container = styled.div``; | ||
| export const Container = styled.div` | ||
| height: 350px; | ||
| `; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,97 @@ | ||
| import React from 'react'; | ||
| import * as S from './GraphCard.styled'; | ||
| import { Line } from 'react-chartjs-2'; | ||
| import 'chart.js/auto'; | ||
| import { ChartData, ChartOptions } from 'chart.js'; | ||
|
|
||
| const GraphCard = () => { | ||
| return <S.Container>GraphCard Component</S.Container>; | ||
| return ( | ||
| <S.Container> | ||
| <Line data={data} options={options} /> | ||
| </S.Container> | ||
| ); | ||
| }; | ||
|
|
||
| const data: ChartData<'line'> = { | ||
| labels: [ | ||
| '월요일', | ||
| '화요일', | ||
| '수요일', | ||
| '목요일', | ||
| '금요일', | ||
| '토요일', | ||
| '일요일', | ||
| ], | ||
| datasets: [ | ||
| { | ||
| label: '방문자 수', | ||
| data: [8, 6, 4, 0, 7, 5, 3], | ||
| fill: true, | ||
| backgroundColor: 'rgba(54, 162, 235, 0.2)', | ||
| borderColor: 'rgba(54, 162, 235, 1)', | ||
| borderWidth: 2, | ||
| tension: 0.3, | ||
| pointRadius: 5, | ||
| pointBackgroundColor: '#ffffff', | ||
| pointBorderColor: 'rgba(54, 162, 235, 1)', | ||
| pointBorderWidth: 2, | ||
| pointHoverRadius: 6, | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| const options: ChartOptions<'line'> = { | ||
| responsive: true, | ||
| maintainAspectRatio: false, | ||
| plugins: { | ||
| legend: { | ||
| display: false, | ||
| }, | ||
| tooltip: { | ||
| enabled: true, | ||
| }, | ||
| title: { | ||
| display: false, | ||
| }, | ||
| }, | ||
| scales: { | ||
| x: { | ||
| grid: { | ||
| display: true, | ||
| borderDash: [5, 5], | ||
| color: 'rgba(0,0,0,0.1)', | ||
| }, | ||
| ticks: { | ||
| font: { | ||
| size: 14, | ||
| }, | ||
| color: '#333', | ||
| }, | ||
| }, | ||
| y: { | ||
| grid: { | ||
| display: true, | ||
| drawBorder: false, | ||
|
|
||
| borderDash: [5, 5], | ||
| color: 'rgba(0,0,0,0.1)', | ||
| }, | ||
| ticks: { | ||
| stepSize: 2, | ||
| font: { | ||
| size: 14, | ||
| }, | ||
| color: '#333', | ||
| callback: (value) => `${value}`, | ||
| }, | ||
| beginAtZero: true, | ||
| suggestedMax: 8, | ||
| }, | ||
| }, | ||
| animation: { | ||
| duration: 800, | ||
| easing: 'easeOutQuart', | ||
| }, | ||
| }; | ||
|
|
||
| export default GraphCard; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,44 @@ | ||
| import { Link } from 'react-router-dom'; | ||
| import styled from 'styled-components'; | ||
|
|
||
| export const Container = styled.div``; | ||
| export const Container = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| `; | ||
|
|
||
| export const Wrapper = styled.div` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| padding: 10px; | ||
| `; | ||
|
|
||
| export const UserArea = styled.div` | ||
| display: flex; | ||
| `; | ||
|
|
||
| export const ContentArea = styled(Link)` | ||
| margin-left: 16px; | ||
| `; | ||
|
|
||
| export const NickName = styled.p` | ||
| font-size: 14px; | ||
| `; | ||
|
|
||
| export const Email = styled.p` | ||
| font-size: 9px; | ||
| opacity: 50%; | ||
| `; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export const MoveToUsersArea = styled(Link)` | ||
| display: flex; | ||
| align-items: center; | ||
| `; | ||
|
|
||
| export const Text = styled.p` | ||
| font-size: 9px; | ||
| `; | ||
|
|
||
| export const Arrow = styled.img` | ||
| width: 10px; | ||
| height: 10px; | ||
| `; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,38 @@ | ||
| import React from 'react'; | ||
| import * as S from './AllUserPreview.styled'; | ||
| import { useGetAllUsers } from '../../../../hooks/admin/useGetAllUsers'; | ||
| import Avatar from '../../../common/avatar/Avatar'; | ||
| import { ADMIN_ROUTE } from '../../../../constants/routes'; | ||
| import arrow_right from '../../../../assets/ArrowRight.svg'; | ||
|
|
||
| const AllUserPreview = () => { | ||
| return <S.Container>AllUserPreview Component</S.Container>; | ||
| const { allUserData, isLoading } = useGetAllUsers(); | ||
|
||
|
|
||
| const previewList = allUserData | ||
| ? allUserData.length > 6 | ||
| ? allUserData.slice(0, 4) | ||
| : allUserData | ||
| : []; | ||
|
|
||
| return ( | ||
| <S.Container> | ||
| {previewList?.map((user) => ( | ||
| <S.Wrapper key={user.id}> | ||
| <S.UserArea> | ||
| <Avatar image={undefined} size='40px' /> | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| <S.ContentArea to={`${ADMIN_ROUTE.allUser}/${user.id}`}> | ||
| <S.NickName>{user.user.nickname}</S.NickName> | ||
| <S.Email>{user.email}</S.Email> | ||
| </S.ContentArea> | ||
| </S.UserArea> | ||
| <S.MoveToUsersArea to={`${ADMIN_ROUTE.allUser}/${user.id}`}> | ||
| <S.Text>상세 보기</S.Text> | ||
| <S.Arrow src={arrow_right} /> | ||
| </S.MoveToUsersArea> | ||
| </S.Wrapper> | ||
| ))} | ||
| </S.Container> | ||
| ); | ||
| }; | ||
|
|
||
| export default AllUserPreview; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,69 @@ | ||
| import { Link } from 'react-router-dom'; | ||
| import styled from 'styled-components'; | ||
|
|
||
| export const Container = styled.div``; | ||
| export const Container = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| `; | ||
|
|
||
| export const Wrapper = styled.div` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| padding: 10px; | ||
| `; | ||
|
|
||
| export const Content = styled.div` | ||
| display: flex; | ||
| `; | ||
|
|
||
| export const Inquiry = styled(Link)` | ||
| margin-left: 16px; | ||
| `; | ||
|
|
||
| export const Category = styled.p` | ||
| font-size: 9px; | ||
| opacity: 50%; | ||
| `; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export const Title = styled.p` | ||
| font-size: 13px; | ||
| white-space: nowrap; | ||
| overflow: hidden; | ||
| text-overflow: ellipsis; | ||
| `; | ||
|
|
||
| export const StateArea = styled.div` | ||
| display: flex; | ||
| `; | ||
|
|
||
| export const Date = styled.p` | ||
| font-size: 9px; | ||
| opacity: 50%; | ||
| `; | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| export const Divider = styled.p` | ||
| font-size: 9px; | ||
| opacity: 20%; | ||
| margin-left: 3px; | ||
| margin-right: 3px; | ||
| `; | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export const InquiryState = styled.p<{ $isCompleted: boolean }>` | ||
| font-size: 9px; | ||
| color: ${({ $isCompleted }) => ($isCompleted ? `#07DE00` : `#DE1A00`)}; | ||
| `; | ||
|
|
||
| export const MoveToInquiryArea = styled(Link)` | ||
| display: flex; | ||
| font-size: 9px; | ||
| `; | ||
|
|
||
| export const Text = styled.p` | ||
| font-size: 9px; | ||
| `; | ||
|
|
||
| export const Arrow = styled.img` | ||
| width: 11px; | ||
| height: 11px; | ||
| `; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,45 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import * as S from './InquiresPreview.styled'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useGetAllInquiries } from '../../../../hooks/admin/useGetAllInquiries'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import Avatar from '../../../common/avatar/Avatar'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ADMIN_ROUTE } from '../../../../constants/routes'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import arrow_right from '../../../../assets/ArrowRight.svg'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const InquiresPreview = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return <S.Container>InquiresPreview Component</S.Container>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { allInquiriesData } = useGetAllInquiries(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const previewList = allInquiriesData | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? allInquiriesData.length > 6 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? allInquiriesData.slice(0, 4) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : allInquiriesData | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Container> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {previewList?.map((inquiry) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Wrapper key={inquiry.id}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Content> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* <Link to={`${ADMIN_ROUTE.}`} */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Avatar image={inquiry.user.img} size='40px' /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Inquiry to={`${ADMIN_ROUTE.inquiries}/${inquiry.id}`}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Category>{inquiry.category}</S.Category> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Title>{inquiry.title}</S.Title> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.StateArea> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Date>{inquiry.createdAt}</S.Date> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Divider>|</S.Divider> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.InquiryState $isCompleted={inquiry.state}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {inquiry.state ? '답변 완료' : '답변 대기 중'} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </S.InquiryState> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </S.StateArea> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </S.Inquiry> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </S.Content> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.MoveToInquiryArea to={`${ADMIN_ROUTE.inquiries}/${inquiry.id}`}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Text>상세 보기</S.Text> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <S.Arrow src={arrow_right} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </S.MoveToInquiryArea> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </S.Wrapper> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 에러 상태 처리 누락 API 요청이 실패했을 때의 에러 상태 처리가 누락되어 있습니다. 사용자에게 적절한 피드백을 제공해야 합니다.
{previewList?.map((inquiry) => (
...
))}
+ {!isLoading && previewList.length === 0 && (
+ <div>표시할 문의가 없습니다.</div>
+ )}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </S.Container> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default InquiresPreview; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ApiAllInquiries 같은 경우는 activityLog가 아니기 때문에 디렉토리 분리를 하는게 더 좋지 않을까 싶습니당,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그럼 admin으로 하나를 더 만들게요