로그인로그

This commit is contained in:
2025-06-26 15:51:53 +09:00
parent f24fd02cf2
commit ec12b35464
7 changed files with 59 additions and 339 deletions
+5
View File
@@ -0,0 +1,5 @@
export { LoginlogApiService } from "./api/loginlog-service";
export type {
Loginlog,
LoginlogParams,
} from "./model/loginlog-interface";
@@ -1,4 +1,3 @@
import { z } from "zod";
import type { PageableList, PageableParams } from "@/shared/model";
export interface LoginlogParams extends PageableParams {
@@ -1,9 +1,7 @@
import dayjs from "dayjs";
import { useState } from "react";
import type { ColumnDef } from "@tanstack/react-table";
import { Button } from "@common/components";
import type { UpCode } from "@/entities/code";
import { LoginlogDialog } from "../ui/loginlog-dialog";
import type { Loginlog } from "@/entities/loginlog";
export const columns: ColumnDef<Loginlog>[] = [
{
@@ -16,49 +14,36 @@ export const columns: ColumnDef<Loginlog>[] = [
},
},
{
accessorKey: "upCd",
header: "상위코드",
},
{
accessorKey: "upCdNm",
header: "상위코드명",
},
{
accessorKey: "cdSe",
header: "코드구분",
},
{
accessorKey: "useYn",
header: "사용여부",
},
{
accessorKey: "regDt",
accessorKey: "lgnDt",
header: "등록일",
cell: ({ row }) => {
const date = dayjs(row.original.regDt);
return date.format("YYYY-MM-DD");
const date = dayjs(row.original.lgnDt);
return date.format("YYYY-MM-DD HH24:mi:ss");
},
},
{
accessorKey: "userId",
header: "사용자ID",
},
{
id: "actions",
header: "관리",
cell: ({ row }) => (
<div onClick={(e) => e.stopPropagation()}>
<LoginlogButton upCd={row.original.upCd} />
</div>
),
accessorKey: "로그인IP",
header: "remoteIp",
},
{
accessorKey: "lgnMthd",
header: "접속방식",
},
{
accessorKey: "오류여부",
header: "errOcrnYn",
},
{
accessorKey: "오류코드",
header: "lgnErrCd",
},
{
accessorKey: "사이트ID",
header: "siteId",
}
];
const LoginlogButton = ({ upCd }: { upCd: string }) => {
const [dialogOpen, setDialogOpen] = useState(false);
return (
<LoginlogDialog
upCd={upCd}
open={dialogOpen}
onOpenChange={setDialogOpen}
>
<Button size="sm">상세</Button>
</LoginlogDialog>
);
};
@@ -1,280 +0,0 @@
import { memo } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Input,
RadioGroup,
RadioGroupItem,
Label,
DialogTrigger,
} from "@common/components";
import {
CodeSelect,
UpCodeApi,
upCodeFormSchema,
type UpCodeFormData,
} from "@/entities/code";
import { SiteSelect } from "@/entities/site";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
import { Form, FormField, FormItem, FormControl } from "@/shared/ui/form";
type LoginlogDialogProps = {
upCd?: string;
children?: React.ReactNode;
open?: boolean;
onOpenChange?: (open: boolean) => void;
};
const LoginlogDialog = memo(
({
upCd,
children,
open,
onOpenChange,
...props
}: LoginlogDialogProps) => {
const isRegistration = !upCd;
const { confirm } = useConfirm();
const alert = useAlertStore((state) => state.setMessage);
const { data: detailData } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.UP_CODE, upCd],
queryFn: () => UpCodeApi.upCodeDetail(upCd!),
enabled: open && !!upCd,
});
const { mutateAsync: createUpCode } = useMutationWrapper({
mutationFn: (data: UpCodeFormData) => UpCodeApi.createUpCode(data),
onSuccess: () => {
alert("등록되었습니다.", () => onOpenChange?.(false));
},
});
const { mutateAsync: updateUpCode } = useMutationWrapper({
mutationFn: (data: UpCodeFormData) => UpCodeApi.updateUpCode(upCd!, data),
onSuccess: () => {
alert("수정되었습니다.", () => onOpenChange?.(false));
},
});
const form = useForm<UpCodeFormData>({
resolver: zodResolver(upCodeFormSchema),
values: {
upCd: detailData?.upCd ?? "",
upCdNm: detailData?.upCdNm ?? "",
cdSe: detailData?.cdSe ?? "",
siteId: detailData?.siteId ?? "",
upCdExpln: detailData?.upCdExpln ?? "",
useYn: detailData?.useYn ?? "Y",
delYn: "N",
},
});
const onSubmit =
(callback: (data: UpCodeFormData) => void) =>
async (data: UpCodeFormData) => {
const confirmMessage = isRegistration
? "등록하시겠습니까?"
: "수정하시겠습니까?";
if (await confirm(confirmMessage)) callback(data);
};
return (
<Dialog open={open} onOpenChange={onOpenChange} {...props}>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="max-w-[60rem]" aria-describedby={undefined}>
<DialogHeader>
<DialogTitle>
{isRegistration ? "상위코드 등록" : "상위코드 관리"}
</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(
onSubmit(isRegistration ? createUpCode : updateUpCode),
)}
className="space-y-4"
>
<Table variant="secondary">
<colgroup>
<col width="15%" />
<col width="85%" />
</colgroup>
<TableBody>
<TableRow>
<TableHead>상위코드</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="upCd"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="상위코드를를 입력해주세요"
disabled={!isRegistration}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>상위코드명</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="upCdNm"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="상위코드명을 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>상위코드설명</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="upCdExpln"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="상위코드설명을 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>코드구분</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="cdSe"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormControl>
<CodeSelect
upCd="CODECL"
value={field.value || ""}
onValueChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>사이트</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="siteId"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormControl>
<SiteSelect
value={field.value || ""}
onValueChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>사용여부</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="useYn"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="useYn"
onValueChange={field.onChange}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="useY" />
<Label htmlFor="useY">사용</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="N" id="useN" />
<Label htmlFor="useN">사용안함</Label>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
<DialogFooter className="!justify-center">
<Button size="lg" type="submit">
{isRegistration ? "등록" : "저장"}
</Button>
<DialogClose asChild>
<Button size="lg" color="white">
취소
</Button>
</DialogClose>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
},
(prevProps, nextProps) => {
return (
prevProps.upCd === nextProps.upCd && prevProps.open === nextProps.open
);
},
);
LoginlogDialog.displayName = "LoginlogDialog";
export { LoginlogDialog };
@@ -8,19 +8,17 @@ import {
type PaginationState,
} from "@tanstack/react-table";
import { Button, DataTable, Input, Label } from "@common/components";
import { LoginlogApi} from "../api/loginlog-service";
import { type LoginlogParams} from "../model/loginlog-interface";
import { LoginlogApi} from "../../../entities/loginlog/api/loginlog-service";
import { type LoginlogParams} from "../../../entities/loginlog/model/loginlog-interface";
import { ENDPOINT } from "@/shared/config";
import { columns } from "../model/table-columns";
import { LoginlogDialog } from "./loginlog-dialog";
type LoginlogPageProps = {
lgnSn?: string;
};
function LoginlogPage({ lgnSn }: LoginlogPageProps) {
const [dialogOpen, setDialogOpen] = useState(false);
function LoginlogPage({ }: LoginlogPageProps) {
const [loginlogQuery, setLoginlogQuery] = useState<LoginlogParams>({
userId: "",
siteId: "",
@@ -31,16 +29,14 @@ function LoginlogPage({ lgnSn }: LoginlogPageProps) {
});
const { data: loginlogList, refetch } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.LWPO_CODE, lgnSn, pagination],
queryKey: [ENDPOINT.USER_SERVICE.LOGINLOG, , pagination],
queryFn: () =>
LoginlogApi.getLoginlogList({
...loginlogQuery,
lgnSn,
page: pagination.pageIndex,
page: pagination.pageIndex,
size: pagination.pageSize,
}),
enabled: !!lgnSn,
placeholderData: keepPreviousData,
placeholderData: keepPreviousData,
});
const table = useReactTable({
@@ -52,7 +48,6 @@ function LoginlogPage({ lgnSn }: LoginlogPageProps) {
enableMultiRowSelection: false,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
getRowId: (row) => row.lwpoCd,
});
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
+21 -5
View File
@@ -414,6 +414,9 @@ importers:
apps/wr:
dependencies:
'@common/assets':
specifier: workspace:*
version: link:../../packages/common/assets
'@common/business_components':
specifier: workspace:*
version: link:../../packages/common/business_components
@@ -423,6 +426,9 @@ importers:
'@hookform/resolvers':
specifier: ^4.1.3
version: 4.1.3(react-hook-form@7.54.2)
'@tanstack/react-table':
specifier: ^8.21.2
version: 8.21.2(react-dom@18.3.1)(react@18.3.1)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -432,6 +438,9 @@ importers:
lucide-react:
specifier: ^0.474.0
version: 0.474.0(react@18.3.1)
moment:
specifier: ^2.30.1
version: 2.30.1
next:
specifier: 15.1.7
version: 15.1.7(react-dom@18.3.1)(react@18.3.1)
@@ -447,6 +456,9 @@ importers:
tailwindcss-animate:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.4.17)
tus-js-client:
specifier: ^4.3.1
version: 4.3.1
zod:
specifier: ^3.24.2
version: 3.24.2
@@ -2377,7 +2389,7 @@ packages:
magic-string: 0.27.0
react-docgen-typescript: 2.2.2(typescript@5.6.3)
typescript: 5.6.3
vite: 6.2.0(@types/node@22.13.9)
vite: 6.2.0(@types/node@22.13.5)
dev: true
/@jridgewell/gen-mapping@0.3.8:
@@ -3970,7 +3982,7 @@ packages:
browser-assert: 1.2.1
storybook: 8.6.0(prettier@3.5.2)
ts-dedent: 2.2.0
vite: 6.2.0(@types/node@22.13.9)
vite: 6.2.0(@types/node@22.13.5)
dev: true
/@storybook/components@8.6.0(storybook@8.6.0):
@@ -4102,7 +4114,7 @@ packages:
resolve: 1.22.10
storybook: 8.6.0(prettier@3.5.2)
tsconfig-paths: 4.2.0
vite: 6.2.0(@types/node@22.13.9)
vite: 6.2.0(@types/node@22.13.5)
transitivePeerDependencies:
- rollup
- supports-color
@@ -4577,7 +4589,7 @@ packages:
'@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.9)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
vite: 6.2.0(@types/node@22.13.9)
vite: 6.2.0(@types/node@22.13.5)
transitivePeerDependencies:
- supports-color
dev: true
@@ -7526,6 +7538,10 @@ packages:
node-source-walk: 7.0.1
dev: true
/moment@2.30.1:
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
dev: false
/motion-dom@11.18.1:
resolution: {integrity: sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==}
dependencies:
@@ -9755,7 +9771,7 @@ packages:
debug: 4.4.0
globrex: 0.1.2
tsconfck: 3.1.5(typescript@5.6.3)
vite: 6.2.0(@types/node@22.13.9)
vite: 6.2.0(@types/node@22.13.5)
transitivePeerDependencies:
- supports-color
- typescript