Files
frontend/apps/cs/src/widgets/post/ui/post.tsx
T

270 lines
8.9 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import dayjs from "dayjs";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useMenuStore, useMutationWrapper } from "@/shared/lib";
import { AttachmentApi } from "@/entities/attachment";
import { BoardApi } from "@/entities/board";
import { PostApi } from "@/entities/post";
import {
Button,
Label,
RadioGroup,
RadioGroupItem,
Skeleton,
} from "@common/components";
import { PostEvaluationApi } from "../api/post-evaluation-service";
import { PostAction } from "./post-action";
import { PostComment } from "./post-comment";
function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) {
const router = useRouter();
const alert = useAlertStore((state) => state.setMessage);
const { bbsInfo, setBbsInfo } = useMenuStore((state) => state);
const [filename, setFilename] = useState("");
const [evlScr, setEvlScr] = useState(5);
const { data: boardInfo } = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.BBS, bbsId],
queryFn: () => BoardApi.getBoardInfo(bbsId),
});
const { data: postData } = useQuery({
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo],
queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(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" &&
bbsInfo?.bbsTypeCd !== "BBSMOVIE",
});
const { data: imageUrl, isLoading } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, postData?.atflCd],
queryFn: () =>
AttachmentApi.getImageUrlByAtflCd(postData?.atflCd as string),
enabled: !!postData?.atflCd,
});
const { data: videoAttachment } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, postData?.atflCd],
queryFn: () => AttachmentApi.getAttachmentsInfo(postData?.atflCd as string),
select: (data) => data[0],
enabled: !!postData?.atflCd && bbsInfo?.bbsTypeCd === "BBSMOVIE",
});
const { data: videoUrl } = useQuery({
queryKey: [ENDPOINT.ATTACH_SERVICE.STREAM_VIDEO, videoAttachment?.atflId],
queryFn: () =>
AttachmentApi.getStreamVideoUrlByAtflId(
videoAttachment?.atflId as string,
),
enabled: !!videoAttachment?.atflId,
});
const { mutateAsync: deletePost } = useMutationWrapper({
mutationFn: (delRsn?: string) =>
PostApi.deletePost(parseInt(bbsId), parseInt(pstNo), delRsn),
onSuccess: () => {
alert("삭제되었습니다.", router.back);
},
});
const { mutate: postEvaluation } = useMutationWrapper({
mutationFn: (evlScr: number) =>
PostEvaluationApi.postEvaluation({
pstNo: parseInt(pstNo),
evlScr,
}),
onSuccess: () => {
alert("게시물 평가가 완료되었습니다.");
},
});
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]);
useEffect(() => {
if (!boardInfo) return;
setBbsInfo(boardInfo);
}, [boardInfo]);
return (
<>
<PostAction bbsId={bbsId} pstNo={pstNo} handleDelete={deletePost}>
<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" && (
<>
{isLoading && (
<div className="relative my-6 flex h-[75rem] w-[100rem]">
<Skeleton className="h-full w-full" />
</div>
)}
{imageUrl && (
<div className="relative my-6 flex h-[75rem] w-[100rem]">
<Image
src={imageUrl}
className="object-cover"
fill
alt="썸네일 이미지"
/>
</div>
)}
</>
)}
{bbsInfo?.bbsTypeCd === "BBSMOVIE" && (
<div className="relative my-6 flex h-[56.25rem] w-[100rem]">
<video src={videoUrl} controls className="object-contain" />
</div>
)}
<pre className="min-h-[40rem] whitespace-pre-wrap p-8 font-noto 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>
</>
)
)}
{bbsInfo?.dgstfnEvlUseYn === "Y" && (
<>
<hr />
<div className="mt-8 flex items-center gap-2 px-4">
<p>
이 페이지에서 제공하는
<br />
정보에 대하여 만족하시나요?
</p>
<div className="ml-4 rounded-sm border border-border bg-accent px-8 py-4">
<RadioGroup
value={evlScr.toString()}
onValueChange={(value) => setEvlScr(parseInt(value))}
className="flex items-center space-x-4"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="5" id="evl5" />
<Label htmlFor="evl5">매우 만족 😊</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="4" id="evl4" />
<Label htmlFor="evl4">만족 🙂</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="3" id="evl3" />
<Label htmlFor="evl3">보통 😐</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="2" id="evl2" />
<Label htmlFor="evl2">불만족 🙁</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="1" id="evl1" />
<Label htmlFor="evl1">매우 불만족 😞</Label>
</div>
</RadioGroup>
</div>
<Button
size="lg"
color="white"
onClick={() => postEvaluation(evlScr)}
>
평가하기
</Button>
</div>
</>
)}
</div>
</PostAction>
{bbsInfo?.cmntUseYn === "Y" && (
<PostComment bbsId={parseInt(bbsId)} pstNo={parseInt(pstNo)} />
)}
</>
);
}
export { Post };