개인정보조회 로그그

This commit is contained in:
2025-06-27 21:55:24 +09:00
parent eaacc5293d
commit 59afe895e1
8 changed files with 324 additions and 4 deletions
@@ -0,0 +1,20 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
PrivacyLogList,
PrivacyLogParams,
PrivacyLogService,
} from "../model/privacylog-interface";
export class PrivacyLogPrivacyService implements PrivacyLogService {
public async getPrivacyLogList(params: PrivacyLogParams): Promise<PrivacyLogList> {
const response = await client.get<PrivacyLogList>(
`${ENDPOINT.USER_SERVICE.PRIVACYLOG}`,
{ params },
);
return response.data;
}
}
export const PrivacyLogPrivacy = new PrivacyLogPrivacyService();
+6
View File
@@ -0,0 +1,6 @@
export { PrivacyLogPrivacy } from "./api/privacylog-service";
export type {
PrivacyLog,
PrivacyLogParams,
} from "./model/privacylog-interface";
@@ -0,0 +1,27 @@
import type { PageableList, PageableParams } from "@/shared/model";
export interface PrivacyLogParams extends PageableParams {
siteId?: string;
rqstrIp?: string;
inqInfoKeywordType?: string;
inqInfoKeyword?: string;
inqDtStart?: string;
inqDtEnd?: string;
}
export interface PrivacyLog {
inqSn: number;
siteId: string;
siteNm: string;
inqDt: string;
rqstrIp: string;
rqstrId: string;
rqstrNm: string;
inqInfoSummary: string;
}
export type PrivacyLogList = PageableList<PrivacyLog>;
export interface PrivacyLogService {
getPrivacyLogList: (params: PrivacyLogParams) => Promise<PrivacyLogList>;
}
@@ -17,6 +17,7 @@ import {
SelectItem,
SelectTrigger,
SelectValue,
LoadingWithImage,
} from "@common/components";
import { LoginlogApi } from "@/entities/loginlog";
import { type LoginlogParams} from "@/entities/loginlog/model/loginlog-interface";
@@ -40,6 +41,7 @@ function LoginlogPage({ }) {
};
const [dateRange, setDateRange] = useState<DateRange>(defaultDateRange);
const [isManualLoading, setIsManualLoading] = useState(false);
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
@@ -89,7 +91,10 @@ function LoginlogPage({ }) {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
query.refetch();
setIsManualLoading(true);
query.refetch().finally(() => {
setIsManualLoading(false);
});
};
const handleLoginlogParamsChange =
@@ -178,8 +183,8 @@ function LoginlogPage({ }) {
<SelectItem value="Y">오류</SelectItem>
</SelectContent>
</Select>
<Button className="h-[3.2rem]" type="submit">
조회
<Button className="h-[3.2rem]" type="submit" disabled={query.isLoading || query.isPending || isManualLoading}>
{query.isLoading || query.isPending || isManualLoading ? "조회 중..." : "조회"}
</Button>
</form>
</div>
@@ -208,7 +213,19 @@ function LoginlogPage({ }) {
</div>
</div>
</div>
<div className="card !m-[1.2rem] !p-0">{tableComponent}</div>
<div className="card !m-[1.2rem] !p-0 relative">
{/* Debug: 로딩 상태 표시 */}
{(query.isLoading || query.isPending || isManualLoading) && (
<div className="absolute inset-0 bg-white/80 flex items-center justify-center z-10">
<div className="[data-theme='black']:bg-transparent rounded-lg bg-secondary p-10 w-[10rem] h-[10rem] flex items-center justify-center">
<div className="relative size-[5rem]">
<LoadingWithImage color="primary" />
</div>
</div>
</div>
)}
{tableComponent}
</div>
</div>
</div>
);
+1
View File
@@ -0,0 +1 @@
export { PrivacyLogPage } from "./ui/privacylog-page";
@@ -0,0 +1,41 @@
import dayjs from "dayjs";
import type { ColumnDef } from "@tanstack/react-table";
import type { PrivacyLog } from "@/entities/privacylog";
export const columns: ColumnDef<PrivacyLog>[] = [
{
id: "no",
header: "NO",
cell: ({ row, table }) => {
const totalRows = table.getRowCount();
const { pageIndex, pageSize } = table.getState().pagination;
return totalRows - (pageIndex * pageSize + row.index);
},
},
{
accessorKey: "inqDt",
header: "요청일시",
cell: ({ row }) => {
const date = dayjs(row.original.inqDt);
return date.format("YYYY-MM-DD HH:mm:ss");
},
},
{
accessorKey: "siteNm",
header: "사이트명",
},
{
accessorKey: "rqstrNm",
header: "조회자명",
},
{
accessorKey: "rqstrIp",
header: "조회IP",
},
{
accessorKey: "inqInfoSummary",
header: "조회정보",
}
];
@@ -0,0 +1,206 @@
"use client";
import dayjs from "dayjs";
import { useState } from "react";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type PaginationState,
} from "@tanstack/react-table";
import {
Button,
DateRangePicker,
Input,
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@common/components";
import { PrivacyLogPrivacy } from "@/entities/privacylog";
import { type PrivacyLogParams} from "@/entities/privacylog/model/privacylog-interface";
import { useDataTable } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { columns } from "@/pages/privacylog/model/table-columns";
type DateRange = {
from: Date | undefined;
to?: Date | undefined;
};
function PrivacyLogPage({ }) {
// 1주일 전부터 오늘까지 기본 날짜 설정
const defaultDateRange: DateRange = {
from: dayjs().subtract(7, "day").toDate(),
to: dayjs().toDate(),
};
const [dateRange, setDateRange] = useState<DateRange>(defaultDateRange);
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [privacylogPostQuery, setprivacylogPostQuery] = useState< PrivacyLogParams>({
siteId: "",
inqInfoKeywordType: "userName",
inqInfoKeyword: "",
rqstrIp: "",
inqDtStart: dayjs().subtract(7, "day").format("YYYY-MM-DD"),
inqDtEnd: dayjs().format("YYYY-MM-DD"),
sort:"inqDt,asc"
});
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.PRIVACYLOG, pagination],
queryFn: () =>
PrivacyLogPrivacy.getPrivacyLogList({
...privacylogPostQuery,
page: pagination.pageIndex,
size: pagination.pageSize,
}),
placeholderData: keepPreviousData,
});
const { tableComponent, tableInstance: table } = useDataTable({
query,
tableOptions: {
columns,
state: {
pagination,
},
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
},
dataTableOptions: {
viewPagination: true,
},
});
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
query.refetch();
};
const handleprivacylogParamsChange =
(key: keyof PrivacyLogParams) => (value: unknown) => {
setprivacylogPostQuery({ ...privacylogPostQuery, [key]: value });
};
const handlePresetDateRange = (value: string) => {
let presetDateRange: DateRange | undefined;
switch (value) {
case "1W":
presetDateRange = {
from: dayjs().subtract(7, "day").toDate(),
to: dayjs().toDate(),
};
break;
case "1M":
presetDateRange = {
from: dayjs().subtract(30, "day").toDate(),
to: dayjs().toDate(),
};
break;
case "3M":
presetDateRange = {
from: dayjs().subtract(90, "day").toDate(),
to: dayjs().toDate(),
};
break;
default:
presetDateRange = undefined;
}
handleDateRangeChange(presetDateRange);
};
const handleDateRangeChange = (value: DateRange | undefined) => {
setDateRange(value || defaultDateRange);
setprivacylogPostQuery({
...privacylogPostQuery,
inqDtStart: dayjs(value?.from).format("YYYY-MM-DD") ?? dayjs().subtract(7, "day").format("YYYY-MM-DD"),
inqDtEnd: dayjs(value?.to).format("YYYY-MM-DD") ?? dayjs().format("YYYY-MM-DD"),
});
};
return (
<div className="flex-1">
<div className="card card-border">
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
<Label className="mr-2">조회기간</Label>
<DateRangePicker
value={dateRange}
onValueChange={handleDateRangeChange}
>
<div className="card card-border flex items-center gap-2">
<Select onValueChange={handlePresetDateRange}>
<SelectTrigger>
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1W">일주일</SelectItem>
<SelectItem value="1M">1개월</SelectItem>
<SelectItem value="3M">3개월</SelectItem>
</SelectContent>
</Select>
</div>
</DateRangePicker>
<Label className="ml-4 mr-2 text-label">조회자명</Label>
<Input
className="h-[3.2rem] w-[20rem] rounded-sm !text-[1.3rem]"
value={privacylogPostQuery.inqInfoKeyword}
onChange={(e) =>
handleprivacylogParamsChange("inqInfoKeyword")(e.target.value)
}
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
</form>
</div>
<div className="card card-border">
<div className="card-header">
<div className="flex w-full items-center justify-between">
<h3 className="text-2xl font-medium text-label">로그인로그 목록</h3>
<div className="flex gap-2">
<Select
value={table.getState().pagination.pageSize.toString()}
onValueChange={(value) => {
table.setPageSize(parseInt(value));
}}
>
<SelectTrigger className="h-[3.2rem] w-[12rem]">
<SelectValue placeholder="페이지 크기" />
</SelectTrigger>
<SelectContent>
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={pageSize.toString()}>
{pageSize}개씩 보기
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</div>
<div className="card !m-[1.2rem] !p-0">{tableComponent}</div>
</div>
</div>
);
}
export {PrivacyLogPage };
+2
View File
@@ -21,6 +21,8 @@ export const ENDPOINT = {
ROLES: "/user-service/api/v1/roles",
LOGINLOG: "/user-service/api/v1/login-logs",
APILOG: "/user-service/api/v1/api-logs",
PRIVACYLOG: "/user-service//api/v1/prvc-inq-logs",
},
CMS_SERVICE: {
SITES: "/cms-service/api/v1/sites",