[apps/cs] feat: 접속자 통계 > 접속 로그 상세 테이블 작업

This commit is contained in:
2025-07-29 15:44:47 +09:00
parent 3ed203c597
commit c222e809b6
6 changed files with 139 additions and 79 deletions
@@ -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<ConnectionLogList> {
const response = await client.get<ConnectionLogList>(
`${ENDPOINT.CMS_SERVICE.CONNECTION_LOG}`,
{ params },
);
return response.data;
}
async getConnectionStatistics(
params: ConnectionStatisticsParams,
): Promise<ConnectionStatistics> {
const response = await client.get<ConnectionStatistics>(
`${ENDPOINT.CMS_SERVICE.CONNECTION_LOG}/stat`,
{ params },
);
return response.data;
}
}
export const ConnectionLogApi = new ConnectionLogApiService();
@@ -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<ConnectionStatistics> {
const response = await client.get<ConnectionStatistics>(
`${ENDPOINT.CMS_SERVICE.CONNECTION_LOG}/stat`,
{ params },
);
return response.data;
}
}
export const UserStatisticsApi = new UserStatisticsApiService();
@@ -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: (
@@ -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<ConnectionLog>;
export interface ConnectionLogService {
getConnectionLog(params: ConnectionLogParams): Promise<ConnectionLogList>;
getConnectionStatistics(
params: ConnectionStatisticsParams,
): Promise<ConnectionStatistics>;
@@ -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<AccessLog>();
export const columns = [
columnHelper.accessor("no", {
export const columns: ColumnDef<ConnectionLog>[] = [
{
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(),
}),
},
];
@@ -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() {
<h3 className="text-2xl font-medium">일별 접속 현황</h3>
</div>
<div className="h-96 p-4">
{query.data?.dayList.length ? (
<Line {...chartData.day(query.data)} />
{connectionStatisticsData?.dayList?.length ? (
<Line {...chartData.day(connectionStatisticsData)} />
) : (
<NoData />
)}
@@ -243,8 +252,8 @@ function UserStatisticsPage() {
<h3 className="text-2xl font-medium">사이트별 접속자</h3>
</div>
<div className="h-96 p-4">
{query.data?.siteList.length ? (
<Bar {...chartData.site(query.data)} />
{connectionStatisticsData?.siteList?.length ? (
<Bar {...chartData.site(connectionStatisticsData)} />
) : (
<NoData />
)}
@@ -256,8 +265,8 @@ function UserStatisticsPage() {
<h3 className="text-2xl font-medium">브라우저별 접속자</h3>
</div>
<div className="h-96 p-4">
{query.data?.browserList.length ? (
<Doughnut {...chartData.browser(query.data)} />
{connectionStatisticsData?.browserList?.length ? (
<Doughnut {...chartData.browser(connectionStatisticsData)} />
) : (
<NoData />
)}
@@ -272,7 +281,7 @@ function UserStatisticsPage() {
<div className="flex w-full items-center justify-between">
<h3 className="text-2xl font-medium text-label">접속 로그 상세</h3>
<div className="flex gap-2">
{/* <Select
<Select
value={table.getState().pagination.pageSize.toString()}
onValueChange={(value) => {
table.setPageSize(parseInt(value));
@@ -288,11 +297,11 @@ function UserStatisticsPage() {
</SelectItem>
))}
</SelectContent>
</Select> */}
</Select>
</div>
</div>
</div>
{/* <div className="card !m-[1.2rem] !p-0">{tableComponent}</div> */}
<div className="card !m-[1.2rem] !p-0">{tableComponent}</div>
</div>
</div>
);