Merge branch 'iipbs/cs/dev'
Deploy Frontend / deploy (push) Successful in 5m3s

This commit is contained in:
2026-06-11 16:38:24 +09:00
18 changed files with 768 additions and 564 deletions
+15 -11
View File
@@ -1,17 +1,29 @@
"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 Row,
type RowSelectionState,
type Table,
type Row,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useDataTable } from "@/shared/lib";
import { GroupApi } from "@/entities/group";
import { GroupUsersApi } from "@/entities/group-user";
import {
type AdminUserListResponse,
UserApi,
type UserSearchParams,
} from "@/entities/user";
import {
Button,
Checkbox,
Input,
Label,
Select,
@@ -19,18 +31,10 @@ import {
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() {
+41 -34
View File
@@ -1,20 +1,25 @@
"use client";
import { useEffect, useState, useRef } from "react";
import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { Button, Input, Label } from "@common/components";
import { BannerApi } from "../api/banner-service";
import { type Banner } from "../model/banner-interface";
import { createColumns } from "../model/banner-table-columns";
import { type Banner, type BannerParams } from "../model/banner-interface";
import { PreviewBannerDialog } from "./preview-banner-dialog";
type PostingTarget = {
@@ -22,50 +27,52 @@ type PostingTarget = {
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
bnrTtl: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function BannerPage() {
const router = useRouter();
const imageRefs = useRef<Map<number, string>>(new Map());
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const [bnrTtl, setBnrTtl] = useState("");
const [searchParams, setSearchParams] = useState<BannerParams>({
bnrTtl: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [bnrTtl, setBnrTtl] = useState(params.bnrTtl);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, params],
queryFn: () =>
BannerApi.getBannerList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
bnrTtl: params.bnrTtl,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
bnrTtl,
});
setParams({ bnrTtl, page: 0 });
};
const handleReset = () => {
setBnrTtl("");
setSearchParams({
...searchParams,
bnrTtl: "",
});
resetParams();
};
const handleRowClick = (row: Banner) => {
@@ -92,7 +99,7 @@ function BannerPage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
+42 -38
View File
@@ -2,12 +2,19 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Board, BoardApi } from "@/entities/board";
import { CodeSelect } from "@/entities/code";
import {
Button,
Input,
@@ -18,60 +25,57 @@ import {
SelectTrigger,
SelectValue,
} from "@common/components";
import { CodeSelect } from "@/entities/code";
import { BoardApi, type BoardParams } from "@/entities/board";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { type Board } from "@/entities/board";
import { columns } from "../model/table-columns";
const DEFAULT_PARAMS = {
bbsTypeCd: "",
bbsNm: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function BbsPage() {
const router = useRouter();
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [bbsTypeCd, setBbsTypeCd] = useState("");
const [bbsNm, setBbsNm] = useState("");
const [searchParams, setSearchParams] = useState<BoardParams>({
bbsTypeCd: "",
bbsNm: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [bbsTypeCd, setBbsTypeCd] = useState(params.bbsTypeCd);
const [bbsNm, setBbsNm] = useState(params.bbsNm);
const query = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.BBS, searchParams, pagination],
queryKey: [ENDPOINT.BOARD_SERVICE.BBS, params],
queryFn: () =>
BoardApi.getBoardList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
bbsTypeCd: params.bbsTypeCd,
bbsNm: params.bbsNm,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
bbsTypeCd,
bbsNm,
});
setParams({ bbsTypeCd, bbsNm, page: 0 });
};
const handleReset = () => {
setBbsTypeCd("");
setBbsNm("");
setSearchParams({
...searchParams,
bbsTypeCd: "",
bbsNm: "",
});
resetParams();
};
const handleRowClick = (row: Board) => {
@@ -89,7 +93,7 @@ function BbsPage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
+39 -35
View File
@@ -2,60 +2,64 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import {
ContentsApi,
type Contents,
type ContentsParams,
} from "@/entities/content";
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Contents, ContentsApi } from "@/entities/content";
import { Button, Input, Label } from "@common/components";
import { columns } from "../model/table-columns";
const DEFAULT_PARAMS = {
contsTtl: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function ContentsPage() {
const router = useRouter();
const [contsTtl, setContsTtl] = useState("");
const [searchParams, setSearchParams] = useState<ContentsParams>({
contsTtl: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [contsTtl, setContsTtl] = useState(params.contsTtl);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS, params],
queryFn: () =>
ContentsApi.getContentsList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
contsTtl: params.contsTtl,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
contsTtl,
});
setParams({ contsTtl, page: 0 });
};
const handleReset = () => {
setContsTtl("");
setSearchParams({
...searchParams,
contsTtl: "",
});
resetParams();
};
const handleRowClick = (row: Contents) => {
@@ -69,7 +73,7 @@ function ContentsPage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
+7 -4
View File
@@ -1,13 +1,16 @@
"use client";
import { useState } from "react";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { Button, Input, Label } from "@common/components";
import { AttachmentApi, type AttachmentParams } from "@/entities/attachment";
import { useAlertStore, useDataTable, useMutationWrapper } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useDataTable, useMutationWrapper } from "@/shared/lib";
import { AttachmentApi, type AttachmentParams } from "@/entities/attachment";
import { Button, Input, Label } from "@common/components";
import { createColumns } from "../model/table-columns";
function FilePage() {
+37 -32
View File
@@ -2,68 +2,73 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { Button, Input, Label } from "@common/components";
import { ImageApi } from "../api/image-service";
import { type Image } from "../model/image-interface";
import { columns } from "../model/image-table-columns";
import { type Image, type ImageParams } from "../model/image-interface";
type PostingTarget = {
pstgTrgtNm: string;
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
keyword: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function ImagePage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<ImageParams>({
keyword: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.IMAGES, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.IMAGES, params],
queryFn: () =>
ImageApi.getImageList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
sort: params.sort,
page: params.page,
size: params.size,
}),
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
});
setParams({ keyword, page: 0 });
};
const handleReset = () => {
setKeyword("");
setSearchParams({
...searchParams,
keyword: "",
});
resetParams();
};
const handleRowClick = (row: Image) => {
@@ -81,7 +86,7 @@ function ImagePage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
+55 -47
View File
@@ -2,45 +2,76 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { CodeSelect } from "@/entities/code";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import {
AuthorizationsApi,
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import {
type Authorizations,
type AuthorizationsParams,
AuthorizationsApi,
} from "@/entities/authorization";
import { CodeSelect } from "@/entities/code";
import { Button, Input, Label } from "@common/components";
import { columns } from "../model/authorizations-table-columns";
const DEFAULT_PARAMS = {
authorizationName: "",
httpMethodCode: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function ModulePage() {
const router = useRouter();
const [authorizationName, setAuthorizationName] = useState("");
const [httpMethodCode, setHttpMethodCode] = useState("");
const [searchParams, setSearchParams] = useState<AuthorizationsParams>({
authorizationName: "",
httpMethodCode: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [authorizationName, setAuthorizationName] = useState(
params.authorizationName,
);
const [httpMethodCode, setHttpMethodCode] = useState(params.httpMethodCode);
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS, searchParams, pagination],
queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS, params],
queryFn: () =>
AuthorizationsApi.getAuthorizationsList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
authorizationName: params.authorizationName,
httpMethodCode: params.httpMethodCode,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setParams({ authorizationName, httpMethodCode, page: 0 });
};
const handleReset = () => {
setAuthorizationName("");
setHttpMethodCode("");
resetParams();
};
const handleRowClick = (row: Authorizations) => {
router.push(`/module/register/${row.authorizationNo}`);
};
@@ -52,7 +83,7 @@ function ModulePage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
@@ -62,29 +93,6 @@ function ModulePage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
authorizationName,
httpMethodCode,
});
};
const handleReset = () => {
setAuthorizationName("");
setHttpMethodCode("");
setSearchParams({
...searchParams,
authorizationName: "",
httpMethodCode: "",
});
};
return (
<div className="card card-border !mt-6">
<div className="card card-border">
+37 -32
View File
@@ -2,70 +2,75 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { ENDPOINT } from "@/shared/config";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { AuthorityButton } from "@/entities/menu-authority";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { Button, Input, Label } from "@common/components";
import { PopupApi } from "../api/popup-service";
import type { Popup } from "../model/popup-interface";
import { columns } from "../model/table-columns";
import type { Popup, PopupParams } from "../model/popup-interface";
type PostingTarget = {
pstgTrgtNm: string;
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
keyword: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function PopupPage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<PopupParams>({
keyword: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS, params],
queryFn: () =>
PopupApi.getPopupList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
});
setParams({ keyword, page: 0 });
};
const handleReset = () => {
setKeyword("");
setSearchParams({
...searchParams,
keyword: "",
});
resetParams();
};
const handleRowClick = (row: Popup) => {
@@ -82,7 +87,7 @@ function PopupPage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
+98 -82
View File
@@ -1,15 +1,35 @@
"use client";
import dayjs from "dayjs";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import dayjs from "dayjs";
import { ENDPOINT } from "@/shared/config";
import {
useAlertStore,
useDataTable,
useSearchParamsState,
useSelectPrompt,
} from "@/shared/lib";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
import { BoardApi } from "@/entities/board";
import { AuthorityButton } from "@/entities/menu-authority";
import {
type DeletePost,
deleteReasonMessages,
PostApi,
type RecentPost,
} from "@/entities/post";
import { SortSelect } from "@/widgets/post";
import {
Button,
Input,
@@ -20,76 +40,72 @@ import {
SelectTrigger,
SelectValue,
} from "@common/components";
import { SortSelect } from "@/widgets/post";
import {
PostApi,
deleteReasonMessages,
type DeletePost,
type RecentPost,
type RecentPostParams,
} from "@/entities/post";
import { BoardApi } from "@/entities/board";
import { AuthorityButton } from "@/entities/menu-authority";
import { ENDPOINT } from "@/shared/config";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
import { useAlertStore, useSelectPrompt, useDataTable } from "@/shared/lib";
import { columns } from "../model/table-columns";
import { MovePostDialog } from "./move-post-dialog";
type DateRange = {
from?: Date | undefined;
to?: Date | undefined;
from?: Date | null | undefined;
to?: Date | null | undefined;
};
const selectData = Object.entries(deleteReasonMessages).map(
([label, value]) => ({ label, value }),
);
const DEFAULT_PARAMS = {
keywordType: "",
keyword: "",
bbsId: "",
delYn: "N",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function parseDateRange(frDateYmd: string, toDateYmd: string): DateRange {
return {
from: frDateYmd ? dayjs(frDateYmd, "YYYYMMDD").toDate() : undefined,
to: toDateYmd ? dayjs(toDateYmd, "YYYYMMDD").toDate() : undefined,
};
}
function RecentPage() {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const { prompt } = useSelectPrompt(selectData);
const { confirm } = useConfirm();
const alert = useAlertStore((state) => state.setMessage);
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [tableSort, setTableSort] = useState("regDt,desc");
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [presetDateRange, setPresetDateRange] = useState("");
const [dateRange, setDateRange] = useState<DateRange>({
from: undefined,
to: undefined,
});
const [keywordType, setKeywordType] = useState("");
const [keyword, setKeyword] = useState("");
const [bbsId, setBbsId] = useState<string | null>(null);
const [delYn, setDelYn] = useState("N");
const [searchParams, setSearchParams] = useState<RecentPostParams>({
keywordType: "",
keyword: "",
bbsId: null,
delYn: "N",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
});
const [isDeletePostData, setIsDeletePostData] = useState(false);
const [dateRange, setDateRange] = useState<DateRange>(() =>
parseDateRange(params.frDateYmd, params.toDateYmd),
);
const [keywordType, setKeywordType] = useState(params.keywordType);
const [keyword, setKeyword] = useState(params.keyword);
const [bbsId, setBbsId] = useState(params.bbsId);
const [delYn, setDelYn] = useState(params.delYn);
const query = useQuery({
queryKey: [
ENDPOINT.BOARD_SERVICE.POSTS,
searchParams,
pagination,
tableSort,
],
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, params],
queryFn: () =>
PostApi.getRecentPostList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
sort: tableSort,
keywordType: params.keywordType,
keyword: params.keyword,
bbsId: params.bbsId ? parseInt(params.bbsId) : null,
delYn: params.delYn,
frDateYmd: params.frDateYmd,
toDateYmd: params.toDateYmd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
@@ -99,9 +115,22 @@ function RecentPage() {
queryFn: () => BoardApi.getBoardList(),
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const isDeletePostData = params.delYn === "Y";
const handleRowClick = (row: RecentPost) => {
const from = `${pathname}${searchParams?.toString() ? `?${searchParams.toString()}` : ""}`;
router.push(
`/bbs/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}`,
`/bbs/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}?from=${encodeURIComponent(from)}`,
);
};
@@ -116,7 +145,7 @@ function RecentPage() {
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
@@ -127,45 +156,32 @@ function RecentPage() {
const isSelectedRow = table.getFilteredSelectedRowModel().rows.length > 0;
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsDeletePostData(delYn === "Y");
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
setParams({
keywordType,
keyword,
bbsId: bbsId ? parseInt(bbsId) : null,
bbsId,
delYn,
frDateYmd: dateRange?.from
? dayjs(dateRange.from).format("YYYYMMDD")
: "",
toDateYmd: dateRange?.to ? dayjs(dateRange.to).format("YYYYMMDD") : "",
page: 0,
});
};
const handleReset = () => {
setKeywordType("");
setKeyword("");
setBbsId(null);
setBbsId("");
setDelYn("N");
setPresetDateRange("");
setDateRange({
from: undefined,
to: undefined,
});
setSearchParams({
...searchParams,
keywordType: "",
keyword: "",
bbsId: null,
delYn: "N",
frDateYmd: "",
toDateYmd: "",
});
setIsDeletePostData(false);
resetParams();
};
const handlePresetDateRange = (value: string) => {
@@ -273,10 +289,7 @@ function RecentPage() {
setDateRange={handleDateRangeChange}
/>
<Label className="ml-4 mr-2 text-label">게시판</Label>
<Select
value={bbsId?.toString() || ""}
onValueChange={(value) => setBbsId(value ? value : null)}
>
<Select value={bbsId} onValueChange={setBbsId}>
<SelectTrigger className="w-[12rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
@@ -289,7 +302,7 @@ function RecentPage() {
</SelectContent>
</Select>
<Label className="ml-4 mr-2 text-label">상태</Label>
<Select value={delYn} onValueChange={(value) => setDelYn(value)}>
<Select value={delYn} onValueChange={setDelYn}>
<SelectTrigger className="w-[10rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
@@ -376,10 +389,10 @@ function RecentPage() {
</>
)}
<Select
value={table.getState().pagination.pageSize.toString()}
onValueChange={(value) => {
table.setPageSize(parseInt(value));
}}
value={params.size.toString()}
onValueChange={(value) =>
setParams({ size: parseInt(value), page: 0 })
}
>
<SelectTrigger className="h-[3.2rem] w-[12rem]">
<SelectValue placeholder="페이지 크기" />
@@ -392,7 +405,10 @@ function RecentPage() {
))}
</SelectContent>
</Select>
<SortSelect value={tableSort} onValueChange={setTableSort} />
<SortSelect
value={params.sort}
onValueChange={(value) => setParams({ sort: value })}
/>
</div>
</div>
</div>
+51 -42
View File
@@ -2,39 +2,68 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { RoleApi, type Role, type RoleParams } from "@/entities/role";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Role, RoleApi } from "@/entities/role";
import { Button, Input, Label } from "@common/components";
import { createColumns } from "../model/table-columns";
const DEFAULT_PARAMS = {
keywordType: "roleName",
keyword: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function RolePage() {
const router = useRouter();
const alert = useAlertStore((state) => state.setMessage);
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<RoleParams>({
keywordType: "roleName",
keyword: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.ROLES, searchParams, pagination],
queryKey: [ENDPOINT.USER_SERVICE.ROLES, params],
queryFn: () =>
RoleApi.getRoles({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keywordType: params.keywordType,
keyword: params.keyword,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setParams({ keyword, page: 0 });
};
const handleReset = () => {
setKeyword("");
resetParams();
};
const handleRowClick = (row: Role) => {
router.push(`/role/register/${row.roleId}`);
};
@@ -48,7 +77,7 @@ function RolePage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
},
dataTableOptions: {
onRowClick: handleRowClick,
@@ -56,26 +85,6 @@ function RolePage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
});
};
const handleReset = () => {
setKeyword("");
setSearchParams({
...searchParams,
keyword: "",
});
};
return (
<div className="card card-border !mt-6">
<div className="card card-border">
+40 -29
View File
@@ -1,54 +1,65 @@
"use client";
import "@common/assets/styles/grid.css";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { SiteApi, type Site, type SiteParams } from "@/entities/site";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { type Site, SiteApi } from "@/entities/site";
import { Button, Input, Label } from "@common/components";
import { columns } from "../model/table-columns";
const DEFAULT_PARAMS = {
siteNm: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function SitePage() {
const router = useRouter();
const [siteNm, setSiteNm] = useState("");
const [searchParams, setSearchParams] = useState<SiteParams>({
siteNm: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [siteNm, setSiteNm] = useState(params.siteNm);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SITES, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.SITES, params],
queryFn: () =>
SiteApi.getSiteList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
siteNm: params.siteNm,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setSearchParams({
...searchParams,
siteNm,
});
setParams({ siteNm, page: 0 });
};
const handleReset = () => {
setSiteNm("");
setSearchParams({
...searchParams,
siteNm: "",
});
resetParams();
};
const handleRowClick = (row: Site) => {
@@ -62,7 +73,7 @@ function SitePage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
+45 -40
View File
@@ -2,19 +2,23 @@
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { AuthorityButton } from "@/entities/menu-authority";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { AuthorityButton } from "@/entities/menu-authority";
import { POSTING_TYPE, PostingDialog } from "@/entities/posting";
import { Button, Input, Label } from "@common/components";
import { SurveyApi } from "../api/survey-service";
import type { Survey, SurveyParams } from "../model/survey-interface";
import type { Survey } from "../model/survey-interface";
import { columns } from "../model/survey-table-columns";
type PostingTarget = {
@@ -22,32 +26,53 @@ type PostingTarget = {
pstgTrgtNo: number;
};
const DEFAULT_PARAMS = {
srvyTtl: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function SurveyPage() {
const router = useRouter();
const [srvyTtl, setSrvyTtl] = useState("");
const [searchParams, setSearchParams] = useState<SurveyParams>({
srvyTtl: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [srvyTtl, setSrvyTtl] = useState(params.srvyTtl);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [postingTargets, setPostingTargets] = useState<PostingTarget[]>([]);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SURVEY, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.SURVEY, params],
queryFn: () =>
SurveyApi.getSurveyList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
srvyTtl: params.srvyTtl,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setParams({ srvyTtl, page: 0 });
};
const handleReset = () => {
setSrvyTtl("");
resetParams();
};
const handleRowClick = (row: Survey) => {
router.push(`/survey/${row.srvySn}`);
};
@@ -62,7 +87,7 @@ function SurveyPage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
onRowSelectionChange: setRowSelection,
},
dataTableOptions: {
@@ -71,26 +96,6 @@ function SurveyPage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
srvyTtl,
});
};
const handleReset = () => {
setSrvyTtl("");
setSearchParams({
...searchParams,
srvyTtl: "",
});
};
useEffect(() => {
const postingTargets = table
.getFilteredSelectedRowModel()
+44 -32
View File
@@ -2,58 +2,70 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { CodeSelect } from "@/entities/code";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { CodeSelect } from "@/entities/code";
import { Button, Input, Label } from "@common/components";
import { TemplateApi } from "../api/template-service";
import type { Template } from "../model/template-interface";
import { columns } from "../model/template-table-columns";
import type { Template, TemplateParams } from "../model/template-interface";
const DEFAULT_PARAMS = {
keyword: "",
tmpltSeCd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function TemplatePage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [tmpltSeCd, setTmpltSeCd] = useState("");
const [searchParams, setSearchParams] = useState<TemplateParams>({
keyword: "",
tmpltSeCd: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [tmpltSeCd, setTmpltSeCd] = useState(params.tmpltSeCd);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.TEMPLATE, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.TEMPLATE, params],
queryFn: () =>
TemplateApi.getTemplateList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
tmpltSeCd: params.tmpltSeCd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setSearchParams({
...searchParams,
keyword,
tmpltSeCd,
});
setParams({ keyword, tmpltSeCd, page: 0 });
};
const handleReset = () => {
setKeyword("");
setTmpltSeCd("");
setSearchParams({
...searchParams,
keyword: "",
tmpltSeCd: "",
});
resetParams();
};
const handleRowClick = (row: Template) => {
@@ -67,7 +79,7 @@ function TemplatePage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
+44 -37
View File
@@ -2,63 +2,70 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { CodeSelect } from "@/entities/code";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { CodeSelect } from "@/entities/code";
import { Button, Input, Label } from "@common/components";
import { TermsApi } from "../api/terms-service";
import type { Terms } from "../model/terms-interface";
import { columns } from "../model/terms-table-columns";
import type { TermsParams, Terms } from "../model/terms-interface";
const DEFAULT_PARAMS = {
keyword: "",
trmsSeCd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function TermsPage() {
const router = useRouter();
const [keyword, setKeyword] = useState("");
const [trmsSeCd, setTrmsSeCd] = useState("");
const [searchParams, setSearchParams] = useState<TermsParams>({
keyword: "",
trmsSeCd: "",
sort: "regDt,desc",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [keyword, setKeyword] = useState(params.keyword);
const [trmsSeCd, setTrmsSeCd] = useState(params.trmsSeCd);
const query = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.TERMS, searchParams, pagination],
queryKey: [ENDPOINT.CMS_SERVICE.TERMS, params],
queryFn: () =>
TermsApi.getTermsList({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keyword: params.keyword,
trmsSeCd: params.trmsSeCd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keyword,
trmsSeCd,
});
setParams({ keyword, trmsSeCd, page: 0 });
};
const handleReset = () => {
setKeyword("");
setTrmsSeCd("");
setSearchParams({
...searchParams,
keyword: "",
trmsSeCd: "",
});
resetParams();
};
const handleRowClick = (row: Terms) => {
@@ -72,7 +79,7 @@ function TermsPage() {
state: {
pagination,
},
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
},
+68 -60
View File
@@ -3,19 +3,19 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
type OnChangeFn,
type PaginationState,
} from "@tanstack/react-table";
import dayjs from "dayjs";
import { ENDPOINT } from "@/shared/config";
import { useDataTable } from "@/shared/lib";
import { useDataTable, useSearchParamsState } from "@/shared/lib";
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
import { CodeApi } from "@/entities/code";
import {
UserApi,
type UserListResponse,
type UserSearchParams,
} from "@/entities/user";
import { UserApi, type UserListResponse } from "@/entities/user";
import {
Button,
Checkbox,
@@ -35,34 +35,39 @@ type DateRange = {
to?: Date | null;
};
const DEFAULT_PARAMS = {
keywordType: "all",
keyword: "",
userStateCode: "",
aprvYn: "",
grpId: "",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
page: 0,
size: 10,
};
function parseDateRange(frDateYmd: string, toDateYmd: string): DateRange {
return {
from: frDateYmd ? dayjs(frDateYmd, "YYYYMMDD").toDate() : undefined,
to: toDateYmd ? dayjs(toDateYmd, "YYYYMMDD").toDate() : undefined,
};
}
function UsersPage() {
const router = useRouter();
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const { params, setParams, resetParams } =
useSearchParamsState(DEFAULT_PARAMS);
const [presetDateRange, setPresetDateRange] = useState("");
const [dateRange, setDateRange] = useState<DateRange>({
from: undefined,
to: undefined,
});
const [keywordType, setKeywordType] = useState("all");
const [keyword, setKeyword] = useState("");
const [userStateCode, setUserStateCode] = useState("");
const [aprvYn, setAprvYn] = useState("");
const [grpId, setGrpId] = useState("");
const [searchParams, setSearchParams] = useState<UserSearchParams>({
keywordType: "all",
keyword: "",
userStateCode: "",
aprvYn: "",
grpId: "",
frDateYmd: "",
toDateYmd: "",
sort: "regDt,desc",
});
const [dateRange, setDateRange] = useState<DateRange>(() =>
parseDateRange(params.frDateYmd, params.toDateYmd),
);
const [keywordType, setKeywordType] = useState(params.keywordType);
const [keyword, setKeyword] = useState(params.keyword);
const [userStateCode, setUserStateCode] = useState(params.userStateCode);
const [aprvYn, setAprvYn] = useState(params.aprvYn);
const [grpId, setGrpId] = useState(params.grpId);
const { data: userSttsStats } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS, "userStatusStatistics"],
@@ -70,13 +75,21 @@ function UsersPage() {
});
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS, searchParams, pagination],
queryKey: [ENDPOINT.USER_SERVICE.USERS, params],
queryFn: () =>
UserApi.findPageUser({
...searchParams,
page: pagination.pageIndex,
size: pagination.pageSize,
keywordType: params.keywordType,
keyword: params.keyword,
userStateCode: params.userStateCode,
aprvYn: params.aprvYn,
grpId: params.grpId,
frDateYmd: params.frDateYmd,
toDateYmd: params.toDateYmd,
sort: params.sort,
page: params.page,
size: params.size,
}),
placeholderData: keepPreviousData,
});
const { data: userStateCodes } = useQuery({
@@ -84,6 +97,16 @@ function UsersPage() {
queryFn: () => CodeApi.getCode("USERSTTS"),
});
const pagination: PaginationState = {
pageIndex: params.page,
pageSize: params.size,
};
const handlePaginationChange: OnChangeFn<PaginationState> = (updater) => {
const next = typeof updater === "function" ? updater(pagination) : updater;
setParams({ page: next.pageIndex, size: next.pageSize });
};
const handleRowClick = (row: UserListResponse) => {
router.push(`/users/${row.userUnqId}`);
};
@@ -97,7 +120,7 @@ function UsersPage() {
},
manualPagination: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onPaginationChange: handlePaginationChange,
},
dataTableOptions: {
onRowClick: handleRowClick,
@@ -105,14 +128,9 @@ function UsersPage() {
},
});
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
setParams({
keywordType,
keyword,
userStateCode,
@@ -122,6 +140,7 @@ function UsersPage() {
? dayjs(dateRange.from).format("YYYYMMDD")
: "",
toDateYmd: dateRange?.to ? dayjs(dateRange.to).format("YYYYMMDD") : "",
page: 0,
});
};
@@ -131,20 +150,12 @@ function UsersPage() {
setUserStateCode("");
setAprvYn("");
setGrpId("");
setPresetDateRange("");
setDateRange({
from: undefined,
to: undefined,
});
setSearchParams({
...searchParams,
keywordType: "all",
keyword: "",
userStateCode: "",
aprvYn: "",
grpId: "",
frDateYmd: "",
toDateYmd: "",
});
resetParams();
};
const handlePresetDateRange = (value: string) => {
@@ -351,12 +362,9 @@ function UsersPage() {
<h3 className="text-2xl font-medium text-label">회원 목록</h3>
<div className="flex gap-2">
<Select
defaultValue="regDt,desc"
value={params.sort}
onValueChange={(value) => {
setSearchParams({
...searchParams,
sort: value,
});
setParams({ sort: value });
}}
>
<SelectTrigger>
+4 -3
View File
@@ -1,8 +1,9 @@
export { getCookie, setCookie, removeCookie } from "./cookie";
export { useMutationWrapper } from "./use-mutation-wrapper";
export { getCookie, removeCookie, setCookie } from "./cookie";
export { useAlertStore } from "./use-alert-store";
export { useDataTable } from "./use-data-table";
export { useMenuStore } from "./use-menu-store";
export { useUserStore } from "./use-user-store";
export { useMutationWrapper } from "./use-mutation-wrapper";
export { usePrompt } from "./use-prompt";
export { useSearchParamsState } from "./use-search-params-state";
export { useSelectPrompt } from "./use-select-prompt";
export { useUserStore } from "./use-user-store";
@@ -0,0 +1,90 @@
"use client";
import { useCallback, useMemo, useRef } from "react";
import { useSearchParams } from "next/navigation";
/**
* 조회 조건을 URL searchParams와 동기화하는 훅.
*
* - URL이 단일 소스 오브 트루스(SSOT)이며, 새로고침·뒤로가기 시에도 조건이 유지된다.
* - 기본값과 동일한 값은 URL에서 제거해 쿼리스트링을 깨끗하게 유지한다.
* - `window.history.replaceState` 기반 shallow 갱신으로 서버 라운드트립 없이 동작한다.
* - 숫자 기본값이 주어진 키는 자동으로 `Number` 파싱한다.
*
* @example
* const { params, setParams, resetParams } = useSearchParamsState({
* keyword: "",
* page: 0,
* size: 10,
* });
*/
export function useSearchParamsState<T extends Record<string, ParamValue>>(
defaults: T,
): UseSearchParamsStateReturn<T> {
const searchParams = useSearchParams();
const defaultsRef = useRef(defaults);
const params = useMemo(
() => parseParams(searchParams, defaultsRef.current),
[searchParams],
);
const setParams = useCallback((partial: Partial<T>) => {
const next = new URLSearchParams(window.location.search);
for (const [key, value] of Object.entries(partial)) {
if (value === undefined || value === defaultsRef.current[key]) {
next.delete(key);
} else {
next.set(key, String(value));
}
}
replaceUrl(next);
}, []);
const resetParams = useCallback(() => {
const next = new URLSearchParams(window.location.search);
for (const key of Object.keys(defaultsRef.current)) {
next.delete(key);
}
replaceUrl(next);
}, []);
return { params, setParams, resetParams };
}
function parseParams<T extends Record<string, ParamValue>>(
searchParams: ReturnType<typeof useSearchParams>,
defaults: T,
): T {
const result = { ...defaults };
for (const key of Object.keys(defaults) as (keyof T)[]) {
const raw = searchParams?.get(key as string) ?? null;
if (raw === null) continue;
if (typeof defaults[key] === "number") {
const parsed = Number(raw);
if (!Number.isNaN(parsed)) result[key] = parsed as T[keyof T];
} else {
result[key] = raw as T[keyof T];
}
}
return result;
}
function replaceUrl(searchParams: URLSearchParams) {
const qs = searchParams.toString();
const url = qs
? `${window.location.pathname}?${qs}`
: window.location.pathname;
window.history.replaceState(null, "", url);
}
type ParamValue = string | number | null;
interface UseSearchParamsStateReturn<T> {
params: T;
setParams: (partial: Partial<T>) => void;
resetParams: () => void;
}
+11 -6
View File
@@ -1,11 +1,13 @@
import { toast } from "sonner";
import { useEffect } from "react";
import { useRouter, usePathname } from "next/navigation";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useClipboard } from "@frontend-opensource/use-react-hooks";
import { Button } from "@common/components";
import { deleteReasonMessages } from "@/entities/post";
import { AuthorityButton } from "@/entities/menu-authority";
import { toast } from "sonner";
import { useMenuStore, useSelectPrompt, useUserStore } from "@/shared/lib";
import { AuthorityButton } from "@/entities/menu-authority";
import { deleteReasonMessages } from "@/entities/post";
import { Button } from "@common/components";
type PostActionProps = {
bbsId: string;
@@ -21,11 +23,14 @@ const selectData = Object.entries(deleteReasonMessages).map(
function PostAction({ children, handleDelete }: PostActionProps) {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const { copyText, copied } = useClipboard();
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 from = searchParams?.get("from");
const backLink = from ?? postPageLink;
const onDelete = async () => {
const { resolved, value } = await prompt("삭제 사유를 입력해주세요.");
@@ -65,7 +70,7 @@ function PostAction({ children, handleDelete }: PostActionProps) {
size="lg"
color="white"
type="button"
onClick={() => router.push(postPageLink)}
onClick={() => router.push(backLink)}
>
목록
</Button>