last
This commit is contained in:
214
src/app/page.tsx
214
src/app/page.tsx
@@ -1,21 +1,56 @@
|
||||
import { HeroBanner } from "@/app/components/HeroBanner";
|
||||
import HorizontalCardScroller from "@/app/components/HorizontalCardScroller";
|
||||
import CategoryBoardBrowser from "@/app/components/CategoryBoardBrowser";
|
||||
import { PostList } from "@/app/components/PostList";
|
||||
import ProfileLabelIcon from "@/app/svgs/profilelableicon";
|
||||
import SearchIcon from "@/app/svgs/SearchIcon";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function Home({ searchParams }: { searchParams: Promise<{ sort?: "recent" | "popular" } | undefined> }) {
|
||||
const sp = await searchParams;
|
||||
const sort = sp?.sort ?? "recent";
|
||||
|
||||
// 메인페이지 설정 불러오기
|
||||
const SETTINGS_KEY = "mainpage_settings" as const;
|
||||
const settingRow = await prisma.setting.findUnique({ where: { key: SETTINGS_KEY } });
|
||||
const parsed = settingRow ? JSON.parse(settingRow.value as string) : {};
|
||||
const showBanner: boolean = parsed.showBanner ?? true;
|
||||
const showPartnerShops: boolean = parsed.showPartnerShops ?? true;
|
||||
const visibleBoardIds: string[] = Array.isArray(parsed.visibleBoardIds) ? parsed.visibleBoardIds : [];
|
||||
|
||||
// 보드 메타데이터 (이름 표시용)
|
||||
const boardsMeta = visibleBoardIds.length
|
||||
? await prisma.board.findMany({ where: { id: { in: visibleBoardIds } }, select: { id: true, name: true } })
|
||||
: [];
|
||||
const idToMeta = new Map(boardsMeta.map((b) => [b.id, b] as const));
|
||||
const orderedBoards = visibleBoardIds
|
||||
.map((id) => idToMeta.get(id))
|
||||
.filter((v): v is { id: string; name: string } => Boolean(v));
|
||||
const firstTwo = orderedBoards.slice(0, 2);
|
||||
const restBoards = orderedBoards.slice(2);
|
||||
|
||||
const renderBoardPanel = (board: { id: string; name: string }) => (
|
||||
<div key={board.id} className="rounded-xl overflow-hidden h-full min-h-0 flex flex-col bg-white">
|
||||
<div className="px-3 py-2 border-b border-neutral-200 flex items-center justify-between">
|
||||
<a href={`/boards/${board.id}`} className="text-lg md:text-xl font-bold text-neutral-800 truncate">{board.name}</a>
|
||||
<a href={`/boards/${board.id}`} className="text-xs px-3 py-1 rounded-full border border-neutral-300 text-neutral-700 hover:bg-neutral-100">더보기</a>
|
||||
</div>
|
||||
<div className="flex-1 min-h-0 overflow-hidden p-0">
|
||||
<PostList boardId={board.id} sort={sort} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* 히어로 섹션: 상단 대형 비주얼 영역 */}
|
||||
<section>
|
||||
<HeroBanner />
|
||||
</section>
|
||||
{/* 히어로 섹션: 상단 대형 비주얼 영역 (설정 온오프) */}
|
||||
{showBanner && (
|
||||
<section>
|
||||
<HeroBanner />
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* 1행: 커스텀 중앙 50vw 주황 스크롤바 */}
|
||||
{(() => {
|
||||
{/* 제휴 샾 가로 스크롤 (설정 온오프) */}
|
||||
{showPartnerShops && (() => {
|
||||
const items = [
|
||||
{ id: 1, region: "경기도", name: "라온마사지샾", address: "수원시 팔달구 매산로 45", image: "/sample.jpg" },
|
||||
{ id: 2, region: "강원도", name: "휴앤힐링마사지샾", address: "춘천시 중앙로 112", image: "/sample.jpg" },
|
||||
@@ -31,94 +66,103 @@ export default async function Home({ searchParams }: { searchParams: Promise<{ s
|
||||
return <HorizontalCardScroller items={items} />;
|
||||
})()}
|
||||
|
||||
{/* 2행: 고정 높이 610px */}
|
||||
<section className="min-h-[514px] overflow-hidden">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:[grid-template-columns:1fr_2fr_2fr] gap-4 h-full min-h-0">
|
||||
<div className="hidden xl:grid relative overflow-hidden rounded-xl bg-white px-[25px] py-[34px] grid-rows-[120px_120px_1fr] gap-y-[32px] h-full w-full md:min-w-[350px]">
|
||||
<div className="absolute inset-x-0 top-0 h-[56px] bg-[#d5d5d5] z-0" />
|
||||
<div className="h-[120px] flex items-center justify-center relative z-10">
|
||||
<div className="flex items-center justify-center gap-[8px]">
|
||||
<img src="https://picsum.photos/seed/profile/200/200" alt="프로필" className="w-[120px] h-[120px] rounded-full object-cover" />
|
||||
<div className="w-[62px] h-[62px] rounded-full bg-neutral-200 flex items-center justify-center text-[11px] text-neutral-700">
|
||||
Lv
|
||||
{/* 1행: 프로필 + 선택된 보드 2개 (최대 2개) */}
|
||||
{(firstTwo.length > 0) && (
|
||||
<section className="min-h-[514px] overflow-hidden">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:[grid-template-columns:1fr_2fr_2fr] gap-4 h-full min-h-0">
|
||||
<div className="hidden xl:grid relative overflow-hidden rounded-xl bg-white px-[25px] py-[34px] grid-rows-[120px_120px_1fr] gap-y-[32px] h-full w-full md:min-w-[350px]">
|
||||
<div className="absolute inset-x-0 top-0 h-[56px] bg-[#d5d5d5] z-0" />
|
||||
<div className="h-[120px] flex items-center justify-center relative z-10">
|
||||
<div className="flex items-center justify-center gap-[8px]">
|
||||
<img src="https://picsum.photos/seed/profile/200/200" alt="프로필" className="w-[120px] h-[120px] rounded-full object-cover" />
|
||||
<div className="w-[62px] h-[62px] rounded-full bg-neutral-200 flex items-center justify-center text-[11px] text-neutral-700">
|
||||
Lv
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[120px] flex flex-col items-center relative z-10">
|
||||
<div className="text-[18px] text-[#5c5c5c] font-[700] truncate text-center mb-[20px]">홍길동</div>
|
||||
<div className="w-[300px] pl-[67px] flex flex-col gap-[12px]">
|
||||
<div className="grid grid-cols-[64px_auto] gap-x-[24px] items-center h-[16px]">
|
||||
<div className="w-[64px] flex items-center">
|
||||
<ProfileLabelIcon width={16} height={16} />
|
||||
<span className="ml-[8px] text-[12px] text-[#8c8c8c] font-[700]">레벨</span>
|
||||
<div className="h-[120px] flex flex-col items-center relative z-10">
|
||||
<div className="text-[18px] text-[#5c5c5c] font-[700] truncate text-center mb-[20px]">홍길동</div>
|
||||
<div className="w-[300px] pl-[67px] flex flex-col gap-[12px]">
|
||||
<div className="grid grid-cols-[64px_auto] gap-x-[24px] items-center h-[16px]">
|
||||
<div className="w-[64px] flex items-center">
|
||||
<ProfileLabelIcon width={16} height={16} />
|
||||
<span className="ml-[8px] text-[12px] text-[#8c8c8c] font-[700]">레벨</span>
|
||||
</div>
|
||||
<div className="text-[16px] text-[#5c5c5c] font-[700]">Lv. 79</div>
|
||||
</div>
|
||||
<div className="text-[16px] text-[#5c5c5c] font-[700]">Lv. 79</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-[64px_auto] gap-x-[24px] items-center h-[16px]">
|
||||
<div className="w-[64px] flex items-center">
|
||||
<ProfileLabelIcon width={16} height={16} />
|
||||
<span className="ml-[8px] text-[12px] text-[#8c8c8c] font-[700]">등급</span>
|
||||
<div className="grid grid-cols-[64px_auto] gap-x-[24px] items-center h-[16px]">
|
||||
<div className="w-[64px] flex items-center">
|
||||
<ProfileLabelIcon width={16} height={16} />
|
||||
<span className="ml-[8px] text-[12px] text-[#8c8c8c] font-[700]">등급</span>
|
||||
</div>
|
||||
<div className="text-[16px] text-[#5c5c5c] font-[700]">Iron</div>
|
||||
</div>
|
||||
<div className="text-[16px] text-[#5c5c5c] font-[700]">Iron</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-[64px_auto] gap-x-[24px] items-center h-[16px]">
|
||||
<div className="w-[64px] flex items-center">
|
||||
<ProfileLabelIcon width={16} height={16} />
|
||||
<span className="ml-[8px] text-[12px] text-[#8c8c8c] font-[700]">포인트</span>
|
||||
<div className="grid grid-cols-[64px_auto] gap-x-[24px] items-center h-[16px]">
|
||||
<div className="w-[64px] flex items-center">
|
||||
<ProfileLabelIcon width={16} height={16} />
|
||||
<span className="ml-[8px] text-[12px] text-[#8c8c8c] font-[700]">포인트</span>
|
||||
</div>
|
||||
<div className="text-[16px] text-[#5c5c5c] font-[700]">1,600,000</div>
|
||||
</div>
|
||||
<div className="text-[16px] text-[#5c5c5c] font-[700]">1,600,000</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-[12px] relative z-10">
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">내 정보 페이지</span>
|
||||
</span>
|
||||
</button>
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">포인트 히스토리</span>
|
||||
</span>
|
||||
</button>
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">내가 쓴 게시글</span>
|
||||
</span>
|
||||
<span className="absolute right-[8px] w-[47px] h-[18px] rounded-full bg-white text-[#707070] text-[10px] font-[600] leading-[18px] flex items-center justify-end pr-[6px]">12 개</span>
|
||||
</button>
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">내가 쓴 댓글</span>
|
||||
</span>
|
||||
<span className="absolute right-[8px] w-[47px] h-[18px] rounded-full bg-white text-[#707070] text-[10px] font-[600] leading-[18px] flex items-center justify-end pr-[6px]">7 개</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-[12px] relative z-10">
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">내 정보 페이지</span>
|
||||
</span>
|
||||
</button>
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">포인트 히스토리</span>
|
||||
</span>
|
||||
</button>
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">내가 쓴 게시글</span>
|
||||
</span>
|
||||
<span className="absolute right-[8px] w-[47px] h-[18px] rounded-full bg-white text-[#707070] text-[10px] font-[600] leading-[18px] flex items-center justify-end pr-[6px]">12 개</span>
|
||||
</button>
|
||||
<button className="relative w-[300px] h-[32px] rounded-full bg-[#8c8c8c] hover:bg-[#7a7a7a] text-white text-[12px] font-[700] flex items-center">
|
||||
<span className="absolute left-[100px] inline-flex items-center">
|
||||
<SearchIcon width={16} height={16} />
|
||||
<span className="ml-[8px]">내가 쓴 댓글</span>
|
||||
</span>
|
||||
<span className="absolute right-[8px] w-[47px] h-[18px] rounded-full bg-white text-[#707070] text-[10px] font-[600] leading-[18px] flex items-center justify-end pr-[6px]">7 개</span>
|
||||
</button>
|
||||
</div>
|
||||
{firstTwo.map((b) => (
|
||||
<div key={b.id} className="rounded-xl overflow-hidden h-full min-h-0 flex flex-col">
|
||||
{renderBoardPanel(b)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="rounded-xl overflow-hidden h-full min-h-0 flex flex-col">
|
||||
<CategoryBoardBrowser />
|
||||
</div>
|
||||
<div className="flex flex-col rounded-xl overflow-hidden h-full min-h-0">
|
||||
<CategoryBoardBrowser categoryName="명예의 전당" categorySlug="hall-of-fame" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* 3행: 최소/최대 높이 + 내부 스크롤 가능 */}
|
||||
<section className="min-h-[514px] md:h-[620px] overflow-hidden">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 h-full min-h-0">
|
||||
<div className="rounded-xl overflow-hidden h-full min-h-0 flex flex-col">
|
||||
<CategoryBoardBrowser categoryName="소통방" categorySlug="community" />
|
||||
</div>
|
||||
<div className="rounded-xl overflow-hidden h-full min-h-0 flex flex-col">
|
||||
<CategoryBoardBrowser categoryName="제휴업소 정보" categorySlug="partner-info" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{/* 나머지 보드: 2개씩 다음 열로 렌더링 */}
|
||||
{restBoards.length > 0 && (
|
||||
<>
|
||||
{Array.from({ length: Math.ceil(restBoards.length / 2) }).map((_, i) => {
|
||||
const pair = restBoards.slice(i * 2, i * 2 + 2);
|
||||
return (
|
||||
<section key={`rest-${i}`} className="min-h-[514px] md:h-[620px] overflow-hidden">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 h-full min-h-0">
|
||||
{pair.map((b) => (
|
||||
<div key={b.id} className="rounded-xl overflow-hidden h-full min-h-0 flex flex-col">
|
||||
{renderBoardPanel(b)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user