From e5c44a910760fdc1b3b4fdc25f463dee6cb8a36b Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 11 Oct 2025 02:52:57 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=84=B1=EC=8B=A4=EC=98=A8?= =?UTF-8?q?=EB=8F=84=20=EB=82=B4=EC=97=AD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/detail/[id]/sincerity-temp/page.tsx | 55 ++++++++++++- public/icons/trending-down.svg | 3 + public/icons/trending-up.svg | 3 + .../sincerity-temperature-history.server.ts | 19 +++++ .../api/sincerity-temperature-history.ts | 19 +++++ src/features/admin/api/types.ts | 30 +++++++ ...use-sincerity-temperature-history-query.ts | 17 ++++ .../admin/ui/sincerity-temp-table.tsx | 82 +++++++++++++++++++ 8 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 public/icons/trending-down.svg create mode 100644 public/icons/trending-up.svg create mode 100644 src/features/admin/api/sincerity-temperature-history.server.ts create mode 100644 src/features/admin/api/sincerity-temperature-history.ts create mode 100644 src/features/admin/model/use-sincerity-temperature-history-query.ts create mode 100644 src/features/admin/ui/sincerity-temp-table.tsx diff --git a/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx b/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx index ea0e590f..3e5e4851 100644 --- a/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx +++ b/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx @@ -1,3 +1,54 @@ -export default function SincerityTempPage() { - return
SincerityTempPage
; +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query'; +import { getSincerityTemperatureHistoryInServer } from '@/features/admin/api/sincerity-temperature-history.server'; +import { GetSincerityTemperatureHistoryResponse } from '@/features/admin/api/types'; +import SincerityTempTable from '@/features/admin/ui/sincerity-temp-table'; + +export default async function SincerityTempPage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const queryClient = new QueryClient(); + const { id } = await params; + + const memberId = Number(id); + + // 서버 side에서 첫 페이지 데이터 미리 가져오기 + await queryClient.prefetchQuery({ + queryKey: ['sincerityTemperatureHistory', memberId, 1], // "sincerityTemperatureHistory", memberId, page + queryFn: () => + getSincerityTemperatureHistoryInServer({ + memberId: Number(memberId), + page: 1, + }), + }); + + const data: GetSincerityTemperatureHistoryResponse = + await queryClient.getQueryData([ + 'sincerityTemperatureHistory', + memberId, + 1, + ]); + + return ( + +
+
+ + 현재 성실 온도 + + +
+ {data.currentSincerityTemperature} ℃ +
+
+ + +
+
+ ); } diff --git a/public/icons/trending-down.svg b/public/icons/trending-down.svg new file mode 100644 index 00000000..7a615a60 --- /dev/null +++ b/public/icons/trending-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/trending-up.svg b/public/icons/trending-up.svg new file mode 100644 index 00000000..1ea42c7d --- /dev/null +++ b/public/icons/trending-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/features/admin/api/sincerity-temperature-history.server.ts b/src/features/admin/api/sincerity-temperature-history.server.ts new file mode 100644 index 00000000..e8ff25be --- /dev/null +++ b/src/features/admin/api/sincerity-temperature-history.server.ts @@ -0,0 +1,19 @@ +import { axiosServerInstance } from '@/shared/tanstack-query/axios.server'; +import { + GetSincerityTemperatureHistoryRequest, + GetSincerityTemperatureHistoryResponse, +} from './types'; + +// 성실 온도 이력 조회 API 요청 함수 +export const getSincerityTemperatureHistoryInServer = async ({ + memberId, + page = 1, +}: GetSincerityTemperatureHistoryRequest): Promise => { + const queryString = `page=${page}&page-size=10`; + + const res = await axiosServerInstance.get( + `/admin/members/${memberId}/sincerity-temperature-histories?${queryString}`, + ); + + return res.data.content; +}; diff --git a/src/features/admin/api/sincerity-temperature-history.ts b/src/features/admin/api/sincerity-temperature-history.ts new file mode 100644 index 00000000..188f202f --- /dev/null +++ b/src/features/admin/api/sincerity-temperature-history.ts @@ -0,0 +1,19 @@ +import { axiosInstance } from '@/shared/tanstack-query/axios'; +import { + GetSincerityTemperatureHistoryRequest, + GetSincerityTemperatureHistoryResponse, +} from './types'; + +// 성실 온도 이력 조회 API 요청 함수 +export const getSincerityTemperatureHistory = async ({ + memberId, + page = 1, +}: GetSincerityTemperatureHistoryRequest): Promise => { + const queryString = `page=${page}&page-size=10`; + + const res = await axiosInstance.get( + `/admin/members/${memberId}/sincerity-temperature-histories?${queryString}`, + ); + + return res.data.content; +}; diff --git a/src/features/admin/api/types.ts b/src/features/admin/api/types.ts index f85d2cb9..0c4ab8a2 100644 --- a/src/features/admin/api/types.ts +++ b/src/features/admin/api/types.ts @@ -2,6 +2,7 @@ export type RoleId = 'ROLE_MEMBER' | 'ROLE_ADMIN' | 'ROLE_MENTOR'; type RoleName = '일반' | '관리자' | '멘토'; export type MemberStatus = 'ACTIVE' | 'PAUSED' | 'PERM_BAN' | 'DORMANT'; +// 사용자 리스트 조회 API 요청 타입 export interface GetMemberListRequest { roleId?: RoleId; memberStatus?: MemberStatus; @@ -9,6 +10,7 @@ export interface GetMemberListRequest { page?: number; } +// 사용자 리스트 조회 API 응답 타입 export interface GetMemberListResponse { page: number; size: number; @@ -29,10 +31,12 @@ export interface GetMemberListResponse { }[]; } +// 사용자 계정 이력 조회 API 요청 타입 export interface GetAccountHistoriesRequest { memberId: number; } +// 사용자 계정 이력 조회 API 응답 타입 export interface GetAccountHistoriesResponse { memberId: number; joinedAt: string; @@ -50,12 +54,38 @@ export interface GetAccountHistoriesResponse { }[]; } +// 사용자 상태 변경 API 요청 타입 export interface ChangeMemberStatusRequest { memberId: number; to: MemberStatus; } +// 사용자 권한 변경 API 요청 타입 export interface ChangeMemberRoleRequest { memberId: number; roleId: RoleId; } + +// 성실 온도 이력 조회 API 요청 타입 +export interface GetSincerityTemperatureHistoryRequest { + memberId: number; + page: number; +} + +// 성실 온도 이력 조회 API 응답 타입 +export interface GetSincerityTemperatureHistoryResponse { + currentSincerityTemperature: number; + sincerityTemperatureHistory: { + content: { + reasonType: 'STUDY_REVIEW'; + increment: number; + recordedAt: string; + }[]; + page: number; + size: number; + totalElements: number; + totalPages: number; + hasNext: boolean; + hasPrevious: boolean; + }; +} diff --git a/src/features/admin/model/use-sincerity-temperature-history-query.ts b/src/features/admin/model/use-sincerity-temperature-history-query.ts new file mode 100644 index 00000000..a183962c --- /dev/null +++ b/src/features/admin/model/use-sincerity-temperature-history-query.ts @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query'; +import { getSincerityTemperatureHistory } from '../api/sincerity-temperature-history'; +import { GetSincerityTemperatureHistoryRequest } from '../api/types'; + +export const useGetSincerityTemperatureHistoryQuery = ({ + memberId, + page, +}: GetSincerityTemperatureHistoryRequest) => { + return useQuery({ + queryKey: ['sincerityTemperatureHistory', memberId, page], + queryFn: () => + getSincerityTemperatureHistory({ + memberId, + page, + }), + }); +}; diff --git a/src/features/admin/ui/sincerity-temp-table.tsx b/src/features/admin/ui/sincerity-temp-table.tsx new file mode 100644 index 00000000..5490dca3 --- /dev/null +++ b/src/features/admin/ui/sincerity-temp-table.tsx @@ -0,0 +1,82 @@ +'use client'; + +import { useState } from 'react'; +import { formatYYYYMMDD } from '@/shared/lib/time'; +import Pagination from '@/shared/ui/pagination'; +import TrendingDown from 'public/icons/trending-down.svg'; +import TrendingUp from 'public/icons/trending-up.svg'; +import { useGetSincerityTemperatureHistoryQuery } from '../model/use-sincerity-temperature-history-query'; + +interface SincerityTempTableProps { + memberId: number; +} + +export default function SincerityTempTable({ + memberId, +}: SincerityTempTableProps) { + const [page, setPage] = useState(1); + + const { data } = useGetSincerityTemperatureHistoryQuery({ + memberId, + page, + }); + + const sincerityTemperatureHistory = data?.sincerityTemperatureHistory.content; + + return ( + <> +
+ + 성실온도 내역 + + +
+ {sincerityTemperatureHistory?.length > 0 ? ( + sincerityTemperatureHistory.map((history, idx) => ( +
+
+ + {history.reasonType === 'STUDY_REVIEW' ? '스터디 리뷰' : ''} + + + {formatYYYYMMDD(history.recordedAt)} + +
+ +
+ {history.increment > 0 ? ( + <> + + +{history.increment}℃ + + + + ) : ( + <> + + {history.increment}℃ + + + + )} +
+
+ )) + ) : ( +
+ 성실 온도 내역이 없습니다. +
+ )} +
+
+ + + + ); +} From 71008dc8f531365337ea06e9d563225fba9aeeec Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 11 Oct 2025 03:09:30 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EC=84=B1=EC=8B=A4=EC=98=A8?= =?UTF-8?q?=EB=8F=84=20preset=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/detail/[id]/sincerity-temp/page.tsx | 23 ++++++++++++++++--- src/features/admin/api/types.ts | 1 + 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx b/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx index 3e5e4851..cb4567b9 100644 --- a/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx +++ b/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx @@ -6,6 +6,14 @@ import { import { getSincerityTemperatureHistoryInServer } from '@/features/admin/api/sincerity-temperature-history.server'; import { GetSincerityTemperatureHistoryResponse } from '@/features/admin/api/types'; import SincerityTempTable from '@/features/admin/ui/sincerity-temp-table'; +import { getSincerityPresetByLevelName } from '@/shared/config/sincerity-temp-presets'; + +const LEVEL_NAME_MAP = { + FIRST: '1단계', + SECOND: '2단계', + THIRD: '3단계', + FOURTH: '4단계', +}; export default async function SincerityTempPage({ params, @@ -22,7 +30,7 @@ export default async function SincerityTempPage({ queryKey: ['sincerityTemperatureHistory', memberId, 1], // "sincerityTemperatureHistory", memberId, page queryFn: () => getSincerityTemperatureHistoryInServer({ - memberId: Number(memberId), + memberId, page: 1, }), }); @@ -34,6 +42,10 @@ export default async function SincerityTempPage({ 1, ]); + const temperPreset = getSincerityPresetByLevelName( + LEVEL_NAME_MAP[data.sincerityTempLevel], + ); + return (
@@ -42,8 +54,13 @@ export default async function SincerityTempPage({ 현재 성실 온도 -
- {data.currentSincerityTemperature} ℃ +
+ + + {data.currentSincerityTemperature} ℃ +
diff --git a/src/features/admin/api/types.ts b/src/features/admin/api/types.ts index 0c4ab8a2..494cde1b 100644 --- a/src/features/admin/api/types.ts +++ b/src/features/admin/api/types.ts @@ -75,6 +75,7 @@ export interface GetSincerityTemperatureHistoryRequest { // 성실 온도 이력 조회 API 응답 타입 export interface GetSincerityTemperatureHistoryResponse { currentSincerityTemperature: number; + sincerityTempLevel: 'FIRST' | 'SECOND' | 'THIRD' | 'FOURTH'; sincerityTemperatureHistory: { content: { reasonType: 'STUDY_REVIEW'; From b577abd41bc1b214643c57d3e1277f8457dbb8c6 Mon Sep 17 00:00:00 2001 From: aken-you Date: Sat, 11 Oct 2025 03:11:46 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20memberId=20=EB=B3=80=ED=99=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx b/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx index cb4567b9..69f6bffb 100644 --- a/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx +++ b/app/(admin)/admin/detail/[id]/sincerity-temp/page.tsx @@ -64,7 +64,7 @@ export default async function SincerityTempPage({
- +
);