Files
frontend/apps/common/features/src/widgets/post/ui/post.tsx
T

173 lines
5.3 KiB
TypeScript

"use client";
import dayjs from "dayjs";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Button, Skeleton } from "@common/components";
import { AttachmentApi } from "@/entities/attachment";
import { PostApi } from "@/entities/post";
import {
useAlertStore,
useUserStore,
useMenuStore,
usePrompt,
} from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { PostAction } from "./post-action";
import { PostComment } from "./post-comment";
function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) {
const router = useRouter();
const { prompt } = usePrompt();
const { setMessage: alert } = useAlertStore((state) => state);
const { userInfo } = useUserStore((state) => state);
const { bbsInfo } = useMenuStore((state) => state);
const [filename, setFilename] = useState("");
const { data: postData } = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo],
queryFn: () => PostApi.getPostInfo(bbsId, pstNo),
});
const { data: downloadResponse, isFetching } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.DOWNLOAD, postData?.atflCd],
queryFn: () =>
AttachmentApi.downloadAttachmentByAtflCd(postData?.atflCd as string),
enabled: !!postData?.atflCd && bbsInfo?.bbsTypeCd !== "BBSIMAGE",
});
const { data: imageUrl, isLoading } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, postData?.atflCd],
queryFn: () =>
AttachmentApi.getImageUrlByAtflCd(postData?.atflCd as string),
enabled: !!postData?.atflCd,
});
const { mutate: deletePost } = useMutation({
mutationFn: (pstPswd: string) => PostApi.deletePost(bbsId, pstNo, pstPswd),
onSuccess: () => {
alert("삭제되었습니다.", router.back);
},
});
const handleDelete = async () => {
let pstPswd = "";
if (userInfo?.userUnqId) {
pstPswd = userInfo?.userUnqId;
} else {
pstPswd = await prompt("비밀번호를 입력해주세요.");
}
deletePost(pstPswd);
};
const handleDownload = async () => {
if (!downloadResponse) return;
try {
const blob = new Blob([downloadResponse.data]);
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename || "download";
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(link);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
if (!downloadResponse) return;
try {
let filename = "";
const contentDisposition =
downloadResponse.headers["content-disposition"];
if (contentDisposition) {
const filenameMatch = contentDisposition.match(
/filename\*=UTF-8''([^;]+)/,
);
if (filenameMatch && filenameMatch[1]) {
filename = decodeURIComponent(filenameMatch[1]);
}
}
setFilename(filename);
} catch {
setFilename("");
}
}, [downloadResponse]);
return (
<>
<PostAction bbsId={bbsId} pstNo={pstNo} handleDelete={handleDelete}>
<div className="card card-border !p-8">
<h3 className="text-[2rem] font-bold text-foreground">
{postData?.pstTtl}
</h3>
<div className="card !m-0 !mt-8 bg-accent">
<ul className="flex gap-8 p-4 text-xl text-label">
<li>
작성자: <b>{postData?.wrtrNm}</b>
</li>
<li>
등록일: <b>{dayjs(postData?.regDt).format("YYYY-MM-DD")}</b>
</li>
<li>
조회수: <b>{postData?.inqNocs}</b>
</li>
</ul>
</div>
<hr className="mt-8" />
{bbsInfo?.bbsTypeCd === "BBSIMAGE" && (
<div className="relative my-6 flex h-[75rem] w-[100rem]">
{isLoading && <Skeleton className="h-full w-full" />}
{imageUrl && (
<Image
src={imageUrl}
className="object-cover"
fill
alt="썸네일 이미지"
/>
)}
</div>
)}
<pre className="font-noto min-h-[40rem] whitespace-pre-wrap p-8 text-2xl tracking-tighter text-foreground">
{postData?.pstCn ?? ""}
</pre>
{isFetching ? (
<div className="p-8">
<Skeleton className="h-[2.5rem] w-[32rem]" />
</div>
) : (
filename && (
<>
<hr />
<div className="flex items-center gap-4 p-8">
<span className="font-bold text-label">첨부파일</span>
<Button
size="sm"
color="white"
className="flex w-fit items-center gap-2"
onClick={handleDownload}
>
<i className="diveicon di-download" />
<span>{filename}</span>
</Button>
</div>
</>
)
)}
</div>
</PostAction>
<PostComment bbsId={parseInt(bbsId)} pstNo={parseInt(pstNo)} />
</>
);
}
export { Post };