메인스타일, 호버 등

This commit is contained in:
koreacomp5
2025-11-05 22:27:29 +09:00
parent e518c988b2
commit 5b8749d11f
3 changed files with 50 additions and 28 deletions

View File

@@ -218,7 +218,7 @@ export function BoardPanelClient({
// attachments에서 이미지를 먼저 찾고, 없으면 content에서 추출 // attachments에서 이미지를 먼저 찾고, 없으면 content에서 추출
const firstImage = post.attachments?.[0]?.url || extractImageFromContent(post.content); const firstImage = post.attachments?.[0]?.url || extractImageFromContent(post.content);
return ( return (
<Link key={post.id} href={`/posts/${post.id}`} className="flex h-[150px] items-start rounded-[16px] overflow-hidden bg-white"> <Link key={post.id} href={`/posts/${post.id}`} className="group flex h-[150px] items-start rounded-[16px] overflow-hidden bg-white">
<div className="h-[150px] w-[214px] relative shrink-0 bg-[#ededed] overflow-hidden"> <div className="h-[150px] w-[214px] relative shrink-0 bg-[#ededed] overflow-hidden">
{firstImage ? ( {firstImage ? (
<img <img
@@ -248,7 +248,7 @@ export function BoardPanelClient({
</> </>
)} )}
</div> </div>
<span className="text-[16px] leading-[22px] text-[#5c5c5c] truncate">{stripHtml(post.title)}</span> <span className="text-[16px] leading-[22px] text-[#5c5c5c] truncate group-hover:text-[var(--red-50,#F94B37)] group-hover:font-[700]" style={{ fontFamily: "var(--font-family-Font-1, Pretendard)" }}>{stripHtml(post.title)}</span>
{(post.stat?.commentsCount ?? 0) > 0 && ( {(post.stat?.commentsCount ?? 0) > 0 && (
<span className="ml-1 text-[14px] text-[#f45f00] font-bold shrink-0">[{post.stat?.commentsCount}]</span> <span className="ml-1 text-[14px] text-[#f45f00] font-bold shrink-0">[{post.stat?.commentsCount}]</span>
)} )}
@@ -314,9 +314,9 @@ export function BoardPanelClient({
<div className="bg-white px-[24px] pt-[8px] pb-[16px]"> <div className="bg-white px-[24px] pt-[8px] pb-[16px]">
<ul className="min-h-[326px]"> <ul className="min-h-[326px]">
{selectedBoardData.textPosts.map((p) => ( {selectedBoardData.textPosts.map((p) => (
<li key={p.id} className="border-b border-[#ededed] h-[56px] pl-0 pr-[24px] pt-[16px] pb-[16px]"> <li key={p.id} className="border-b border-[#ededed] h-[28px] pl-0 pr-[24px] pt-0 pb-0">
<div className="flex items-center justify-between w-full"> <div className="flex items-center justify-between w-full">
<Link href={`/posts/${p.id}`} className="flex items-center gap-[4px] h-[24px] overflow-hidden flex-1 min-w-0"> <Link href={`/posts/${p.id}`} className="group flex items-center gap-[4px] h-[32px] overflow-hidden flex-1 min-w-0">
<div className="relative w-[16px] h-[16px] shrink-0"> <div className="relative w-[16px] h-[16px] shrink-0">
{isNewWithin1Hour(p.createdAt) && ( {isNewWithin1Hour(p.createdAt) && (
<> <>
@@ -327,7 +327,7 @@ export function BoardPanelClient({
</> </>
)} )}
</div> </div>
<span className="text-[16px] leading-[22px] text-[#5c5c5c] truncate max-w-[calc(100vw-280px)]">{stripHtml(p.title)}</span> <span className="text-[14px] leading-[20px] text-[#5c5c5c] truncate max-w-[calc(100vw-280px)] group-hover:text-[var(--red-50,#F94B37)] group-hover:font-[700]" style={{ fontFamily: "var(--font-family-Font-1, Pretendard)" }}>{stripHtml(p.title)}</span>
{(p.stat?.commentsCount ?? 0) > 0 && ( {(p.stat?.commentsCount ?? 0) > 0 && (
<span className="text-[14px] text-[#f45f00] font-bold">[{p.stat?.commentsCount}]</span> <span className="text-[14px] text-[#f45f00] font-bold">[{p.stat?.commentsCount}]</span>
)} )}
@@ -339,7 +339,7 @@ export function BoardPanelClient({
</ul> </ul>
</div> </div>
) : ( ) : (
<PostList key={board.id} boardId={board.id} sort="recent" /> <PostList key={board.id} boardId={board.id} sort="recent" titleHoverOrange pageSizeOverride={16} compact />
)} )}
</div> </div>
</div> </div>

View File

@@ -35,7 +35,7 @@ function stripHtml(html: string | null | undefined): string {
return html.replace(/<[^>]*>/g, "").trim(); return html.replace(/<[^>]*>/g, "").trim();
} }
export function PostList({ boardId, sort = "recent", q, tag, author, authorId, start, end, variant = "default", newPostHref }: { boardId?: string; sort?: "recent" | "popular"; q?: string; tag?: string; author?: string; authorId?: string; start?: string; end?: string; variant?: "default" | "board"; newPostHref?: string }) { export function PostList({ boardId, sort = "recent", q, tag, author, authorId, start, end, variant = "default", newPostHref, titleHoverOrange, pageSizeOverride, compact }: { boardId?: string; sort?: "recent" | "popular"; q?: string; tag?: string; author?: string; authorId?: string; start?: string; end?: string; variant?: "default" | "board"; newPostHref?: string; titleHoverOrange?: boolean; pageSizeOverride?: number; compact?: boolean }) {
const sp = useSearchParams(); const sp = useSearchParams();
const listContainerRef = useRef<HTMLDivElement | null>(null); const listContainerRef = useRef<HTMLDivElement | null>(null);
const [lockedMinHeight, setLockedMinHeight] = useState<number | null>(null); const [lockedMinHeight, setLockedMinHeight] = useState<number | null>(null);
@@ -43,7 +43,9 @@ export function PostList({ boardId, sort = "recent", q, tag, author, authorId, s
// board 변형에서는 URL에서 pageSize를 읽고, 기본값은 20 // board 변형에서는 URL에서 pageSize를 읽고, 기본값은 20
const defaultPageSize = variant === "board" ? 20 : 10; const defaultPageSize = variant === "board" ? 20 : 10;
const pageSizeParam = sp.get("pageSize"); const pageSizeParam = sp.get("pageSize");
const pageSize = pageSizeParam ? Math.min(50, Math.max(10, parseInt(pageSizeParam, 10))) : defaultPageSize; const pageSize = (variant === "board" && pageSizeOverride)
? pageSizeOverride
: (pageSizeParam ? Math.min(50, Math.max(10, parseInt(pageSizeParam, 10))) : defaultPageSize);
// board 변형: 번호 페이지네이션 // board 변형: 번호 페이지네이션
const initialPage = useMemo(() => Math.max(1, parseInt(sp.get("page") || "1", 10)), [sp]); const initialPage = useMemo(() => Math.max(1, parseInt(sp.get("page") || "1", 10)), [sp]);
@@ -168,15 +170,17 @@ export function PostList({ boardId, sort = "recent", q, tag, author, authorId, s
<div ref={listContainerRef} style={{ minHeight: lockedMinHeight ? `${lockedMinHeight}px` : undefined }}> <div ref={listContainerRef} style={{ minHeight: lockedMinHeight ? `${lockedMinHeight}px` : undefined }}>
<ul className="divide-y divide-[#ececec]"> <ul className="divide-y divide-[#ececec]">
{items.map((p) => ( {items.map((p) => (
<li key={p.id} className={`px-4 ${variant === "board" ? "py-2.5" : "py-3 md:py-3"} hover:bg-neutral-50 transition-colors`}> <li key={p.id} className={`px-4 ${variant === "board" ? (compact ? "py-1.5" : "py-2.5") : "py-3 md:py-3"} hover:bg-neutral-50 transition-colors`}>
<div className="grid grid-cols-1 md:grid-cols-[20px_1fr_120px_120px_80px] items-center gap-2"> <div className="grid grid-cols-1 md:grid-cols-[20px_1fr_120px_120px_80px] items-center gap-2">
{/* bullet/공지 아이콘 자리 */} {/* bullet/공지 아이콘 자리 */}
<div className="hidden md:flex items-center justify-center text-[#f94b37]">{p.isPinned ? "★" : "•"}</div> <div className="hidden md:flex items-center justify-center text-[#f94b37]">{p.isPinned ? "★" : "•"}</div>
<div className="min-w-0"> <div className="min-w-0">
<Link href={`/posts/${p.id}`} className="block truncate text-[15px] md:text-base text-neutral-900"> <Link href={`/posts/${p.id}`} className={`group block truncate text-neutral-900`}>
{p.isPinned && <span className="mr-2 inline-flex items-center rounded bg-orange-100 text-orange-700 px-1.5 py-0.5 text-[11px]"></span>} {p.isPinned && <span className="mr-2 inline-flex items-center rounded bg-orange-100 text-orange-700 px-1.5 py-0.5 text-[11px]"></span>}
{stripHtml(p.title)} <span className={`${titleHoverOrange ? "group-hover:text-[var(--red-50,#F94B37)] group-hover:font-[700] group-hover:text-[16px] group-hover:leading-[22px]" : ""} ${compact ? "text-[14px] leading-[20px]" : "text-[15px] md:text-base"}`} style={{ fontFamily: "var(--font-family-Font-1, Pretendard)" }}>
{stripHtml(p.title)}
</span>
{(p.stat?.commentsCount ?? 0) > 0 && ( {(p.stat?.commentsCount ?? 0) > 0 && (
<span className="ml-1 text-[12px] md:text-[12px] text-[#f94b37] align-middle">[{p.stat?.commentsCount}]</span> <span className="ml-1 text-[12px] md:text-[12px] text-[#f94b37] align-middle">[{p.stat?.commentsCount}]</span>
)} )}

View File

@@ -57,6 +57,14 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s
if (admin) currentUser = admin; if (admin) currentUser = admin;
} }
// 내가 쓴 게시글/댓글 수
let myPostsCount = 0;
let myCommentsCount = 0;
if (currentUser) {
myPostsCount = await prisma.post.count({ where: { authorId: currentUser.userId, status: "published" } });
myCommentsCount = await prisma.comment.count({ where: { authorId: currentUser.userId } });
}
// 메인페이지 설정 불러오기 // 메인페이지 설정 불러오기
const SETTINGS_KEY = "mainpage_settings" as const; const SETTINGS_KEY = "mainpage_settings" as const;
const settingRow = await prisma.setting.findUnique({ where: { key: SETTINGS_KEY } }); const settingRow = await prisma.setting.findUnique({ where: { key: SETTINGS_KEY } });
@@ -164,7 +172,7 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s
where: { boardId: sb.id, status: "published" }, where: { boardId: sb.id, status: "published" },
select: { id: true, title: true, createdAt: true, stat: { select: { recommendCount: true, commentsCount: true } } }, select: { id: true, title: true, createdAt: true, stat: { select: { recommendCount: true, commentsCount: true } } },
orderBy: { createdAt: "desc" }, orderBy: { createdAt: "desc" },
take: 7, take: 16,
}); });
} }
// 기본 타입은 PostList가 자체적으로 API를 호출하므로 데이터 미리 가져오지 않음 // 기본 타입은 PostList가 자체적으로 API를 호출하므로 데이터 미리 가져오지 않음
@@ -270,28 +278,38 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s
</div> </div>
</div> </div>
<div className="flex flex-col gap-[12px] relative z-10"> <div className="flex flex-col gap-[12px] relative z-10">
<Link href="/my-page" className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center justify-center"> <Link href="/my-page" className="w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#5c5c5c] text-white text-[12px] font-[700] flex items-center px-[12px]">
<span className="inline-flex items-center"> <span className="flex items-center w-full pl-[88px]">
<SearchIcon width={16} height={16} /> <span className="flex items-center gap-[8px]">
<span className="ml-[8px]"> </span> <SearchIcon width={16} height={16} />
<span> </span>
</span>
</span> </span>
</Link> </Link>
<Link href="/my-page?tab=points" className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center"> <Link href="/my-page?tab=points" className="w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#5c5c5c] text-white text-[12px] font-[700] flex items-center px-[12px]">
<span className="absolute left-[100px] inline-flex items-center"> <span className="flex items-center w-full pl-[88px]">
<SearchIcon width={16} height={16} /> <span className="flex items-center gap-[8px]">
<span className="ml-[8px]"> </span> <SearchIcon width={16} height={16} />
<span> </span>
</span>
</span> </span>
</Link> </Link>
<Link href={`/my-page?tab=posts`} className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center"> <Link href={`/my-page?tab=posts`} className="w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#5c5c5c] text-white text-[12px] font-[700] flex items-center px-[12px]">
<span className="absolute left-[100px] inline-flex items-center"> <span className="flex items-center w-full pl-[88px]">
<SearchIcon width={16} height={16} /> <span className="flex items-center gap-[8px]">
<span className="ml-[8px]"> </span> <SearchIcon width={16} height={16} />
<span> </span>
</span>
<span className="ml-auto inline-flex items-center justify-center h-[20px] px-[8px] rounded-full bg-white text-[#5c5c5c] text-[12px] leading-[20px] shrink-0">{myPostsCount.toLocaleString()}</span>
</span> </span>
</Link> </Link>
<Link href={`/my-page?tab=comments`} className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center"> <Link href={`/my-page?tab=comments`} className="w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#5c5c5c] text-white text-[12px] font-[700] flex items-center px-[12px]">
<span className="absolute left-[100px] inline-flex items-center"> <span className="flex items-center w-full pl-[88px]">
<SearchIcon width={16} height={16} /> <span className="flex items-center gap-[8px]">
<span className="ml-[8px]"> </span> <SearchIcon width={16} height={16} />
<span> </span>
</span>
<span className="ml-auto inline-flex items-center justify-center h-[20px] px-[8px] rounded-full bg-white text-[#5c5c5c] text-[12px] leading-[20px] shrink-0">{myCommentsCount.toLocaleString()}</span>
</span> </span>
</Link> </Link>
</div> </div>