Skip to content

Commit

Permalink
Merge pull request #8 from InQ-InQ-InQ-InQ-InQ/feature-7
Browse files Browse the repository at this point in the history
[FEAT] : 프로필, 검색결과 화면 UI 구현
  • Loading branch information
yummjin authored Feb 9, 2025
2 parents a72c064 + 8e3e738 commit 5a7234f
Show file tree
Hide file tree
Showing 25 changed files with 203 additions and 26 deletions.
14 changes: 14 additions & 0 deletions src/app/(withNavbar)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Nav } from "@/widgets/menu/ui";
import React from "react";

export default function layout({ children }: { children: React.ReactNode }) {
return (
<div className="flex min-h-screen flex-col">
<Nav />
<div className="flex size-full p-4 pb-0">{children}</div>
<footer className="bottom-0 mt-auto flex-shrink-0 p-4 py-6 text-center text-gray-600">
© 2024 SpotShare. All rights reserved.
</footer>
</div>
);
}
25 changes: 25 additions & 0 deletions src/app/(withNavbar)/profile/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ProfileLayout } from "@/widgets/profile/ui";

interface ProfileParams {
params: {
id: string;
};
}

export default async function Page({ params }: ProfileParams) {
const awaitedParams = await Promise.resolve(params);
return <ProfileLayout id={awaitedParams.id} />;
}

// async function getProfile(id: string) {
// const response = await fetch(`/api/profile/${id}`);
// const data = await response.json();
// return data;
// }

export async function generateMetadata({ params }: ProfileParams) {
const awaitedParams = await Promise.resolve(params);
return {
description: `${awaitedParams.id}의 공유 Spots`,
};
}
6 changes: 6 additions & 0 deletions src/app/(withNavbar)/search/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { SearchLayout } from "@/widgets/search/ui";
import React from "react";

export default function page() {
return <SearchLayout />;
}
2 changes: 2 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
--color-third: rgb(255, 229, 123);
--color-gray: rgb(153, 153, 153);
--color-gray-600: rgb(130, 130, 130);
--color-bgGray: rgb(244, 244, 244);
--color-bgGray-50: rgb(250, 250, 250);
}

