Files
frontend/apps/common/features/src/widgets/post/ui/post-page.tsx
T
2026-10-06 14:32:26 +09:00

288 lines
8.7 KiB
TypeScript

"use client";
import { useRef, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
useReactTable,
type PaginationState,
type RowSelectionState,
} from "@tanstack/react-table";
import {
Button,
DataTable,
Input,
Label,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Skeleton,
} from "@common/components";
import {
PostApi,
PostPageGuard,
type Post,
type PostParams,
} from "@/entities/post";
import { ENDPOINT } from "@/shared/config";
import { useMenuStore, useUserStore } from "@/shared/lib";
import { GridViewDataTable } from "@/shared/ui/grid-view-data-table";
import { columns as tableColumns } from "../model/post-table-columns";
import { createColumns as createGridViewColumns } from "../model/grid-view-table-columns";
import { SortSelect } from "./sort-select";
function PostPage({
bbsId,
tableCaption,
}: {
bbsId: string;
tableCaption?: string;
}) {
const router = useRouter();
const pathname = usePathname();
const { confirm } = useConfirm();
const userInfo = useUserStore((state) => state.userInfo);
const { bbsInfo, pageMenuAuthority } = useMenuStore((state) => state);
const thumbRefs = useRef<Map<number, string>>(new Map());
const [tableSort, setTableSort] = useState("");
const [keywordType, setKeywordType] = useState("title");
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<PostParams>({
keywordType: "title",
keyword: "",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const { data: upndFixPostList } = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, bbsInfo],
queryFn: () =>
PostApi.getPostList(parseInt(bbsId), {
...searchParams,
upndFixYn: bbsInfo?.upndFixUseYn,
}),
select: (data) => {
const newContent = data.content.map((post) => ({
...post,
isUpndFixPost: post.upndFixYn === "Y",
}));
return {
...data,
content: newContent,
};
},
enabled: bbsInfo?.upndFixUseYn === "Y",
});
const {
data: postList,
isLoading,
refetch,
} = useQuery({
queryKey: [
ENDPOINT.BOARD_SERVICE.POSTS_USER,
bbsId,
searchParams,
pagination,
tableSort,
],
queryFn: () =>
PostApi.getPostList(parseInt(bbsId), {
...searchParams,
ansUseYn: bbsInfo?.ansUseYn,
page: pagination.pageIndex,
size: pagination.pageSize,
sort: tableSort,
}),
});
const isGridViewPage =
bbsInfo?.bbsTypeCd === "BBSIMAGE" || bbsInfo?.bbsTypeCd === "BBSMOVIE";
const table = useReactTable({
data: isGridViewPage
? (postList?.content ?? [])
: [...(upndFixPostList?.content ?? []), ...(postList?.content ?? [])],
columns: isGridViewPage ? createGridViewColumns(thumbRefs) : tableColumns,
state: { pagination, rowSelection },
rowCount: postList?.totalElements ?? 0,
manualPagination: true,
enableMultiRowSelection: true,
getCoreRowModel: getCoreRowModel(),
onPaginationChange: setPagination,
onRowSelectionChange: setRowSelection,
meta: { bbsTypeCd: bbsInfo?.bbsTypeCd },
});
const onPostDelete = async () => {
const selectedPosts = table
.getFilteredSelectedRowModel()
.rows.map((row) => row.original.pstNo);
if (await confirm("삭제하시겠습니까?")) {
setRowSelection({});
await Promise.all(
selectedPosts.map((pstNo) =>
PostApi.deletePost(parseInt(bbsId), pstNo, userInfo?.userUnqId),
),
);
refetch();
}
};
const handleSubmit = (e: React.SubmitEvent<HTMLFormElement>) => {
e.preventDefault();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keywordType,
keyword,
});
};
const handleReset = () => {
setKeywordType("title");
setKeyword("");
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParams({
...searchParams,
keywordType: "title",
keyword: "",
});
};
const handleRowClick = (row: Post) => {
router.push(`${pathname}/${row.pstNo}`);
};
return (
<PostPageGuard bbsId={bbsId}>
<div className="card card-border !mt-6">
<div className="card card-border">
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
<Label className="mr-2 text-label">검색</Label>
<Select value={keywordType} onValueChange={setKeywordType}>
<SelectTrigger className="h-[3.2rem] w-[10rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="title">제목</SelectItem>
<SelectItem value="all">제목+내용</SelectItem>
<SelectItem value="writer">작성자</SelectItem>
</SelectContent>
</Select>
<Input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
<Button
type="button"
color="white"
className="h-[3.2rem]"
onClick={handleReset}
>
초기화
</Button>
</form>
</div>
<div className="card card-border">
<div className="card-header">
<div className="flex w-full items-center justify-between">
<h3 className="text-2xl font-medium text-label">콘텐츠 목록</h3>
<div className="flex gap-2">
{pageMenuAuthority?.regAuthrtYn === "Y" && (
<Button onClick={() => router.push(`${pathname}/register`)}>
등록
</Button>
)}
<Select
value={table.getState().pagination.pageSize.toString()}
onValueChange={(value) => {
table.setPageSize(parseInt(value));
}}
>
<SelectTrigger className="h-[3.2rem] w-[12rem]">
<SelectValue placeholder="페이지 크기" />
</SelectTrigger>
<SelectContent>
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={pageSize.toString()}>
{pageSize}개씩 보기
</SelectItem>
))}
</SelectContent>
</Select>
{bbsInfo?.ansUseYn === "N" && (
<SortSelect value={tableSort} onValueChange={setTableSort} />
)}
</div>
</div>
</div>
<div className="card !m-[1.2rem] h-full overflow-auto !p-0">
{isLoading ? (
<Skeleton className="h-[36rem] w-full" />
) : (
isGridViewPage ? (
<GridViewDataTable
table={table}
onRowClick={handleRowClick}
viewPagination
footerContent={
pageMenuAuthority?.delAuthrtYn === "Y" ? (
<Button
color="red"
onClick={onPostDelete}
disabled={!table.getFilteredSelectedRowModel().rows.length}
>
선택삭제
</Button>
) : undefined
}
/>
) : (
<DataTable
table={table}
onRowClick={handleRowClick}
viewPagination
caption={tableCaption}
footerContent={
pageMenuAuthority?.delAuthrtYn === "Y" ? (
<Button
color="red"
onClick={onPostDelete}
disabled={!table.getFilteredSelectedRowModel().rows.length}
>
선택삭제
</Button>
) : undefined
}
/>
)
)}
</div>
</div>
</div>
</PostPageGuard>
);
}
export { PostPage };