191 lines
5.8 KiB
TypeScript
191 lines
5.8 KiB
TypeScript
import dayjs from "dayjs";
|
|
import Image from "next/image";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import type { ColumnDef } from "@tanstack/react-table";
|
|
import { Badge, Checkbox, Skeleton, cn } from "@common/components";
|
|
import { AttachmentApi } from "@/entities/attachment";
|
|
import type { Post } from "@/entities/post";
|
|
import type { BbsType } from "@/shared/store";
|
|
import { ENDPOINT } from "@/shared/config";
|
|
|
|
declare module "@tanstack/react-table" {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
interface TableMeta<TData> {
|
|
bbsTypeCd?: BbsType;
|
|
}
|
|
}
|
|
|
|
export const createColumns = (
|
|
thumbRefs: React.MutableRefObject<Map<number, string>>,
|
|
): ColumnDef<Post>[] => [
|
|
{
|
|
id: "select",
|
|
header: ({ table }) => (
|
|
<Checkbox
|
|
aria-label="전체 선택"
|
|
title="전체 선택"
|
|
checked={table.getIsAllPageRowsSelected()}
|
|
onClick={(e) => e.stopPropagation()}
|
|
onCheckedChange={(checked) =>
|
|
table.toggleAllPageRowsSelected(!!checked)
|
|
}
|
|
className="h-4 w-4 rounded border-gray-300"
|
|
/>
|
|
),
|
|
cell: ({ row, table }) => {
|
|
const { atflCd, pstNo, pstTtl, pstCn, inqNocs, rgtrNm, regDt } =
|
|
row.original;
|
|
const bbsTypeCd = table.options.meta?.bbsTypeCd;
|
|
|
|
if (bbsTypeCd === "BBSIMAGE") {
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex items-center gap-2">
|
|
<Checkbox
|
|
aria-label="행 선택"
|
|
title="행 선택"
|
|
checked={row.getIsSelected()}
|
|
onClick={(e) => e.stopPropagation()}
|
|
onCheckedChange={(checked) => row.toggleSelected(!!checked)}
|
|
className="h-4 w-4 rounded border-gray-300"
|
|
/>
|
|
{inqNocs >= 100 && <Badge color="red">HOT</Badge>}
|
|
{dayjs().diff(dayjs(row.original.regDt), "hour") < 24 && (
|
|
<Badge color="blue">NEW</Badge>
|
|
)}
|
|
<span className="text-[1.6rem] font-bold text-label">
|
|
{pstTtl}
|
|
</span>
|
|
</div>
|
|
<ThumbImage
|
|
atflCd={atflCd}
|
|
pstNo={pstNo}
|
|
thumbRefs={thumbRefs}
|
|
bbsTypeCd={bbsTypeCd}
|
|
/>
|
|
<div className="flex flex-col">
|
|
<span className="text-[1.4rem] font-bold text-label">
|
|
{rgtrNm}
|
|
</span>
|
|
<span className="text-[1.2rem] text-label">
|
|
{dayjs(regDt).format("YYYY-MM-DD")}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (bbsTypeCd === "BBSMOVIE") {
|
|
return (
|
|
<div className="-m-4">
|
|
<ThumbImage
|
|
atflCd={atflCd}
|
|
pstNo={pstNo}
|
|
thumbRefs={thumbRefs}
|
|
bbsTypeCd={bbsTypeCd}
|
|
classes={{
|
|
image: "object-cover",
|
|
}}
|
|
/>
|
|
<div className="p-4">
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex items-center gap-2">
|
|
<Checkbox
|
|
aria-label="행 선택"
|
|
title="행 선택"
|
|
checked={row.getIsSelected()}
|
|
onClick={(e) => e.stopPropagation()}
|
|
onCheckedChange={(checked) => row.toggleSelected(!!checked)}
|
|
className="h-4 w-4 rounded border-gray-300"
|
|
/>
|
|
{inqNocs >= 100 && <Badge color="red">HOT</Badge>}
|
|
{dayjs().diff(dayjs(regDt), "hour") < 24 && (
|
|
<Badge color="blue">NEW</Badge>
|
|
)}
|
|
<span className="text-[1.6rem] font-bold text-label">
|
|
{pstTtl}
|
|
</span>
|
|
</div>
|
|
<p className="line-clamp-3 overflow-hidden text-[1.2rem] text-label">
|
|
{pstCn}
|
|
</p>
|
|
<div className="flex flex-col">
|
|
<span className="text-[1.4rem] font-bold text-label">
|
|
{rgtrNm}
|
|
</span>
|
|
<span className="text-[1.2rem] text-label">
|
|
{dayjs(regDt).format("YYYY-MM-DD")}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
},
|
|
},
|
|
];
|
|
|
|
type ThumbImageProps = {
|
|
atflCd: string;
|
|
pstNo: number;
|
|
thumbRefs: React.MutableRefObject<Map<number, string>>;
|
|
bbsTypeCd?: string;
|
|
classes?: {
|
|
image?: string;
|
|
};
|
|
};
|
|
|
|
const ThumbImage = ({
|
|
atflCd,
|
|
pstNo,
|
|
thumbRefs,
|
|
bbsTypeCd,
|
|
classes,
|
|
}: ThumbImageProps) => {
|
|
const prevImage = thumbRefs.current.get(pstNo);
|
|
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd],
|
|
queryFn: () => {
|
|
if (bbsTypeCd === "BBSIMAGE") {
|
|
return AttachmentApi.getImageUrlByAtflCd(atflCd);
|
|
} else if (bbsTypeCd === "BBSMOVIE") {
|
|
return AttachmentApi.getMovieThumbnailUrlByAtflCd(atflCd);
|
|
}
|
|
},
|
|
select: (url) => {
|
|
if (url) thumbRefs.current.set(pstNo, url);
|
|
return url;
|
|
},
|
|
enabled: !prevImage && !!atflCd,
|
|
});
|
|
|
|
const imageUrl = prevImage ?? data;
|
|
|
|
if (isLoading)
|
|
return (
|
|
<div className="h-[16rem] w-full">
|
|
<Skeleton className="h-full w-full" />
|
|
</div>
|
|
);
|
|
|
|
if (imageUrl)
|
|
return (
|
|
<div className="relative flex h-[16rem] w-full items-center justify-center">
|
|
<Image
|
|
src={imageUrl}
|
|
className={cn("object-contain", classes?.image)}
|
|
fill
|
|
alt="썸네일 이미지"
|
|
/>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className="flex h-[16rem] w-full items-center justify-center rounded-sm border border-border bg-background">
|
|
<i className="diveicon di-image text-3xl" />
|
|
</div>
|
|
);
|
|
};
|