:root {
Expand Down
10 changes: 0 additions & 10 deletions src/app/profile/page.tsx

This file was deleted.

1 change: 1 addition & 0 deletions src/shared/model/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useModal";
File renamed without changes.
1 change: 1 addition & 0 deletions src/shared/model/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./hooks";
31 changes: 31 additions & 0 deletions src/shared/ui/Post.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client";

import React, { useState } from "react";
import { IoIosHeartEmpty, IoIosHeart, IoMdShare } from "react-icons/io";

export default function Post() {
return (
<div className="flex h-60 flex-col rounded-lg border">
<div className="bg-bgGray flex-1 rounded-t-lg">photo</div>
<PostButton />
</div>
);
}

function PostButton() {
const [liked, setLiked] = useState(false);
return (
<div className="flex w-full justify-between rounded-b-lg bg-white p-2">
<div className="flex justify-center gap-x-1">
<button
className="hover:text-gray cursor-pointer"
onClick={() => setLiked((prev) => !prev)}
>
{liked ? <IoIosHeart color="red" /> : <IoIosHeartEmpty />}
</button>
<span className="text-sm">1</span>
</div>
<IoMdShare />
</div>
);
}
21 changes: 21 additions & 0 deletions src/shared/ui/PostView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import Post from "./Post";

export default function PostView() {
return (
<div className="grid size-full flex-1 grid-cols-2 gap-4 bg-white p-4 md:grid-cols-4 lg:grid-cols-6">
<Post />
<Post />
<Post />
<Post />
<Post />
<Post />
<Post />
<Post />
<Post />
<Post />
<Post />
<Post />
</div>
);
}
15 changes: 9 additions & 6 deletions src/shared/ui/ProfileButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import React, { useState } from "react";
import ProfileModal from "./ProfileModal";
import { cn } from "@/shared/utils";
import { useModal } from "../model";

interface ProfileProps {
className?: string;
Expand All @@ -13,21 +13,24 @@ export default function ProfileButton({
className,
flex = false,
}: ProfileProps) {
const [isVisible, setVisible] = useState(false);
const { visible, setVisible, modalRef } = useModal();

return (
<div
className={cn("z-10 w-fit", className, flex ? "flex" : "absolute")}
onClick={() => setVisible(!isVisible)}
onClick={() => setVisible((prev) => !prev)}
// onMouseEnter={() => setVisible(true)}
// onMouseLeave={() => setVisible(false)}
>
<button className="border-border-color/80 size-8 cursor-pointer rounded-[50%] border bg-white">
<button className="size-8 cursor-pointer rounded-[50%] border bg-white">
K
</button>
<div
ref={modalRef}
className={cn(
"top-9 right-0 h-fit w-30",
isVisible ? "absolute" : "hidden",
"h-fit w-30",
visible ? "absolute" : "hidden",
flex ? "top-14 right-2" : "top-9 right-0",
)}
>
<ProfileModal />
Expand Down
2 changes: 2 additions & 0 deletions src/shared/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export { default as TextField } from "./TextField";
export { default as Button } from "./Button";
export { default as LoginJoinHeader } from "./LoginJoinHeader";
export { default as LoginJoinContainer } from "./LoginJoinContainer";
export { default as Post } from "./Post";
export { default as PostView } from "./PostView";
1 change: 0 additions & 1 deletion src/widgets/home/model/hooks/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/widgets/home/model/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "./constants";
export * from "./hooks";
3 changes: 2 additions & 1 deletion src/widgets/home/ui/HomeRegionButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"use client";

import React, { useState } from "react";
import { regions, useModal } from "@/widgets/home/model";
import { regions } from "@/widgets/home/model";
import { RegionMapModal } from "@/widgets/home/ui";
import { Region } from "@/widgets/home/types";
import { useModal } from "@/shared/model";

interface RegionButtonProps {
name: Region;
Expand Down
29 changes: 22 additions & 7 deletions src/widgets/menu/ui/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,37 @@
import { ProfileButton } from "@/shared/ui";
"use client";

import Link from "next/link";
import React from "react";
import { ProfileButton } from "@/shared/ui";
import React, { useState } from "react";
import { IoIosSearch } from "react-icons/io";
import { IoAddSharp } from "react-icons/io5";
import { cn } from "@/shared/utils";

export default function Nav() {
const [opened, setOpened] = useState(false);
return (
<nav className="sticky top-0 w-full bg-white">
<div className="flex h-12 w-full items-center justify-between px-4">
<Link href="/">
<span className="prompt-extrabold text-2xl">SpotShare</span>
</Link>
<div className="flex gap-x-2">
<Link href="/">
<IoAddSharp className="size-7.5 pt-1" />
</Link>
<div className="flex items-center gap-x-2">
<input
type="text"
placeholder="검색어를 입력하세요"
className={cn(
"w-full rounded-lg border px-2 py-1 text-sm focus:outline-none",
opened ? "block" : "hidden",
)}
/>
<button
className="cursor-pointer"
onClick={() => setOpened((prev) => !prev)}
>
<IoIosSearch size={24} className="hover:text-gray" />
</button>
<Link href="/">
<IoIosSearch className="size-7 pt-1" />
<IoAddSharp size={24} className="hover:text-gray" />
</Link>
<ProfileButton flex={true} />
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/widgets/profile/api/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const PROFILE_ENDPOINT = {
FOLLOWER_COUNT: "/api/users/{id}/follower/count",
FOLLOWING_COUNT: "/api/users/{id}/following/count",
POSTS: "/api/users/{id}/posts",
} as const;

export default PROFILE_ENDPOINT;
1 change: 1 addition & 0 deletions src/widgets/profile/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./endpoint";
1 change: 1 addition & 0 deletions src/widgets/profile/model/constants/content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const CONTENT = ["팔로워", "팔로잉", "게시글"];
1 change: 1 addition & 0 deletions src/widgets/profile/model/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./content";
1 change: 1 addition & 0 deletions src/widgets/profile/model/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./constants";
41 changes: 41 additions & 0 deletions src/widgets/profile/ui/ProfileLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { PostView } from "@/shared/ui";
import React, { type ReactNode } from "react";
import { CONTENT } from "@/widgets/profile/model";

export default function ProfileLayout({ id }: { id: string }) {
const ProfileHeader = (): ReactNode => (
<div className="flex w-full gap-x-4 bg-white px-18 py-10">
<div className="flex size-12 items-center justify-center rounded-[50%] border bg-white text-2xl md:size-30 md:text-6xl">
K
</div>
<div className="flex h-full flex-col justify-center">
<p className="text-4xl font-bold">{id}</p>
<p className="text-gray font-medium">@{id}</p>
<button className="mt-2 cursor-pointer rounded-md border py-1 transition duration-300 ease-in-out hover:bg-gray-600 hover:text-white">
프로필 편집
</button>
</div>
</div>
);

const infoContent = CONTENT;

const ProfileInfo = (): ReactNode => (
<div className="bg-bgGray-50 flex gap-x-4 px-18 py-6 text-lg">
{infoContent.map((info) => (
<div className="text-center" key={info}>
<p className="font-bold">{info}</p>
<p>1</p>
</div>
))}
</div>
);

return (
<div className="size-full flex-col">
<ProfileHeader />
<ProfileInfo />
<PostView />
</div>
);
}
1 change: 1 addition & 0 deletions src/widgets/profile/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ProfileLayout } from "./ProfileLayout";
13 changes: 13 additions & 0 deletions src/widgets/search/ui/SearchLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { PostView } from "@/shared/ui";
import React from "react";

export default function SearchLayout() {
return (
<div className="flex size-full flex-col">
<p className="p-4 pt-0 text-xl font-extrabold md:text-2xl">
&quot;&quot; 에 대한 검색 결과입니다.
</p>
<PostView />
</div>
);
}
1 change: 1 addition & 0 deletions src/widgets/search/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as SearchLayout } from "./SearchLayout";

0 comments on commit 5a7234f

Please sign in to comment.