diff --git a/apps/cs/app/(with-base-layout)/admin/page.tsx b/apps/cs/app/(with-base-layout)/admin/page.tsx new file mode 100644 index 000000000..a7633e201 --- /dev/null +++ b/apps/cs/app/(with-base-layout)/admin/page.tsx @@ -0,0 +1,2 @@ +import { AdminPage } from "@/pages/admin"; +export default AdminPage; diff --git a/apps/cs/middleware.ts b/apps/cs/middleware.ts index d56c1f8ab..6bdfd9545 100644 --- a/apps/cs/middleware.ts +++ b/apps/cs/middleware.ts @@ -13,7 +13,6 @@ export function middleware(req: NextRequest) { const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${ queryParams ? `?${queryParams}` : "" }`; - console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`); return NextResponse.rewrite(new URL(newUrl)); } @@ -26,17 +25,18 @@ export function middleware(req: NextRequest) { if (token) requestHeaders.set(CLAIM_NAME, token); if (userId) requestHeaders.set(AUTH_USER_ID, userId); - console.log( - `[Middleware] Rewriting from '/attach-service' path to: ${newUrl}`, - ); return NextResponse.rewrite(new URL(newUrl), { headers: requestHeaders, }); } + if (pathname === "/") { + return NextResponse.redirect(new URL("/dashboard", req.url)); + } + return NextResponse.next(); } export const config = { - matcher: ["/server/:path*", "/attach-service/:path*"], + matcher: ["/server/:path*", "/attach-service/:path*", "/"], }; diff --git a/apps/cs/src/pages/groups/api/group-users-service.ts b/apps/cs/src/entities/group-user/api/group-user-service.ts similarity index 97% rename from apps/cs/src/pages/groups/api/group-users-service.ts rename to apps/cs/src/entities/group-user/api/group-user-service.ts index 5af60a9ef..85355a88f 100644 --- a/apps/cs/src/pages/groups/api/group-users-service.ts +++ b/apps/cs/src/entities/group-user/api/group-user-service.ts @@ -8,7 +8,7 @@ import type { PageGroupUserList, GroupUserSaveData, GroupUserUpdateData, -} from "../model/group-users-interface"; +} from "../model/group-user-interface"; export class GroupUsersApiService implements GroupUserService { // 그룹 사용자 목록 조회 diff --git a/apps/cs/src/entities/group-user/index.ts b/apps/cs/src/entities/group-user/index.ts new file mode 100644 index 000000000..349194a90 --- /dev/null +++ b/apps/cs/src/entities/group-user/index.ts @@ -0,0 +1,5 @@ +export { GroupUsersApi } from "./api/group-user-service"; +export { + type GroupUserListResponseDto, + type GroupUserSaveData, +} from "./model/group-user-interface"; diff --git a/apps/cs/src/pages/groups/model/group-users-interface.ts b/apps/cs/src/entities/group-user/model/group-user-interface.ts similarity index 100% rename from apps/cs/src/pages/groups/model/group-users-interface.ts rename to apps/cs/src/entities/group-user/model/group-user-interface.ts diff --git a/apps/cs/src/pages/groups/api/groups-service.ts b/apps/cs/src/entities/group/api/group-service.ts similarity index 94% rename from apps/cs/src/pages/groups/api/groups-service.ts rename to apps/cs/src/entities/group/api/group-service.ts index 1ead02087..fd1e879fa 100644 --- a/apps/cs/src/pages/groups/api/groups-service.ts +++ b/apps/cs/src/entities/group/api/group-service.ts @@ -6,7 +6,7 @@ import type { GroupList, GroupResponseDto, GroupFormData, -} from "../model/groups-interface"; +} from "../model/group-interface"; export class GroupsApiService implements GroupService { // 그룹 목록 조회 @@ -56,4 +56,4 @@ export class GroupsApiService implements GroupService { } } -export const GroupsApi = new GroupsApiService(); +export const GroupApi = new GroupsApiService(); diff --git a/apps/cs/src/entities/group/index.ts b/apps/cs/src/entities/group/index.ts new file mode 100644 index 000000000..da1d72a41 --- /dev/null +++ b/apps/cs/src/entities/group/index.ts @@ -0,0 +1,6 @@ +export { GroupApi } from "./api/group-service"; +export { + groupFormSchema, + type GroupFormData, + type GroupListResponseDto, +} from "./model/group-interface"; diff --git a/apps/cs/src/pages/groups/model/groups-interface.ts b/apps/cs/src/entities/group/model/group-interface.ts similarity index 100% rename from apps/cs/src/pages/groups/model/groups-interface.ts rename to apps/cs/src/entities/group/model/group-interface.ts diff --git a/apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx b/apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx index 20485cbdd..a9ba18333 100644 --- a/apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx +++ b/apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx @@ -1,3 +1,4 @@ +import { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery } from "@tanstack/react-query"; @@ -27,12 +28,10 @@ type ManagementAuthorityDialogProps = { menuCd?: number; } & React.ComponentProps; -function ManagementAuthorityDialog({ - menuCd, - ...props -}: ManagementAuthorityDialogProps) { +function ManagementAuthorityDialog({ menuCd }: ManagementAuthorityDialogProps) { const { confirm } = useConfirm(); const alert = useAlertStore((state) => state.setMessage); + const [open, setOpen] = useState(false); const query = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.MENU_AUTHRTS, menuCd], @@ -44,7 +43,7 @@ function ManagementAuthorityDialog({ mutationFn: (data: MenuAuthorityPayload[]) => MenuAuthorityApi.saveMenuAuthority(data), onSuccess: () => { - alert("저장되었습니다."); + alert("저장되었습니다.", () => setOpen(false)); }, }); @@ -72,7 +71,7 @@ function ManagementAuthorityDialog({ }; return ( - + - + {children} 비밀번호 초기화 diff --git a/apps/cs/src/pages/admin/api/manager-ip-service.ts b/apps/cs/src/pages/admin/api/manager-ip-service.ts new file mode 100644 index 000000000..6275fb714 --- /dev/null +++ b/apps/cs/src/pages/admin/api/manager-ip-service.ts @@ -0,0 +1,50 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + ManagerIp, + ManagerIpInfo, + ManagerIpList, + ManagerIpParams, + ManagerIpService, +} from "../model/manager-ip-interface"; + +export class ManagerIpApiService implements ManagerIpService { + public async getManagerIpList( + params: ManagerIpParams, + ): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.MANAGER_IP}`, + { params }, + ); + return response.data; + } + + public async getManagerIpInfo(mngNo: string): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.MANAGER_IP}/${mngNo}`, + ); + return response.data; + } + + public async saveManagerIp(data: ManagerIp[]): Promise { + const response = await client.post( + ENDPOINT.CMS_SERVICE.MANAGER_IP, + data, + ); + return response.data; + } + + public async updateManagerIp(mngNo: string, data: ManagerIp): Promise { + const response = await client.put( + `${ENDPOINT.CMS_SERVICE.MANAGER_IP}/${mngNo}`, + data, + ); + return response.data; + } + + public async deleteManagerIp(mngNo: string): Promise { + await client.delete(`${ENDPOINT.CMS_SERVICE.MANAGER_IP}/${mngNo}`); + } +} + +export const ManagerIpApi = new ManagerIpApiService(); diff --git a/apps/cs/src/pages/admin/index.ts b/apps/cs/src/pages/admin/index.ts new file mode 100644 index 000000000..b8f41285c --- /dev/null +++ b/apps/cs/src/pages/admin/index.ts @@ -0,0 +1 @@ +export { AdminPage } from "./ui/admin-page"; diff --git a/apps/cs/src/pages/admin/model/manager-ip-interface.ts b/apps/cs/src/pages/admin/model/manager-ip-interface.ts new file mode 100644 index 000000000..f698db533 --- /dev/null +++ b/apps/cs/src/pages/admin/model/manager-ip-interface.ts @@ -0,0 +1,42 @@ +import { z } from "zod"; +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface ManagerIpParams extends PageableParams { + ipAddr?: string; + userUnqId?: string; +} + +export const managerIpSchema = z.object({ + mngNo: z.number().optional(), + mode: z.string(), + ipAddr: z.string().max(15), + userUnqId: z.string(), + regDt: z.string().optional(), + rgtr: z.string().optional(), + mdfcnDt: z.string().optional(), + mdfr: z.string().optional(), +}); + +export const managerIpFormSchema = z.object({ + ips: z.array( + managerIpSchema.omit({ + regDt: true, + rgtr: true, + mdfcnDt: true, + mdfr: true, + }), + ), +}); + +export type ManagerIp = z.infer; +export type ManagerIpFormData = z.infer; +export type ManagerIpList = PageableList; +export type ManagerIpInfo = ManagerIp; + +export interface ManagerIpService { + getManagerIpList: (params: ManagerIpParams) => Promise; + getManagerIpInfo: (mngNo: string) => Promise; + saveManagerIp: (data: ManagerIp[]) => Promise; + updateManagerIp: (mngNo: string, data: ManagerIp) => Promise; + deleteManagerIp: (mngNo: string) => Promise; +} diff --git a/apps/cs/src/pages/admin/model/table-columns.tsx b/apps/cs/src/pages/admin/model/table-columns.tsx new file mode 100644 index 000000000..dee012995 --- /dev/null +++ b/apps/cs/src/pages/admin/model/table-columns.tsx @@ -0,0 +1,112 @@ +import dayjs from "dayjs"; +import { memo } from "react"; +import type { ColumnDef } from "@tanstack/react-table"; +import { useQueryClient } from "@tanstack/react-query"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { Button } from "@common/components"; +import { PasswordResetDialog, type AdminUserList } from "@/entities/user"; +import { GroupUsersApi, type GroupUserSaveData } from "@/entities/group-user"; +import { ENDPOINT } from "@/shared/config"; +import { useAlertStore, useMutationWrapper } from "@/shared/lib"; +import { ManagerIpDialog } from "../ui/manager-ip-dialog"; + +export const columns: ColumnDef[] = [ + { + id: "no", + header: "NO", + 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: "userId", + header: "관리자ID", + }, + { + accessorKey: "userName", + header: "관리자명", + }, + { + accessorKey: "groupNm", + header: "관리자구분", + }, + { + accessorKey: "ogdpSeNm", + header: "소속", + }, + { + accessorKey: "mdfrNm", + header: "최종수정자", + }, + { + accessorKey: "mdfcnDt", + header: "최종수정일", + cell: ({ row }) => dayjs(row.original.mdfcnDt).format("YYYY-MM-DD"), + }, + { + id: "action", + header: "관리", + cell: ({ row }) => { + return ( + + ); + }, + }, +]; + +const ActionCell = memo( + ({ userUnqId, groupId }: { userUnqId: string; groupId: string }) => { + const queryClient = useQueryClient(); + const { confirm } = useConfirm(); + const alert = useAlertStore((state) => state.setMessage); + + const { mutate: onDelete } = useMutationWrapper({ + mutationFn: (data: GroupUserSaveData) => + GroupUsersApi.saveGroupUser([data]), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.USER_SERVICE.USERS], + }); + alert("삭제되었습니다."); + }, + }); + + const handleDelete = async () => { + if (await confirm("관리자를 삭제하시겠습니까?")) { + onDelete({ + mode: "D", + groupId, + userUnqId, + }); + } + }; + + return ( +
+ + + + + +
+ ); + }, + (prevProps, nextProps) => { + return ( + prevProps.userUnqId === nextProps.userUnqId && + prevProps.groupId === nextProps.groupId + ); + }, +); + +ActionCell.displayName = "ActionCell"; diff --git a/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx b/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx new file mode 100644 index 000000000..327473932 --- /dev/null +++ b/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx @@ -0,0 +1,258 @@ +"use client"; + +import { useState } from "react"; +import { + keepPreviousData, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; +import { + getCoreRowModel, + type PaginationState, + type RowSelectionState, + type ColumnDef, +} from "@tanstack/react-table"; +import { + Button, + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogFooter, + Input, + Checkbox, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + Label, +} from "@common/components"; +import { + UserApi, + type UserListResponse, + type UserSearchParams, +} from "@/entities/user"; +import { GroupApi } from "@/entities/group"; +import { GroupUsersApi } from "@/entities/group-user"; +import { ENDPOINT } from "@/shared/config"; +import { useAlertStore, useDataTable } from "@/shared/lib"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; + +function AddAdminDialog() { + const queryClient = useQueryClient(); + const alert = useAlertStore((state) => state.setMessage); + const { confirm } = useConfirm(); + + const [open, setOpen] = useState(false); + const [userSearchQuery, setUserSearchQuery] = useState({ + keywordType: "", + keyword: "", + }); + const [selectedGroupId, setSelectedGroupId] = useState(""); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + const [rowSelection, setRowSelection] = useState({}); + + const { data: groupOptions } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.GROUPS], + queryFn: () => + GroupApi.findPageGroup({ + delYn: "N", + mngrYn: "Y", + page: 0, + size: 99, + }), + select: (data) => + data.content.map((item) => ({ + groupId: item.groupId, + groupNm: item.groupNm, + })), + enabled: open, + }); + + const query = useQuery({ + queryKey: [ENDPOINT.USER_SERVICE.USERS, pagination, "normal-user"], + queryFn: () => + UserApi.findPageUser({ + ...userSearchQuery, + page: pagination.pageIndex, + size: pagination.pageSize, + }), + enabled: open, + placeholderData: keepPreviousData, + }); + + const columns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="전체 선택" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="행 선택" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "userId", + header: "사용자ID", + }, + { + accessorKey: "userName", + header: "이름", + }, + { + accessorKey: "ogdpSeNm", + header: "소속", + }, + { + accessorKey: "mphn", + header: "연락처", + }, + ]; + + const { tableComponent } = useDataTable({ + query, + tableOptions: { + columns: columns, + state: { pagination, rowSelection }, + manualPagination: true, + enableRowSelection: true, + enableMultiRowSelection: true, + getCoreRowModel: getCoreRowModel(), + onPaginationChange: setPagination, + onRowSelectionChange: setRowSelection, + getRowId: (row) => row.userUnqId, + }, + dataTableOptions: { + viewPagination: true, + }, + }); + + const handleAddAdmins = async () => { + const selectedRows = Object.keys(rowSelection); + + if (!(await confirm("관리자를 추가하시겠습니까?"))) { + return; + } + + try { + const usersToAdd = selectedRows.map((userUnqId) => ({ + mode: "C", + groupId: selectedGroupId, + userUnqId, + })); + + await GroupUsersApi.saveGroupUser(usersToAdd); + + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.USER_SERVICE.USERS], + }); + alert("관리자 추가를 완료했습니다.", () => { + setOpen(false); + setRowSelection({}); + setSelectedGroupId(""); + }); + } catch (error) { + console.error(error); + } + }; + + const handleSearch = (e: React.FormEvent) => { + e.preventDefault(); + e.stopPropagation(); + setPagination({ pageIndex: 0, pageSize: 10 }); + query.refetch(); + }; + + return ( + + + + + + + 관리자 등록 + + +
+ + + setUserSearchQuery({ + ...userSearchQuery, + keyword: e.target.value, + }) + } + className="!text-[1.3rem]" + placeholder="검색어 입력" + /> + +
+
+ + {tableComponent} +
+
+ + +
+ + + + +
+
+ ); +} + +export { AddAdminDialog }; diff --git a/apps/cs/src/pages/admin/ui/admin-page.tsx b/apps/cs/src/pages/admin/ui/admin-page.tsx new file mode 100644 index 000000000..a4cd5265d --- /dev/null +++ b/apps/cs/src/pages/admin/ui/admin-page.tsx @@ -0,0 +1,236 @@ +"use client"; + +import { useState } from "react"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { + getCoreRowModel, + type PaginationState, + type RowSelectionState, + type Table, + type Row, +} from "@tanstack/react-table"; +import { + Button, + Input, + Label, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + Checkbox, +} from "@common/components"; +import { GroupApi } from "@/entities/group"; +import { + UserApi, + type UserSearchParams, + type AdminUserListResponse, +} from "@/entities/user"; +import { GroupUsersApi } from "@/entities/group-user"; +import { ENDPOINT } from "@/shared/config"; +import { useDataTable, useAlertStore } from "@/shared/lib"; +import { columns } from "../model/table-columns"; +import { AddAdminDialog } from "./add-admin-dialog"; + +function AdminPage() { + const queryClient = useQueryClient(); + const alert = useAlertStore((state) => state.setMessage); + const { confirm } = useConfirm(); + + const [adminQuery, setAdminQuery] = useState({ + keywordType: "", + keyword: "", + grpId: "", + }); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + const [rowSelection, setRowSelection] = useState({}); + + const { data: groupOptions } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.GROUPS], + queryFn: () => + GroupApi.findPageGroup({ + delYn: "N", + mngrYn: "Y", + page: 0, + size: 99, + }), + select: (data) => + data.content.map((item) => ({ + groupId: item.groupId, + groupNm: item.groupNm, + })), + }); + + const query = useQuery({ + queryKey: [ENDPOINT.USER_SERVICE.USERS, pagination], + queryFn: () => + UserApi.findPageAdminUser({ + ...adminQuery, + page: pagination.pageIndex, + size: pagination.pageSize, + }), + }); + + const adminColumns = [ + { + id: "select", + header: ({ table }: { table: Table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="전체 선택" + /> + ), + cell: ({ row }: { row: Row }) => ( + row.toggleSelected(!!value)} + aria-label="행 선택" + /> + ), + enableSorting: false, + enableHiding: false, + }, + ...columns, + ]; + + const { tableComponent, tableInstance: table } = useDataTable({ + query, + tableOptions: { + columns: adminColumns, + state: { + pagination, + rowSelection, + }, + manualPagination: true, + enableRowSelection: true, + enableMultiRowSelection: true, + getCoreRowModel: getCoreRowModel(), + onPaginationChange: setPagination, + onRowSelectionChange: setRowSelection, + }, + dataTableOptions: { + viewPagination: true, + }, + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + query.refetch(); + }; + + const handleDeleteAdmins = async () => { + const selectedRows = table.getSelectedRowModel().rows.map((row) => ({ + groupId: row.original.groupId, + userUnqId: row.original.userUnqId, + })); + + if (!(await confirm("관리자를 삭제하시겠습니까?"))) { + return; + } + + try { + const usersToDelete = selectedRows.map((row) => ({ + mode: "D", + groupId: row.groupId, + userUnqId: row.userUnqId, + })); + + await GroupUsersApi.saveGroupUser(usersToDelete); + + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.USER_SERVICE.USERS], + }); + alert("관리자 삭제를 완료했습니다."); + setRowSelection({}); + } catch (error) { + console.error(error); + } + }; + + return ( +
+
+
+
+ + +
+
+ + + + setAdminQuery({ ...adminQuery, keyword: e.target.value }) + } + placeholder="검색어 입력" + /> +
+ +
+
+
+
+
+

