-
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 13 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,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { ApiAlarmList } from '../models/alarm'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import useAuthStore from '../store/authStore'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { httpClient } from './http.api'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const getAlarmList = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -36,14 +37,19 @@ export const patchAlarm = async (id: number) => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const testLiveAlarm = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await httpClient.get<ApiAlarmList>( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `/user/send-alarm?alarmFilter=0` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { accessToken } = useAuthStore.getState(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (accessToken) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await httpClient.get<ApiAlarmList>( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `/user/send-alarm?alarmFilter=0` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw e; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error(e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw e; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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 함수의 반환 타입 일관성 개선 필요 인증 토큰 확인 로직 추가는 좋은 보안 관행입니다. 하지만 몇 가지 개선이 필요합니다:
다음과 같이 개선을 권장합니다: -export const testLiveAlarm = async () => {
+export const sendUserAlarm = async () => {
const { accessToken } = useAuthStore.getState();
- if (accessToken) {
+ if (!accessToken) {
+ throw new Error('인증 토큰이 없습니다.');
+ }
+
- try {
- const response = await httpClient.get<ApiAlarmList>(
- `/user/send-alarm?alarmFilter=0`
- );
+ try {
+ const response = await httpClient.get<ApiAlarmList>(
+ `/user/send-alarm?alarmFilter=0`
+ );
- return response;
- } catch (e) {
- console.error(e);
- throw e;
- }
- } else {
- return;
+ return response;
+ } catch (e) {
+ console.error(e);
+ throw e;
}
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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; | ||
| `; | ||
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으로 하나를 더 만들게요