From 4e04676a57fc9c16b531aca35831304d404d2441 Mon Sep 17 00:00:00 2001
From: bicochan
Date: Tue, 29 Jul 2025 11:28:01 +0900
Subject: [PATCH 1/4] =?UTF-8?q?[apps/cs]=20chore:=20=EB=8C=80=EC=8B=9C?=
=?UTF-8?q?=EB=B3=B4=EB=93=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=8D=B0?=
=?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=91=9C=EA=B8=B0=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/cs/src/pages/dashboard/ui/dashboard-page.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx b/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx
index 4bae35f7c..b217531dd 100644
--- a/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx
+++ b/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx
@@ -118,7 +118,7 @@ function DashboardPage() {
금일 신규 가입자
{data?.memberTodayCnt
? data.memberTodayCnt.toLocaleString()
- : "-"}
+ : "0"}
명
@@ -141,7 +141,7 @@ function DashboardPage() {
금일 +
- {data?.pstTodayCnt ? data.pstTodayCnt.toLocaleString() : "-"}{" "}
+ {data?.pstTodayCnt ? data.pstTodayCnt.toLocaleString() : "0"}
등록
@@ -168,7 +168,7 @@ function DashboardPage() {
전일 대비 +
{data?.accessBefDayPer
? data.accessBefDayPer.toLocaleString()
- : "-"}
+ : "0"}
%
From 387362d42bd510b4f63404a692949b27a44136cf Mon Sep 17 00:00:00 2001
From: bicochan
Date: Tue, 29 Jul 2025 11:28:48 +0900
Subject: [PATCH 2/4] =?UTF-8?q?[apps/cs]=20feat:=20=EA=B2=8C=EC=8B=9C?=
=?UTF-8?q?=EA=B8=80=20=EB=82=B4=20=EB=AA=A9=EB=A1=9D=20=EB=B2=84=ED=8A=BC?=
=?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
apps/cs/src/widgets/post/ui/post-action.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/apps/cs/src/widgets/post/ui/post-action.tsx b/apps/cs/src/widgets/post/ui/post-action.tsx
index 421cb4542..860d8dad8 100644
--- a/apps/cs/src/widgets/post/ui/post-action.tsx
+++ b/apps/cs/src/widgets/post/ui/post-action.tsx
@@ -25,6 +25,7 @@ function PostAction({ children, handleDelete }: PostActionProps) {
const { prompt } = useSelectPrompt(selectData);
const bbsInfo = useMenuStore((state) => state.bbsInfo);
const userInfo = useUserStore((state) => state.userInfo);
+ const postPageLink = `/bbs/${bbsInfo?.bbsTypeCd.toLowerCase()}/${bbsInfo?.bbsId}`;
const onDelete = async () => {
const { resolved, value } = await prompt("삭제 사유를 입력해주세요.");
@@ -64,7 +65,7 @@ function PostAction({ children, handleDelete }: PostActionProps) {
size="lg"
color="white"
type="button"
- onClick={() => router.back()}
+ onClick={() => router.push(postPageLink)}
>
목록
From 12f7fe53ad759a518b44bfaaa479732ff8340c51 Mon Sep 17 00:00:00 2001
From: bicochan
Date: Tue, 29 Jul 2025 13:23:48 +0900
Subject: [PATCH 3/4] =?UTF-8?q?[apps/cs]=20feat:=20=EC=A0=91=EC=86=8D?=
=?UTF-8?q?=EC=9E=90=ED=86=B5=EA=B3=84=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?=
=?UTF-8?q?=EB=B0=94=EC=9D=B8=EB=94=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../cs/src/pages/user-statistics/api/index.ts | 1 -
.../api/user-statistics-service.ts | 147 ++---------
.../pages/user-statistics/model/chart-data.ts | 245 +++++++-----------
.../model/user-statistics-interface.ts | 84 +++---
.../ui/user-statistics-page.tsx | 163 +++++-------
apps/cs/src/shared/config/api.ts | 1 +
6 files changed, 220 insertions(+), 421 deletions(-)
delete mode 100644 apps/cs/src/pages/user-statistics/api/index.ts
diff --git a/apps/cs/src/pages/user-statistics/api/index.ts b/apps/cs/src/pages/user-statistics/api/index.ts
deleted file mode 100644
index 802b91e30..000000000
--- a/apps/cs/src/pages/user-statistics/api/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { UserStatisticsApi } from "./user-statistics-service";
diff --git a/apps/cs/src/pages/user-statistics/api/user-statistics-service.ts b/apps/cs/src/pages/user-statistics/api/user-statistics-service.ts
index 10c90c024..d0522267b 100644
--- a/apps/cs/src/pages/user-statistics/api/user-statistics-service.ts
+++ b/apps/cs/src/pages/user-statistics/api/user-statistics-service.ts
@@ -1,138 +1,21 @@
+import { client } from "@/shared/api";
import {
- type AccessLog,
- type AccessLogParams,
- type AccessLogService,
- type PageableList,
+ type UserStatisticsService,
+ type ConnectionStatisticsParams,
+ type ConnectionStatistics,
} from "../model/user-statistics-interface";
+import { ENDPOINT } from "@/shared/config";
-// 목 데이터
-const mockAccessLogs: AccessLog[] = [
- {
- no: 1,
- accessTime: "2024-01-15 14:30:25",
- accessIp: "192.168.1.100",
- accessRegion: "서울특별시",
- urlPath: "/index",
- browser: "Chrome",
- site: "SITE1",
- accessType: "메인",
- },
- {
- no: 2,
- accessTime: "2024-01-15 14:28:15",
- accessIp: "203.241.50.123",
- accessRegion: "부산광역시",
- urlPath: "/board/list",
- browser: "Edge",
- site: "SITE2",
- accessType: "게시판",
- },
- {
- no: 3,
- accessTime: "2024-01-15 14:25:42",
- accessIp: "175.223.45.67",
- accessRegion: "대구광역시",
- urlPath: "/statistics",
- browser: "Safari",
- site: "SITE1",
- accessType: "통계",
- },
- {
- no: 4,
- accessTime: "2024-01-15 14:22:18",
- accessIp: "211.252.87.45",
- accessRegion: "인천광역시",
- urlPath: "/contents/view",
- browser: "Firefox",
- site: "SITE3",
- accessType: "컨텐츠",
- },
- {
- no: 5,
- accessTime: "2024-01-15 14:20:33",
- accessIp: "121.254.12.89",
- accessRegion: "광주광역시",
- urlPath: "/search",
- browser: "Chrome",
- site: "SITE1",
- accessType: "검색",
- },
-];
-
-// 목 API 서비스
-class UserStatisticsApiImpl implements AccessLogService {
- async getAccessLogList(
- params: AccessLogParams,
- ): Promise> {
- // 실제 API 호출 대신 목 데이터 반환
- const { page = 0, size = 10 } = params;
-
- // 필터링 로직 (실제로는 서버에서 처리)
- let filteredLogs = [...mockAccessLogs];
-
- if (params.urlPath) {
- filteredLogs = filteredLogs.filter((log) =>
- log.urlPath.toLowerCase().includes(params.urlPath!.toLowerCase()),
- );
- }
-
- if (params.accessIp) {
- filteredLogs = filteredLogs.filter((log) =>
- log.accessIp.includes(params.accessIp!),
- );
- }
-
- if (params.accessRegion) {
- filteredLogs = filteredLogs.filter((log) =>
- log.accessRegion.includes(params.accessRegion!),
- );
- }
-
- if (params.browser) {
- filteredLogs = filteredLogs.filter(
- (log) => log.browser.toLowerCase() === params.browser!.toLowerCase(),
- );
- }
-
- if (params.siteId) {
- filteredLogs = filteredLogs.filter((log) => log.site === params.siteId);
- }
-
- // 페이지네이션
- const start = page * size;
- const end = start + size;
- const paginatedLogs = filteredLogs.slice(start, end);
-
- // 목 응답 생성
- return {
- content: paginatedLogs,
- pageable: {
- pageNumber: page,
- pageSize: size,
- sort: {
- empty: true,
- sorted: false,
- unsorted: true,
- },
- offset: page * size,
- paged: true,
- unpaged: false,
- },
- last: (page + 1) * size >= filteredLogs.length,
- totalPages: Math.ceil(filteredLogs.length / size),
- totalElements: filteredLogs.length,
- size,
- number: page,
- sort: {
- empty: true,
- sorted: false,
- unsorted: true,
- },
- first: page === 0,
- numberOfElements: paginatedLogs.length,
- empty: paginatedLogs.length === 0,
- };
+class UserStatisticsApiService implements UserStatisticsService {
+ async getConnectionStatistics(
+ params: ConnectionStatisticsParams,
+ ): Promise {
+ const response = await client.get(
+ `${ENDPOINT.CMS_SERVICE.CONNECTION_LOG}/stat`,
+ { params },
+ );
+ return response.data;
}
}
-export const UserStatisticsApi = new UserStatisticsApiImpl();
+export const UserStatisticsApi = new UserStatisticsApiService();
diff --git a/apps/cs/src/pages/user-statistics/model/chart-data.ts b/apps/cs/src/pages/user-statistics/model/chart-data.ts
index c836a4e5d..8c4241cfd 100644
--- a/apps/cs/src/pages/user-statistics/model/chart-data.ts
+++ b/apps/cs/src/pages/user-statistics/model/chart-data.ts
@@ -1,148 +1,103 @@
-import { type ChartData, type ChartOptions } from "chart.js";
-
-// 일별 접속 현황 차트 데이터
-export const monthlyStatisticsData = {
- labels: [
- "1일",
- "2일",
- "3일",
- "4일",
- "5일",
- "6일",
- "7일",
- "8일",
- "9일",
- "10일",
- "11일",
- "12일",
- "13일",
- "14일",
- "15일",
- ],
- datasets: [
- {
- type: "bar" as const,
- label: "페이지뷰",
- data: [
- 850, 1000, 750, 750, 700, 750, 780, 800, 850, 900, 1200, 1700, 2300,
- 2100, 2600,
- ],
- backgroundColor: "rgba(54, 162, 235, 0.8)",
- order: 2,
- },
- {
- type: "line" as const,
- label: "신규 접속자",
- data: [
- 700, 600, 500, 550, 500, 550, 500, 550, 600, 850, 1200, 1500, 1700,
- 2300, 2200,
- ],
- borderColor: "rgba(255, 99, 132, 0.8)",
- borderWidth: 2,
- tension: 0.4,
- pointRadius: 3,
- order: 0,
- },
- {
- type: "line" as const,
- label: "총 접속자",
- data: [
- 200, 300, 400, 500, 600, 500, 550, 500, 400, 500, 350, 800, 900, 1200,
- 1300,
- ],
- borderColor: "rgba(75, 192, 192, 0.8)",
- borderWidth: 2,
- tension: 0.4,
- pointRadius: 3,
- order: 1,
- },
- ],
-};
-
-export const monthlyStatisticsOptions: ChartOptions<"bar"> = {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- position: "top" as const,
- },
- tooltip: {
- mode: "index" as const,
- intersect: false,
- },
- },
-};
-
-// 사이트별 접속자 차트 데이터
-export const siteVisitorsData: ChartData<"bar"> = {
- labels: ["SITE1", "SITE2", "SITE3", "SITE4", "SITE5"],
- datasets: [
- {
- label: "사이트별 접속자",
- data: [2500, 2000, 1500, 1200, 800],
- backgroundColor: [
- "rgba(54, 162, 235, 0.8)",
- "rgba(153, 102, 255, 0.8)",
- "rgba(255, 99, 132, 0.8)",
- "rgba(255, 159, 64, 0.8)",
- "rgba(255, 205, 86, 0.8)",
- ],
- borderWidth: 1,
- },
- ],
-};
-
-export const siteVisitorsOptions: ChartOptions<"bar"> = {
- responsive: true,
- maintainAspectRatio: false,
- indexAxis: "y" as const,
- plugins: {
- legend: {
- display: false,
- },
- },
-};
-
-// 브라우저별 접속자 차트 데이터
-export const browserStatisticsData: ChartData<"doughnut"> = {
- labels: ["Chrome", "Edge", "Safari", "Firefox", "Internet Explorer"],
- datasets: [
- {
- data: [45, 25, 15, 10, 5],
- backgroundColor: [
- "rgba(54, 162, 235, 0.8)",
- "rgba(153, 102, 255, 0.8)",
- "rgba(255, 99, 132, 0.8)",
- "rgba(255, 159, 64, 0.8)",
- "rgba(255, 205, 86, 0.8)",
- ],
- borderColor: [
- "rgba(54, 162, 235, 1)",
- "rgba(153, 102, 255, 1)",
- "rgba(255, 99, 132, 1)",
- "rgba(255, 159, 64, 1)",
- "rgba(255, 205, 86, 1)",
- ],
- borderWidth: 1,
- },
- ],
-};
-
-export const browserStatisticsOptions: ChartOptions<"doughnut"> = {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- position: "bottom" as const,
- },
- },
-};
+import type { ChartData, ChartOptions } from "chart.js";
+import { getCssVariableValue } from "@common/components";
+import type { ConnectionStatistics } from "./user-statistics-interface";
export const chartData = {
- monthlyStatisticsData,
- monthlyStatisticsOptions,
- siteVisitorsData,
- siteVisitorsOptions,
- browserStatisticsData,
- browserStatisticsOptions,
+ day: (
+ data: ConnectionStatistics,
+ ): {
+ data: ChartData<"line">;
+ options: ChartOptions<"line">;
+ } => {
+ return {
+ data: {
+ labels: data.dayList.map((item) => item.dayYmd),
+ datasets: [
+ {
+ type: "line",
+ label: "접속자 수",
+ data: data.dayList.map((item) => item.accessCnt),
+ borderColor: getCssVariableValue("--chart-1"),
+ },
+ ],
+ },
+ options: {
+ maintainAspectRatio: false,
+ scales: {
+ y: {
+ ticks: {
+ precision: 0,
+ },
+ },
+ },
+ },
+ };
+ },
+ site: (
+ data: ConnectionStatistics,
+ ): {
+ data: ChartData<"bar">;
+ options: ChartOptions<"bar">;
+ } => {
+ return {
+ data: {
+ labels: data.siteList.map((item) => item.siteNm),
+ datasets: [
+ {
+ type: "bar",
+ label: "사이트별 접속자",
+ data: data.siteList.map((item) => item.accessCnt),
+ backgroundColor: data.siteList.map((_, index) =>
+ getCssVariableValue(`--chart-${(index % 5) + 1}`),
+ ),
+ },
+ ],
+ },
+ options: {
+ maintainAspectRatio: false,
+ indexAxis: "y",
+ scales: {
+ x: {
+ ticks: {
+ precision: 0,
+ },
+ },
+ },
+ plugins: {
+ legend: {
+ display: false,
+ },
+ },
+ },
+ };
+ },
+ browser: (
+ data: ConnectionStatistics,
+ ): {
+ data: ChartData<"doughnut">;
+ options: ChartOptions<"doughnut">;
+ } => {
+ return {
+ data: {
+ labels: data.browserList.map((item) => item.wbsrKndNm),
+ datasets: [
+ {
+ type: "doughnut",
+ data: data.browserList.map((item) => item.accessCnt),
+ backgroundColor: data.browserList.map((_, index) =>
+ getCssVariableValue(`--chart-${(index % 5) + 1}`),
+ ),
+ },
+ ],
+ },
+ options: {
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ position: "bottom",
+ },
+ },
+ },
+ };
+ },
};
diff --git a/apps/cs/src/pages/user-statistics/model/user-statistics-interface.ts b/apps/cs/src/pages/user-statistics/model/user-statistics-interface.ts
index 124d91044..42d247185 100644
--- a/apps/cs/src/pages/user-statistics/model/user-statistics-interface.ts
+++ b/apps/cs/src/pages/user-statistics/model/user-statistics-interface.ts
@@ -1,56 +1,40 @@
-export interface AccessLog {
- no: number;
- accessTime: string;
- accessIp: string;
- accessRegion: string;
- urlPath: string;
- browser: string;
- site: string;
- accessType: string;
-}
-
-export interface AccessLogParams {
- page?: number;
- size?: number;
- urlPath?: string;
- accessIp?: string;
- accessRegion?: string;
- browser?: string;
- accessDateStart?: string;
- accessDateEnd?: string;
+export interface ConnectionStatisticsParams {
+ keywordType?: string;
+ keyword?: string;
siteId?: string;
- sort?: string;
+ cntnIp?: string;
+ cntnRgn?: string;
+ wbsrKnd?: string;
+ urlPath?: string;
+ cntnDtStart?: string;
+ cntnDtEnd?: string;
}
-export interface AccessLogService {
- getAccessLogList(params: AccessLogParams): Promise>;
+interface DayConnectionStatistics {
+ dayYmd: string;
+ accessCnt: number;
}
-export interface PageableList {
- content: T[];
- pageable: {
- pageNumber: number;
- pageSize: number;
- sort: {
- empty: boolean;
- sorted: boolean;
- unsorted: boolean;
- };
- offset: number;
- paged: boolean;
- unpaged: boolean;
- };
- last: boolean;
- totalPages: number;
- totalElements: number;
- size: number;
- number: number;
- sort: {
- empty: boolean;
- sorted: boolean;
- unsorted: boolean;
- };
- first: boolean;
- numberOfElements: number;
- empty: boolean;
+interface SiteConnectionStatistics {
+ siteId: string;
+ siteNm: string;
+ accessCnt: number;
+}
+
+interface BrowserConnectionStatistics {
+ wbsrKnd: string;
+ wbsrKndNm: string;
+ accessCnt: number;
+}
+
+export interface ConnectionStatistics {
+ dayList: DayConnectionStatistics[];
+ siteList: SiteConnectionStatistics[];
+ browserList: BrowserConnectionStatistics[];
+}
+
+export interface UserStatisticsService {
+ getConnectionStatistics(
+ params: ConnectionStatisticsParams,
+ ): Promise;
}
diff --git a/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx b/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx
index 9fedb47f4..4fe1d3fc0 100644
--- a/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx
+++ b/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx
@@ -15,9 +15,8 @@ import {
Filler,
Legend,
Tooltip,
- type ChartData,
} from "chart.js";
-import { Bar, Doughnut } from "react-chartjs-2";
+import { Bar, Doughnut, Line } from "react-chartjs-2";
import {
Button,
Input,
@@ -30,11 +29,13 @@ import {
} from "@common/components";
import { SiteSelect } from "@/entities/site";
import { useDataTable } from "@/shared/lib";
+import { ENDPOINT } from "@/shared/config";
+import { NoData } from "@/shared/ui/no-data";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
-import { UserStatisticsApi } from "../api";
+import { UserStatisticsApi } from "../api/user-statistics-service";
import { chartData } from "../model/chart-data";
import { columns } from "../model/table-columns";
-import { type AccessLogParams } from "../model/user-statistics-interface";
+import { type ConnectionStatisticsParams } from "../model/user-statistics-interface";
ChartJS.register(
CategoryScale,
@@ -65,44 +66,41 @@ function UserStatisticsPage() {
});
const [keywordType, setKeywordType] = useState("");
const [keyword, setKeyword] = useState("");
+ const [siteId, setSiteId] = useState("");
const [presetDateRange, setPresetDateRange] = useState("1W");
const [dateRange, setDateRange] = useState(defaultDateRange);
- const [searchParams, setSearchParams] = useState({
- urlPath: "",
- accessIp: "",
- accessRegion: "",
- browser: "",
- accessDateStart: dayjs().subtract(7, "day").format("YYYY-MM-DD"),
- accessDateEnd: dayjs().format("YYYY-MM-DD"),
- sort: "accessTime,desc",
+ const [searchParams, setSearchParams] = useState({
+ keywordType: "",
+ keyword: "",
+ siteId: "",
+ cntnDtStart: dayjs(defaultDateRange.from).format("YYYY-MM-DD"),
+ cntnDtEnd: dayjs(defaultDateRange.to).format("YYYY-MM-DD"),
});
const query = useQuery({
- queryKey: ["user-statistics", searchParams, pagination],
+ queryKey: [ENDPOINT.CMS_SERVICE.CONNECTION_LOG, searchParams],
queryFn: () =>
- UserStatisticsApi.getAccessLogList({
+ UserStatisticsApi.getConnectionStatistics({
...searchParams,
- page: pagination.pageIndex,
- size: pagination.pageSize,
}),
placeholderData: keepPreviousData,
});
- const { tableComponent, tableInstance: table } = useDataTable({
- query,
- tableOptions: {
- columns,
- state: {
- pagination,
- },
- manualPagination: true,
- getCoreRowModel: getCoreRowModel(),
- onPaginationChange: setPagination,
- },
- dataTableOptions: {
- viewPagination: true,
- },
- });
+ // const { tableComponent, tableInstance: table } = useDataTable({
+ // query,
+ // tableOptions: {
+ // columns,
+ // state: {
+ // pagination,
+ // },
+ // manualPagination: true,
+ // getCoreRowModel: getCoreRowModel(),
+ // onPaginationChange: setPagination,
+ // },
+ // dataTableOptions: {
+ // viewPagination: true,
+ // },
+ // });
const handlePresetDateRange = (value: string) => {
let presetDateRange: DateRange;
@@ -139,54 +137,30 @@ function UserStatisticsPage() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
- setPagination({
- ...pagination,
- pageIndex: 0,
- });
-
- // 검색 조건에 따라 검색 파라미터 설정
- const params: AccessLogParams = {
- sort: "accessTime,desc",
- accessDateStart: dateRange.from
+ const params: ConnectionStatisticsParams = {
+ keywordType,
+ keyword,
+ siteId,
+ cntnDtStart: dateRange.from
? dayjs(dateRange.from).format("YYYY-MM-DD")
: "",
- accessDateEnd: dateRange.to
- ? dayjs(dateRange.to).format("YYYY-MM-DD")
- : "",
+ cntnDtEnd: dateRange.to ? dayjs(dateRange.to).format("YYYY-MM-DD") : "",
};
-
- // 검색어 타입에 따라 파라미터 설정
- switch (keywordType) {
- case "urlPath":
- params.urlPath = keyword;
- break;
- case "ip":
- params.accessIp = keyword;
- break;
- case "region":
- params.accessRegion = keyword;
- break;
- case "browser":
- params.browser = keyword;
- break;
- }
-
setSearchParams(params);
};
const handleReset = () => {
setKeywordType("");
setKeyword("");
+ setSiteId("");
setDateRange(defaultDateRange);
setPresetDateRange("1W");
setSearchParams({
- urlPath: "",
- accessIp: "",
- accessRegion: "",
- browser: "",
- accessDateStart: dayjs().subtract(7, "day").format("YYYY-MM-DD"),
- accessDateEnd: dayjs().format("YYYY-MM-DD"),
- sort: "accessTime,desc",
+ keywordType: "",
+ keyword: "",
+ siteId: "",
+ cntnDtStart: dayjs(defaultDateRange.from).format("YYYY-MM-DD"),
+ cntnDtEnd: dayjs(defaultDateRange.to).format("YYYY-MM-DD"),
});
};
@@ -210,7 +184,11 @@ function UserStatisticsPage() {
setDateRange={handleDateRangeChange}
/>
-
+ setSiteId(value)}
+ />
- {/* 차트 영역 */}
- {/* 일별 접속 현황 */}
-
일별 접속 현황
+ 일별 접속 현황
- }
- options={chartData.monthlyStatisticsOptions}
- />
+ {query.data?.dayList.length ? (
+
+ ) : (
+
+ )}
- {/* 사이트별 접속자 */}
-
사이트별 접속자
+ 사이트별 접속자
-
+ {query.data?.siteList.length ? (
+
+ ) : (
+
+ )}
- {/* 브라우저별 접속자 */}
-
브라우저별 접속자
+ 브라우저별 접속자
-
+ {query.data?.browserList.length ? (
+
+ ) : (
+
+ )}
@@ -295,7 +272,7 @@ function UserStatisticsPage() {
접속 로그 상세
-
+ */}
- {tableComponent}
+ {/* {tableComponent}
*/}
);
diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts
index 6db62f1a0..ce6f61381 100644
--- a/apps/cs/src/shared/config/api.ts
+++ b/apps/cs/src/shared/config/api.ts
@@ -55,6 +55,7 @@ export const ENDPOINT = {
MANAGER_IP: "/cms-service/api/v1/mgrip",
APILOG: "/cms-service/api/v1/api-logs",
SITE_AUTHORITY: "/cms-service/api/v1/site-authrt",
+ CONNECTION_LOG: "/cms-service/api/v1/cntnlog",
},
BOARD_SERVICE: {
BBS: "/board-service/api/v1/bbs",
From c222e809b68e0eabc386ed573199ba5e4313c959 Mon Sep 17 00:00:00 2001
From: bicochan
Date: Tue, 29 Jul 2025 15:44:47 +0900
Subject: [PATCH 4/4] =?UTF-8?q?[apps/cs]=20feat:=20=EC=A0=91=EC=86=8D?=
=?UTF-8?q?=EC=9E=90=20=ED=86=B5=EA=B3=84=20>=20=EC=A0=91=EC=86=8D=20?=
=?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=83=81=EC=84=B8=20=ED=85=8C=EC=9D=B4?=
=?UTF-8?q?=EB=B8=94=20=EC=9E=91=EC=97=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../api/connection-log-service.ts | 33 ++++++++++
.../api/user-statistics-service.ts | 21 ------
.../pages/user-statistics/model/chart-data.ts | 2 +-
...terface.ts => connection-log-interface.ts} | 34 +++++++++-
.../user-statistics/model/table-columns.ts | 65 ++++++++++---------
.../ui/user-statistics-page.tsx | 63 ++++++++++--------
6 files changed, 139 insertions(+), 79 deletions(-)
create mode 100644 apps/cs/src/pages/user-statistics/api/connection-log-service.ts
delete mode 100644 apps/cs/src/pages/user-statistics/api/user-statistics-service.ts
rename apps/cs/src/pages/user-statistics/model/{user-statistics-interface.ts => connection-log-interface.ts} (50%)
diff --git a/apps/cs/src/pages/user-statistics/api/connection-log-service.ts b/apps/cs/src/pages/user-statistics/api/connection-log-service.ts
new file mode 100644
index 000000000..0e2daeb5b
--- /dev/null
+++ b/apps/cs/src/pages/user-statistics/api/connection-log-service.ts
@@ -0,0 +1,33 @@
+import { client } from "@/shared/api";
+import { ENDPOINT } from "@/shared/config";
+import {
+ type ConnectionLogParams,
+ type ConnectionLogList,
+ type ConnectionStatisticsParams,
+ type ConnectionStatistics,
+ type ConnectionLogService,
+} from "../model/connection-log-interface";
+
+class ConnectionLogApiService implements ConnectionLogService {
+ async getConnectionLog(
+ params: ConnectionLogParams,
+ ): Promise {
+ const response = await client.get(
+ `${ENDPOINT.CMS_SERVICE.CONNECTION_LOG}`,
+ { params },
+ );
+ return response.data;
+ }
+
+ async getConnectionStatistics(
+ params: ConnectionStatisticsParams,
+ ): Promise {
+ const response = await client.get(
+ `${ENDPOINT.CMS_SERVICE.CONNECTION_LOG}/stat`,
+ { params },
+ );
+ return response.data;
+ }
+}
+
+export const ConnectionLogApi = new ConnectionLogApiService();
diff --git a/apps/cs/src/pages/user-statistics/api/user-statistics-service.ts b/apps/cs/src/pages/user-statistics/api/user-statistics-service.ts
deleted file mode 100644
index d0522267b..000000000
--- a/apps/cs/src/pages/user-statistics/api/user-statistics-service.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { client } from "@/shared/api";
-import {
- type UserStatisticsService,
- type ConnectionStatisticsParams,
- type ConnectionStatistics,
-} from "../model/user-statistics-interface";
-import { ENDPOINT } from "@/shared/config";
-
-class UserStatisticsApiService implements UserStatisticsService {
- async getConnectionStatistics(
- params: ConnectionStatisticsParams,
- ): Promise {
- const response = await client.get(
- `${ENDPOINT.CMS_SERVICE.CONNECTION_LOG}/stat`,
- { params },
- );
- return response.data;
- }
-}
-
-export const UserStatisticsApi = new UserStatisticsApiService();
diff --git a/apps/cs/src/pages/user-statistics/model/chart-data.ts b/apps/cs/src/pages/user-statistics/model/chart-data.ts
index 8c4241cfd..455dcac56 100644
--- a/apps/cs/src/pages/user-statistics/model/chart-data.ts
+++ b/apps/cs/src/pages/user-statistics/model/chart-data.ts
@@ -1,6 +1,6 @@
import type { ChartData, ChartOptions } from "chart.js";
import { getCssVariableValue } from "@common/components";
-import type { ConnectionStatistics } from "./user-statistics-interface";
+import type { ConnectionStatistics } from "./connection-log-interface";
export const chartData = {
day: (
diff --git a/apps/cs/src/pages/user-statistics/model/user-statistics-interface.ts b/apps/cs/src/pages/user-statistics/model/connection-log-interface.ts
similarity index 50%
rename from apps/cs/src/pages/user-statistics/model/user-statistics-interface.ts
rename to apps/cs/src/pages/user-statistics/model/connection-log-interface.ts
index 42d247185..6fbf4dc15 100644
--- a/apps/cs/src/pages/user-statistics/model/user-statistics-interface.ts
+++ b/apps/cs/src/pages/user-statistics/model/connection-log-interface.ts
@@ -1,3 +1,32 @@
+import type { PageableList, PageableParams } from "@/shared/model";
+
+export interface ConnectionLogParams extends PageableParams {
+ keywordType?: string;
+ keyword?: string;
+ siteId?: string;
+ cntnIp?: string;
+ cntnRgn?: string;
+ wbsrKnd?: string;
+ urlPath?: string;
+ cntnDtStart?: string;
+ cntnDtEnd?: string;
+}
+
+export interface ConnectionLog {
+ cntnSn: number;
+ cntnDt: string;
+ cntnId: string;
+ cntnIp: string;
+ cntnRgn: string;
+ wbsrKnd: string;
+ menuCd: number;
+ siteId: string;
+ urlPath: string;
+ siteNm: string;
+ menuNm: string;
+ wbsrKndNm: string;
+}
+
export interface ConnectionStatisticsParams {
keywordType?: string;
keyword?: string;
@@ -33,7 +62,10 @@ export interface ConnectionStatistics {
browserList: BrowserConnectionStatistics[];
}
-export interface UserStatisticsService {
+export type ConnectionLogList = PageableList;
+
+export interface ConnectionLogService {
+ getConnectionLog(params: ConnectionLogParams): Promise;
getConnectionStatistics(
params: ConnectionStatisticsParams,
): Promise;
diff --git a/apps/cs/src/pages/user-statistics/model/table-columns.ts b/apps/cs/src/pages/user-statistics/model/table-columns.ts
index c0d53380e..09fe58adf 100644
--- a/apps/cs/src/pages/user-statistics/model/table-columns.ts
+++ b/apps/cs/src/pages/user-statistics/model/table-columns.ts
@@ -1,39 +1,46 @@
-import { createColumnHelper } from "@tanstack/react-table";
-import { type AccessLog } from "./user-statistics-interface";
+import type { ColumnDef } from "@tanstack/react-table";
+import type { ConnectionLog } from "./connection-log-interface";
+import dayjs from "dayjs";
-const columnHelper = createColumnHelper();
-
-export const columns = [
- columnHelper.accessor("no", {
+export const columns: ColumnDef[] = [
+ {
header: "No",
- cell: (info) => info.getValue(),
- }),
- columnHelper.accessor("accessTime", {
+ cell: ({ row, table }) => {
+ const totalRows = table.getRowCount();
+ const currentPage = table.getState().pagination.pageIndex;
+ const pageSize = table.getState().pagination.pageSize;
+ return totalRows - (currentPage * pageSize + row.index);
+ },
+ },
+ {
+ accessorKey: "cntnDt",
header: "접속일시",
- cell: (info) => info.getValue(),
- }),
- columnHelper.accessor("accessIp", {
+ cell: ({ row }) => {
+ return dayjs(row.original.cntnDt).format("YYYY-MM-DD HH:mm:ss");
+ },
+ },
+ {
+ accessorKey: "cntnIp",
header: "접속IP",
- cell: (info) => info.getValue(),
- }),
- columnHelper.accessor("accessRegion", {
+ },
+ {
+ accessorKey: "cntnRgn",
header: "접속지역",
- cell: (info) => info.getValue(),
- }),
- columnHelper.accessor("urlPath", {
+ },
+ {
+ accessorKey: "urlPath",
header: "URL경로",
- cell: (info) => info.getValue(),
- }),
- columnHelper.accessor("browser", {
+ },
+ {
+ accessorKey: "wbsrKndNm",
header: "브라우저",
- cell: (info) => info.getValue(),
- }),
- columnHelper.accessor("site", {
+ },
+ {
+ accessorKey: "siteNm",
header: "사이트",
- cell: (info) => info.getValue(),
- }),
- columnHelper.accessor("accessType", {
+ },
+ {
+ accessorKey: "menuNm",
header: "메뉴명",
- cell: (info) => info.getValue(),
- }),
+ },
];
diff --git a/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx b/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx
index 4fe1d3fc0..801ca5eb0 100644
--- a/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx
+++ b/apps/cs/src/pages/user-statistics/ui/user-statistics-page.tsx
@@ -32,10 +32,10 @@ import { useDataTable } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { NoData } from "@/shared/ui/no-data";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
-import { UserStatisticsApi } from "../api/user-statistics-service";
+import { ConnectionLogApi } from "../api/connection-log-service";
import { chartData } from "../model/chart-data";
import { columns } from "../model/table-columns";
-import { type ConnectionStatisticsParams } from "../model/user-statistics-interface";
+import type { ConnectionStatisticsParams } from "../model/connection-log-interface";
ChartJS.register(
CategoryScale,
@@ -80,27 +80,36 @@ function UserStatisticsPage() {
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CONNECTION_LOG, searchParams],
queryFn: () =>
- UserStatisticsApi.getConnectionStatistics({
+ ConnectionLogApi.getConnectionLog({
...searchParams,
}),
placeholderData: keepPreviousData,
});
- // const { tableComponent, tableInstance: table } = useDataTable({
- // query,
- // tableOptions: {
- // columns,
- // state: {
- // pagination,
- // },
- // manualPagination: true,
- // getCoreRowModel: getCoreRowModel(),
- // onPaginationChange: setPagination,
- // },
- // dataTableOptions: {
- // viewPagination: true,
- // },
- // });
+ const { data: connectionStatisticsData } = useQuery({
+ queryKey: [ENDPOINT.CMS_SERVICE.CONNECTION_LOG, searchParams],
+ queryFn: () =>
+ ConnectionLogApi.getConnectionStatistics({
+ ...searchParams,
+ }),
+ placeholderData: keepPreviousData,
+ });
+
+ const { tableComponent, tableInstance: table } = useDataTable({
+ query,
+ tableOptions: {
+ columns,
+ state: {
+ pagination,
+ },
+ manualPagination: true,
+ getCoreRowModel: getCoreRowModel(),
+ onPaginationChange: setPagination,
+ },
+ dataTableOptions: {
+ viewPagination: true,
+ },
+ });
const handlePresetDateRange = (value: string) => {
let presetDateRange: DateRange;
@@ -229,8 +238,8 @@ function UserStatisticsPage() {
일별 접속 현황
- {query.data?.dayList.length ? (
-
+ {connectionStatisticsData?.dayList?.length ? (
+
) : (
)}
@@ -243,8 +252,8 @@ function UserStatisticsPage() {
사이트별 접속자
- {query.data?.siteList.length ? (
-
+ {connectionStatisticsData?.siteList?.length ? (
+
) : (
)}
@@ -256,8 +265,8 @@ function UserStatisticsPage() {
브라우저별 접속자
- {query.data?.browserList.length ? (
-
+ {connectionStatisticsData?.browserList?.length ? (
+
) : (
)}
@@ -272,7 +281,7 @@ function UserStatisticsPage() {
접속 로그 상세
- {/* */}
+
- {/* {tableComponent}
*/}
+ {tableComponent}
);