+ 관리자 목록(총 {query.data?.totalElements}명) +

+
+ + +
+
+
+
{tableComponent}
+
+
+ ); +} + +export { AdminPage }; diff --git a/apps/cs/src/pages/admin/ui/manager-ip-dialog.tsx b/apps/cs/src/pages/admin/ui/manager-ip-dialog.tsx new file mode 100644 index 000000000..a97947dc5 --- /dev/null +++ b/apps/cs/src/pages/admin/ui/manager-ip-dialog.tsx @@ -0,0 +1,241 @@ +"use client"; + +import { useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { useForm, useFieldArray } from "react-hook-form"; +import { + Dialog, + DialogTrigger, + Button, + DialogContent, + DialogHeader, + DialogTitle, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + Input, + DialogFooter, + DialogClose, + Checkbox, +} from "@common/components"; +import { ENDPOINT } from "@/shared/config"; +import { useAlertStore, useMutationWrapper } from "@/shared/lib"; +import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form"; +import { ManagerIpApi } from "../api/manager-ip-service"; +import { + managerIpFormSchema, + type ManagerIp, + type ManagerIpFormData, +} from "../model/manager-ip-interface"; + +type ManagerIpDialogProps = { + userUnqId: string; +}; + +function ManagerIpDialog({ userUnqId }: ManagerIpDialogProps) { + const { confirm } = useConfirm(); + const alert = useAlertStore((state) => state.setMessage); + + const [open, setOpen] = useState(false); + const [selectedRows, setSelectedRows] = useState([]); + + const { data: managerIpList } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.MANAGER_IP, open], + queryFn: () => ManagerIpApi.getManagerIpList({ userUnqId }), + select: (data) => { + const ips = data.content.map((item) => ({ + ...item, + mode: "U", + })); + initialForm(); + return ips; + }, + enabled: open, + }); + + const { mutateAsync } = useMutationWrapper({ + mutationFn: (data: ManagerIp[]) => ManagerIpApi.saveManagerIp(data), + }); + + const form = useForm({ + resolver: zodResolver(managerIpFormSchema), + values: { + ips: managerIpList ?? [], + }, + }); + + const { fields, append, remove, update } = useFieldArray({ + control: form.control, + name: "ips", + }); + + const initialForm = () => { + form.reset(); + setSelectedRows([]); + }; + + const onSubmit = async (data: ManagerIpFormData) => { + if (await confirm("저장하시겠습니까?")) { + await mutateAsync(data.ips); + alert("저장되었습니다.", () => { + setOpen(false); + }); + } + }; + + const handleAddIP = () => { + append({ + mngNo: 0, + mode: "C", + ipAddr: "", + userUnqId, + }); + }; + + const handleSelectAll = () => { + if (selectedRows.length === fields.length) { + setSelectedRows([]); + } else { + setSelectedRows(fields.map((_, index) => index)); + } + }; + + const handleSelectRow = (i: number) => { + setSelectedRows((prev) => + prev.includes(i) ? prev.filter((j) => j !== i) : [...prev, i], + ); + }; + + const handleDeleteIP = async () => { + if (selectedRows.length === 0) return; + + if (await confirm("선택한 IP를 삭제하시겠습니까?")) { + const sortedIndices = [...selectedRows].sort((a, b) => b - a); + + for (const i of sortedIndices) { + const field = fields[i]; + if (field?.mngNo) { + if (field.mode === "C") { + remove(i); + } else { + update(i, { + ...field, + mode: "D", + }); + } + } else { + remove(i); + } + } + + setSelectedRows([]); + } + }; + + const isAllSelected = + fields.length > 0 && selectedRows.length === fields.length; + + return ( + + + + + + + 접속 IP 관리 + +
+ { + e.preventDefault(); + e.stopPropagation(); + form.handleSubmit(onSubmit)(e); + }} + > +
+ + +
+
+ + + + + + + + + + + IP 주소 + + + + {fields.map((field, i) => { + if (field.mode === "D") return null; + return ( + + + handleSelectRow(i)} + /> + + + ( + + + + + + )} + /> + + + ); + })} + +
+
+ + + + + + +
+ +
+
+ ); +} + +export { ManagerIpDialog }; diff --git a/apps/cs/src/pages/banner/ui/banner-page.tsx b/apps/cs/src/pages/banner/ui/banner-page.tsx index 9bfa842ca..db5a5cc15 100644 --- a/apps/cs/src/pages/banner/ui/banner-page.tsx +++ b/apps/cs/src/pages/banner/ui/banner-page.tsx @@ -35,14 +35,13 @@ function BannerPage() { }); const query = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.BANNERS], + queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, pagination], queryFn: () => BannerApi.getBannerList({ ...bannerQuery, page: pagination.pageIndex, size: pagination.pageSize, }), - enabled: false, }); const handleSubmit = (e: React.FormEvent) => { @@ -99,10 +98,6 @@ function BannerPage() { setPostingTargets(postingTargets); }, [rowSelection]); - useEffect(() => { - query.refetch(); - }, [pagination]); - return (
@@ -131,6 +126,7 @@ function BannerPage() { table.setRowSelection({})} /> + diff --git a/apps/cs/src/pages/module/ui/modify-page.tsx b/apps/cs/src/pages/module/ui/modify-page.tsx index e26dfca4b..b2a49b861 100644 --- a/apps/cs/src/pages/module/ui/modify-page.tsx +++ b/apps/cs/src/pages/module/ui/modify-page.tsx @@ -1,5 +1,6 @@ "use client"; +import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery } from "@tanstack/react-query"; @@ -13,6 +14,7 @@ import { import { ModuleForm } from "./module-form"; function ModifyPage({ sequential }: { sequential: string }) { + const router = useRouter(); const alert = useAlertStore((state) => state.setMessage); const { data: authorizationInfo } = useQuery({ @@ -24,7 +26,7 @@ function ModifyPage({ sequential }: { sequential: string }) { mutationFn: (data: AuthorizationFormData) => AuthorizationsApi.updateAuthorization(sequential, data), onSuccess: () => { - alert("수정되었습니다."); + alert("수정되었습니다.", router.back); }, }); diff --git a/apps/cs/src/pages/module/ui/module-page.tsx b/apps/cs/src/pages/module/ui/module-page.tsx index 5d79b74f0..45524eba5 100644 --- a/apps/cs/src/pages/module/ui/module-page.tsx +++ b/apps/cs/src/pages/module/ui/module-page.tsx @@ -1,7 +1,7 @@ "use client"; +import { useState } from "react"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { getCoreRowModel, type PaginationState } from "@tanstack/react-table"; import { Button, Input, Label } from "@common/components"; @@ -29,7 +29,7 @@ function ModulePage() { }); const query = useQuery({ - queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS], + queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS, pagination], queryFn: () => AuthorizationsApi.getAuthorizationsList({ ...authorizationQuery, @@ -37,7 +37,6 @@ function ModulePage() { size: pagination.pageSize, }), placeholderData: keepPreviousData, - enabled: false, }); const handleRowClick = (row: Authorizations) => { @@ -71,10 +70,6 @@ function ModulePage() { query.refetch(); }; - useEffect(() => { - query.refetch(); - }, [pagination]); - return (
diff --git a/apps/cs/src/pages/organization/model/additional-user-table-columns.tsx b/apps/cs/src/pages/organization/model/add-user-table-columns.tsx similarity index 100% rename from apps/cs/src/pages/organization/model/additional-user-table-columns.tsx rename to apps/cs/src/pages/organization/model/add-user-table-columns.tsx diff --git a/apps/cs/src/pages/organization/ui/additional-user-dialog.tsx b/apps/cs/src/pages/organization/ui/add-user-dialog.tsx similarity index 93% rename from apps/cs/src/pages/organization/ui/additional-user-dialog.tsx rename to apps/cs/src/pages/organization/ui/add-user-dialog.tsx index f4af8787d..e0007abfc 100644 --- a/apps/cs/src/pages/organization/ui/additional-user-dialog.tsx +++ b/apps/cs/src/pages/organization/ui/add-user-dialog.tsx @@ -18,16 +18,13 @@ import { } from "@/entities/user-organization"; import { ENDPOINT } from "@/shared/config"; import { useDataTable } from "@/shared/lib"; -import { columns } from "../model/additional-user-table-columns"; +import { columns } from "../model/add-user-table-columns"; -type AdditionalUserDialogProps = { +type AddUserDialogProps = { onAddUsers: (user: SearchUser[]) => void; } & React.ComponentProps; -function AdditionalUserDialog({ - onAddUsers, - ...props -}: AdditionalUserDialogProps) { +function AddUserDialog({ onAddUsers, ...props }: AddUserDialogProps) { const [open, setOpen] = useState(false); const [userNm, setUserNm] = useState(""); const [rowSelection, setRowSelection] = useState({}); @@ -109,4 +106,4 @@ function AdditionalUserDialog({ ); } -export { AdditionalUserDialog }; +export { AddUserDialog }; diff --git a/apps/cs/src/pages/organization/ui/user-organization-section.tsx b/apps/cs/src/pages/organization/ui/user-organization-section.tsx index 000af3c32..6b8e3b789 100644 --- a/apps/cs/src/pages/organization/ui/user-organization-section.tsx +++ b/apps/cs/src/pages/organization/ui/user-organization-section.tsx @@ -37,7 +37,7 @@ import { CodeApi, type ICode } from "@/entities/code"; import type { OrganizationTree } from "@/entities/organization"; import { ENDPOINT } from "@/shared/config"; import { useAlertStore, useMutationWrapper } from "@/shared/lib"; -import { AdditionalUserDialog } from "./additional-user-dialog"; +import { AddUserDialog } from "./add-user-dialog"; import { MoveOrganizationDialog } from "./move-organization-dialog"; type UserOrganizationSectionProps = { @@ -114,6 +114,12 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { control: form.control, }); + const formData = form.watch("userOrganization"); + const formValidation = userOrganizationFormSchema.safeParse({ + userOrganization: formData, + }); + const isFormValid = formValidation.success; + const handleAddUsers = (users: SearchUser[]) => { const generateUserData = (user: SearchUser) => ({ mode: "C", @@ -220,7 +226,7 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { } handleMoveOrganization={handleMoveOrganization} /> - + -
diff --git a/apps/cs/src/pages/popup/ui/popup-page.tsx b/apps/cs/src/pages/popup/ui/popup-page.tsx index b542d39a3..475fee821 100644 --- a/apps/cs/src/pages/popup/ui/popup-page.tsx +++ b/apps/cs/src/pages/popup/ui/popup-page.tsx @@ -36,7 +36,7 @@ function PopupPage() { const [postingTargets, setPostingTargets] = useState([]); const query = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.POPUPS], + queryKey: [ENDPOINT.CMS_SERVICE.POPUPS, pagination], queryFn: () => PopupApi.getPopupList({ ...popupParams, @@ -116,6 +116,7 @@ function PopupPage() { table.setRowSelection({})} /> +

diff --git a/apps/cs/src/pages/recent/ui/move-post-dialog.tsx b/apps/cs/src/pages/recent/ui/move-post-dialog.tsx index 0afa6b75c..09707ceef 100644 --- a/apps/cs/src/pages/recent/ui/move-post-dialog.tsx +++ b/apps/cs/src/pages/recent/ui/move-post-dialog.tsx @@ -1,4 +1,5 @@ -import React, { useRef } from "react"; +import React, { useRef, useState } from "react"; +import { useQueryClient } from "@tanstack/react-query"; import { Button, Dialog, @@ -11,22 +12,41 @@ import { SelectValue, } from "@common/components"; import type { Board } from "@/entities/board"; -import type { MovePost } from "@/entities/post"; +import { PostApi, type MovePost } from "@/entities/post"; import { AuthorityButton } from "@/entities/menu-authority"; +import { useAlertStore, useMutationWrapper } from "@/shared/lib"; +import { ENDPOINT } from "@/shared/config"; type MovePostDialogProps = { selectedPosts: MovePost[]; boardList: Board[]; - onMovePost: (data: MovePost[]) => void; + resetSelectedPost: () => void; }; function MovePostDialog({ selectedPosts, boardList, - onMovePost, + resetSelectedPost, }: MovePostDialogProps) { + const queryClient = useQueryClient(); + const alert = useAlertStore((state) => state.setMessage); const selectedPostsRef = useRef(selectedPosts); + const [open, setOpen] = useState(false); + + const { mutate: movePost } = useMutationWrapper({ + mutationFn: (data: MovePost[]) => PostApi.movePost(data), + onSuccess: () => { + alert("이동이 완료되었습니다.", () => { + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS], + }); + resetSelectedPost(); + setOpen(false); + }); + }, + }); + const onSelectValueChange = (toBbsId: string) => { selectedPostsRef.current = selectedPosts.map((post) => ({ ...post, @@ -35,7 +55,7 @@ function MovePostDialog({ }; return ( - + - + ); diff --git a/apps/cs/src/pages/recent/ui/recent-page.tsx b/apps/cs/src/pages/recent/ui/recent-page.tsx index efa3f765b..f99ee2339 100644 --- a/apps/cs/src/pages/recent/ui/recent-page.tsx +++ b/apps/cs/src/pages/recent/ui/recent-page.tsx @@ -24,18 +24,12 @@ import { import { PostApi, type DeletePost, - type MovePost, type RecentPost, type RecentPostParams, } from "@/entities/post"; import { BoardApi } from "@/entities/board"; import { AuthorityButton } from "@/entities/menu-authority"; -import { - useAlertStore, - useMutationWrapper, - usePrompt, - useDataTable, -} from "@/shared/lib"; +import { useAlertStore, usePrompt, useDataTable } from "@/shared/lib"; import { ENDPOINT } from "@/shared/config"; import { columns } from "../model/table-columns"; import { MovePostDialog } from "./move-post-dialog"; @@ -56,7 +50,7 @@ function RecentPage() { pageSize: 10, }); const [rowSelection, setRowSelection] = useState({}); - const [recentPostParams, setRecentPostParams] = useState({ + const [recentPostQuery, setRecentPostQuery] = useState({ keywordType: "", keyword: "", bbsId: null, @@ -70,7 +64,7 @@ function RecentPage() { queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, pagination], queryFn: () => PostApi.getRecentPostList({ - ...recentPostParams, + ...recentPostQuery, page: pagination.pageIndex, size: pagination.pageSize, }), @@ -82,13 +76,6 @@ function RecentPage() { queryFn: () => BoardApi.getBoardList(), }); - const { mutate: movePost } = useMutationWrapper({ - mutationFn: (data: MovePost[]) => PostApi.movePost(data), - onSuccess: () => { - alert("이동이 완료되었습니다.", query.refetch); - }, - }); - const handleRowClick = (row: RecentPost) => { router.push( `/bbs/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}`, @@ -119,13 +106,13 @@ function RecentPage() { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - setIsDeletePostData(recentPostParams.delYn === "Y"); + setIsDeletePostData(recentPostQuery.delYn === "Y"); query.refetch(); }; const handleRecentPostParamsChange = (key: keyof RecentPostParams) => (value: unknown) => { - setRecentPostParams({ ...recentPostParams, [key]: value }); + setRecentPostQuery({ ...recentPostQuery, [key]: value }); }; const handlePresetDateRange = (value: string) => { @@ -152,7 +139,16 @@ function RecentPage() { default: presetDateRange = undefined; } - setDateRange(presetDateRange); + handleDateRangeChange(presetDateRange); + }; + + const handleDateRangeChange = (value: DateRange | undefined) => { + setDateRange(value); + setRecentPostQuery({ + ...recentPostQuery, + frDateYmd: dayjs(value?.from).format("YYYYMMDD") ?? "", + toDateYmd: dayjs(value?.to).format("YYYYMMDD") ?? "", + }); }; const onSoftDeletePost = async () => { @@ -209,7 +205,10 @@ function RecentPage() {
- +
@@ -264,7 +263,7 @@ function RecentPage() { handleRecentPostParamsChange("keyword")(e.target.value) } @@ -308,7 +307,7 @@ function RecentPage() { pstNo: row.original.pstNo, }))} boardList={boardList?.content ?? []} - onMovePost={movePost} + resetSelectedPost={() => table.setRowSelection({})} /> ({ ? dayjs(field.value as string).toDate() : undefined } - classes={{ trigger: "w-auto px-4 justify-start" }} + classes={{ trigger: "!w-auto px-4 justify-start" }} onDateChange={handleDateTimeChange( "schdlBgngDt" as Path, )} @@ -120,7 +120,7 @@ function ScheduleForm({ ? dayjs(field.value as string).toDate() : undefined } - classes={{ trigger: "w-auto px-4 justify-start" }} + classes={{ trigger: "!w-auto px-4 justify-start" }} onDateChange={handleDateTimeChange( "schdlEndDt" as Path, )} @@ -230,7 +230,7 @@ function ScheduleForm({ ) as PathValue>, ); }} - className="w-auto px-4" + className="!w-auto px-4" placeholder="반복 종료일 선택" disabled={form.watch("reptYn" as Path) === "N"} /> diff --git a/apps/cs/src/pages/site/ui/domain-management-dialog.tsx b/apps/cs/src/pages/site/ui/domain-management-dialog.tsx index f6a2e67fb..de86f8195 100644 --- a/apps/cs/src/pages/site/ui/domain-management-dialog.tsx +++ b/apps/cs/src/pages/site/ui/domain-management-dialog.tsx @@ -89,7 +89,7 @@ function DomainManagementDialog({ if (await confirm("저장하시겠습니까?")) { await mutateAsync(data.domains); handleSaveDomain?.(data.domains); - alert("저장되었습니다."); + alert("저장되었습니다.", () => props.onOpenChange?.(false)); } }; diff --git a/apps/cs/src/pages/site/ui/ip-management-dialog.tsx b/apps/cs/src/pages/site/ui/ip-management-dialog.tsx index df682f06f..ec5c7a0dd 100644 --- a/apps/cs/src/pages/site/ui/ip-management-dialog.tsx +++ b/apps/cs/src/pages/site/ui/ip-management-dialog.tsx @@ -84,7 +84,7 @@ function IPManagementDialog({ const onSubmit = async (data: IPFormData) => { if (await confirm("저장하시겠습니까?")) { await mutateAsync(data.ips); - alert("저장되었습니다."); + alert("저장되었습니다.", () => props.onOpenChange?.(false)); } }; diff --git a/apps/cs/src/pages/site/ui/modify-page.tsx b/apps/cs/src/pages/site/ui/modify-page.tsx index 5f162d368..0f36bc58d 100644 --- a/apps/cs/src/pages/site/ui/modify-page.tsx +++ b/apps/cs/src/pages/site/ui/modify-page.tsx @@ -34,11 +34,10 @@ function ModifyPage({ siteId }: { siteId: string }) { const { mutateAsync: updateSite } = useMutationWrapper({ mutationFn: (data: SiteFormData) => SiteApi.updateSite(siteId, data), onSuccess: () => { - alert("수정되었습니다.", () => { - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.SITES], - }); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.SITES], }); + alert("수정되었습니다.", router.back); }, }); diff --git a/apps/cs/src/pages/site/ui/site-page.tsx b/apps/cs/src/pages/site/ui/site-page.tsx index eb5a81143..6d0041a65 100644 --- a/apps/cs/src/pages/site/ui/site-page.tsx +++ b/apps/cs/src/pages/site/ui/site-page.tsx @@ -2,8 +2,8 @@ import "@common/assets/styles/grid.css"; +import { useState } from "react"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { getCoreRowModel, type PaginationState } from "@tanstack/react-table"; import { Button, Input, Label } from "@common/components"; @@ -24,7 +24,7 @@ function SitePage() { }); const query = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.SITES], + queryKey: [ENDPOINT.CMS_SERVICE.SITES, pagination], queryFn: () => SiteApi.getSiteList({ ...siteQuery, @@ -32,7 +32,6 @@ function SitePage() { size: pagination.pageSize, }), placeholderData: keepPreviousData, - enabled: false, }); const handleSubmit = (e: React.FormEvent) => { @@ -61,10 +60,6 @@ function SitePage() { }, }); - useEffect(() => { - query.refetch(); - }, [pagination]); - return (
diff --git a/apps/cs/src/pages/survey/ui/article-management-dialog.tsx b/apps/cs/src/pages/survey/ui/article-management-dialog.tsx index 38225d092..49d83143c 100644 --- a/apps/cs/src/pages/survey/ui/article-management-dialog.tsx +++ b/apps/cs/src/pages/survey/ui/article-management-dialog.tsx @@ -60,13 +60,13 @@ function ArticleManagementDialog({ enabled: !!srvyQitemNo && !!open, }); - const { mutateAsync } = useMutationWrapper({ + const { mutate } = useMutationWrapper({ mutationFn: (body: Article[]) => SurveyApi.saveArticleList(body), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [ENDPOINT.CMS_SERVICE.SURVEY_ARTICLE, srvyQitemNo], }); - alert("저장되었습니다."); + alert("저장되었습니다.", () => setOpen(false)); }, }); @@ -95,8 +95,7 @@ function ArticleManagementDialog({ const onSubmit = async (data: ArticleFormData) => { if (await confirm("저장하시겠습니까?")) { - await mutateAsync(data.articles); - alert("저장되었습니다."); + mutate(data.articles); } }; diff --git a/apps/cs/src/pages/survey/ui/question-add-dialog.tsx b/apps/cs/src/pages/survey/ui/question-add-dialog.tsx index 4af6e6a4f..03527806d 100644 --- a/apps/cs/src/pages/survey/ui/question-add-dialog.tsx +++ b/apps/cs/src/pages/survey/ui/question-add-dialog.tsx @@ -43,8 +43,7 @@ function QuestionAddDialog({ queryClient.invalidateQueries({ queryKey: [ENDPOINT.CMS_SERVICE.SURVEY_QUESTION, srvySn.toString()], }); - alert("문항이 추가되었습니다."); - onOpenChange?.(false); + alert("문항이 추가되었습니다.", () => onOpenChange?.(false)); }, }); diff --git a/apps/cs/src/pages/survey/ui/question-edit-dialog.tsx b/apps/cs/src/pages/survey/ui/question-edit-dialog.tsx index 6d722f738..07d6a8227 100644 --- a/apps/cs/src/pages/survey/ui/question-edit-dialog.tsx +++ b/apps/cs/src/pages/survey/ui/question-edit-dialog.tsx @@ -50,8 +50,7 @@ function QuestionEditDialog({ queryClient.invalidateQueries({ queryKey: [ENDPOINT.CMS_SERVICE.SURVEY_QUESTION, srvySn.toString()], }); - alert("문항이 수정되었습니다."); - setOpen(false); + alert("문항이 수정되었습니다.", () => setOpen(false)); }, }); diff --git a/apps/cs/src/pages/survey/ui/survey-page.tsx b/apps/cs/src/pages/survey/ui/survey-page.tsx index 3e99f2bad..c34a21f6f 100644 --- a/apps/cs/src/pages/survey/ui/survey-page.tsx +++ b/apps/cs/src/pages/survey/ui/survey-page.tsx @@ -116,6 +116,7 @@ function SurveyPage() { table.setRowSelection({})} /> TemplateApi.updateTemplate(sequential, data), onSuccess: () => { - alert("수정되었습니다."); + alert("수정되었습니다.", router.back); }, }); diff --git a/apps/cs/src/pages/template/ui/template-page.tsx b/apps/cs/src/pages/template/ui/template-page.tsx index 64921185d..dc90b69ff 100644 --- a/apps/cs/src/pages/template/ui/template-page.tsx +++ b/apps/cs/src/pages/template/ui/template-page.tsx @@ -1,7 +1,7 @@ "use client"; +import { useState } from "react"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { getCoreRowModel, type PaginationState } from "@tanstack/react-table"; import { Button, Input, Label } from "@common/components"; @@ -25,7 +25,7 @@ function TemplatePage() { }); const query = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.TEMPLATE], + queryKey: [ENDPOINT.CMS_SERVICE.TEMPLATE, pagination], queryFn: () => TemplateApi.getTemplateList({ ...templateQuery, @@ -33,7 +33,6 @@ function TemplatePage() { size: pagination.pageSize, }), placeholderData: keepPreviousData, - enabled: false, }); const handleSubmit = (e: React.FormEvent) => { @@ -62,10 +61,6 @@ function TemplatePage() { }, }); - useEffect(() => { - query.refetch(); - }, [pagination]); - return (
diff --git a/apps/cs/src/pages/terms/ui/modify-page.tsx b/apps/cs/src/pages/terms/ui/modify-page.tsx index a13bd5787..5490a6b34 100644 --- a/apps/cs/src/pages/terms/ui/modify-page.tsx +++ b/apps/cs/src/pages/terms/ui/modify-page.tsx @@ -22,7 +22,7 @@ function ModifyPage({ sequential }: { sequential: string }) { const { mutateAsync: updateTerms } = useMutationWrapper({ mutationFn: (data: TermsFormData) => TermsApi.updateTerms(sequential, data), onSuccess: () => { - alert("수정되었습니다."); + alert("수정되었습니다.", router.back); }, }); diff --git a/apps/cs/src/pages/terms/ui/terms-page.tsx b/apps/cs/src/pages/terms/ui/terms-page.tsx index 633c68d9e..8cb6a911e 100644 --- a/apps/cs/src/pages/terms/ui/terms-page.tsx +++ b/apps/cs/src/pages/terms/ui/terms-page.tsx @@ -1,7 +1,7 @@ "use client"; +import { useState } from "react"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { getCoreRowModel, type PaginationState } from "@tanstack/react-table"; import { Button, Input, Label } from "@common/components"; @@ -25,7 +25,7 @@ function TermsPage() { }); const query = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.TERMS], + queryKey: [ENDPOINT.CMS_SERVICE.TERMS, pagination], queryFn: () => TermsApi.getTermsList({ ...termsQuery, @@ -33,7 +33,6 @@ function TermsPage() { size: pagination.pageSize, }), placeholderData: keepPreviousData, - enabled: false, }); const handleSubmit = (e: React.FormEvent) => { @@ -62,10 +61,6 @@ function TermsPage() { }, }); - useEffect(() => { - query.refetch(); - }, [pagination]); - return (
diff --git a/apps/cs/src/pages/users/ui/create-page.tsx b/apps/cs/src/pages/users/ui/create-page.tsx index 84cc23b06..f58469327 100644 --- a/apps/cs/src/pages/users/ui/create-page.tsx +++ b/apps/cs/src/pages/users/ui/create-page.tsx @@ -37,8 +37,7 @@ function CreatePage() { const { mutateAsync: saveUser } = useMutationWrapper({ mutationFn: (data: UserSaveData) => UserApi.saveUser(data), onSuccess: () => { - alert("회원이 등록되었습니다."); - router.push("/users"); + alert("회원이 등록되었습니다.", router.back); }, }); diff --git a/apps/cs/src/pages/users/ui/user-detail.tsx b/apps/cs/src/pages/users/ui/user-detail.tsx index 9673f5c02..60c1f34b8 100644 --- a/apps/cs/src/pages/users/ui/user-detail.tsx +++ b/apps/cs/src/pages/users/ui/user-detail.tsx @@ -11,10 +11,9 @@ import { TableHead, TableRow, } from "@common/components"; -import { USER_STATE_CODE, UserApi } from "@/entities/user"; +import { USER_STATE_CODE, UserApi, PasswordResetDialog } from "@/entities/user"; import { ENDPOINT } from "@/shared/config"; import { useAlertStore, useMutationWrapper } from "@/shared/lib"; -import { PasswordResetDialog } from "./password-reset-dialog"; function UserDetail({ userUnqId }: { userUnqId: string }) { const router = useRouter(); @@ -76,7 +75,11 @@ function UserDetail({ userUnqId }: { userUnqId: string }) { 계정잠금 )} - + + + {data?.userStateCode === USER_STATE_CODE.대기 && (