diff --git a/next.config.mjs b/next.config.mjs index 690d2d0d..0b29d3f6 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,7 +1,22 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - /* config options here */ - reactCompiler: true, + images: { + domains: [ + "example.com", + "sprint-fe-project.s3.ap-northeast-2.amazonaws.com", + "st.depositphotos.com", + "i.pinimg.com", + "upload.wikimedia.org", + "cdn.wccftech.com", + "encrypted-tbn0.gstatic.com", + "image.hanatour.com", + "cdn.choicenews.co.kr", + "health.chosun.com", + "via.placeholder.com", + "i.imgur.com", + "images.samsung.com", + ], + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 7e70fe8d..1ab633a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "sprint8", "version": "0.1.0", "dependencies": { + "@tanstack/react-query": "^5.90.7", "clsx": "^2.1.1", "dayjs": "^1.11.19", "next": "16.0.1", @@ -17,6 +18,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@tanstack/eslint-plugin-query": "^5.91.2", "autoprefixer": "^10.4.21", "babel-plugin-react-compiler": "1.0.0", "eslint": "^9", @@ -1477,6 +1479,49 @@ "tailwindcss": "4.1.16" } }, + "node_modules/@tanstack/eslint-plugin-query": { + "version": "5.91.2", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.91.2.tgz", + "integrity": "sha512-UPeWKl/Acu1IuuHJlsN+eITUHqAaa9/04geHHPedY8siVarSaWprY0SVMKrkpKfk5ehRT7+/MZ5QwWuEtkWrFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.44.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.7", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.7.tgz", + "integrity": "sha512-6PN65csiuTNfBMXqQUxQhCNdtm1rV+9kC9YwWAIKcaxAauq3Wu7p18j3gQY3YIBJU70jT/wzCCZ2uqto/vQgiQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.7", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.7.tgz", + "integrity": "sha512-wAHc/cgKzW7LZNFloThyHnV/AX9gTg3w5yAv0gvQHPZoCnepwqCMtzbuPbb2UvfvO32XZ46e8bPOYbfZhzVnnQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", diff --git a/package.json b/package.json index 2718077b..65abc780 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "@tanstack/react-query": "^5.90.7", "clsx": "^2.1.1", "dayjs": "^1.11.19", "next": "16.0.1", @@ -18,6 +19,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@tanstack/eslint-plugin-query": "^5.91.2", "autoprefixer": "^10.4.21", "babel-plugin-react-compiler": "1.0.0", "eslint": "^9", diff --git a/src/Components/common/Header.jsx b/src/Components/common/Header.jsx index 1967c469..62cade4e 100644 --- a/src/Components/common/Header.jsx +++ b/src/Components/common/Header.jsx @@ -1,8 +1,16 @@ +"use client"; import Image from "next/image"; import PandaLogo from "@/public/images/Group19.png"; import Link from "next/link"; +import { useAuth } from "@/providers/AuthProvider"; +import { useRouter } from "next/navigation"; export default function Header() { + const { user, logout, isInitialized } = useAuth(); + const router = useRouter(); + + if (!isInitialized) return null; + return (
@@ -16,25 +24,48 @@ export default function Header() {
- - 로그인 - + +
+ {user ? ( +
+ 프로필 이미지 + + {user.nickname} 님 + + +
+ ) : ( + + )} +
); } diff --git a/src/Components/ui/ProductsList/Pagination.jsx b/src/Components/ui/ProductsList/Pagination.jsx new file mode 100644 index 00000000..7c6641e9 --- /dev/null +++ b/src/Components/ui/ProductsList/Pagination.jsx @@ -0,0 +1,39 @@ +"use client"; +import { IoIosArrowBack } from "react-icons/io"; +import { IoIosArrowForward } from "react-icons/io"; + +export default function Pagination({ page, totalPages, onChange }) { + const pages = Array.from({ length: totalPages }, (_, i) => i + 1); + + return ( +
+ + + {pages.map((num) => ( + + ))} + + +
+ ); +} diff --git a/src/Components/ui/ProductsList/ProductsBest.jsx b/src/Components/ui/ProductsList/ProductsBest.jsx new file mode 100644 index 00000000..16119074 --- /dev/null +++ b/src/Components/ui/ProductsList/ProductsBest.jsx @@ -0,0 +1,47 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { getProducts } from "@/lib/services/products"; +import ProductsCard from "./ProductsCard"; + +export default function ProductsBest() { + const [bestProducts, setBestProducts] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + async function fetchBest() { + try { + setLoading(true); + const data = await getProducts({ + orderBy: "favorite", + page: 1, + pageSize: 4, + }); + setBestProducts(data.list); + } catch (error) { + console.error("베스트 상품 불러오기 실패:", error); + } finally { + setLoading(false); + } + } + fetchBest(); + }, []); + + return ( +
+

+ 베스트 상품 +

+ + {loading ? ( +

로딩 중...

+ ) : ( + + )} +
+ ); +} diff --git a/src/Components/ui/ProductsList/ProductsCard.jsx b/src/Components/ui/ProductsList/ProductsCard.jsx new file mode 100644 index 00000000..bf18ac1d --- /dev/null +++ b/src/Components/ui/ProductsList/ProductsCard.jsx @@ -0,0 +1,57 @@ +"use client"; +import Image from "next/image"; +import React, { useState } from "react"; +import heart from "@/public/images/ic_heart.png"; +import defaultImg from "@/public/images/eximg.png"; +import Link from "next/link"; + +export default function ProductsCard({ + id, + name, + description, + images, + favoriteCount, + price, +}) { + console.log("상품 ID:", id, "이미지 데이터:", images); + const [currentImg, setCurrentImg] = useState(images?.[0] || defaultImg); + + return ( +
  • + +
    + {name} setCurrentImg(defaultImg)} + // unoptimized Image 도메인 최적화 제한 해제 ! + /> +
    +

    + {description} +

    +

    + {price ? Number(price).toLocaleString() : 0}원 +

    +
    + 하트이모티콘 +

    + {favoriteCount} +

    +
    +
    +
    + +
  • + ); +} diff --git a/src/Components/ui/ProductsList/ProductsDetailPage.jsx b/src/Components/ui/ProductsList/ProductsDetailPage.jsx new file mode 100644 index 00000000..968ebd7a --- /dev/null +++ b/src/Components/ui/ProductsList/ProductsDetailPage.jsx @@ -0,0 +1,113 @@ +"use client"; +import Image from "next/image"; +import OptionsMenu from "../button/OptionsMenu"; +import { useAuth } from "@/providers/AuthProvider"; +import defaultImg from "@/public/images/profile.png"; +import heart from "@/public/images/heart.png"; +import ReserveButton from "../button/ReserveButton"; +import ProductsComment from "../comments/ProductsComment"; + +export default function ProductsDetailPage({ product }) { + const { + name, + description, + images, + price, + tags, + ownerId, + favoriteCount, + ownerNickname, + date, + } = product; + + const { user } = useAuth(); + + console.log("title:", name); + return ( + <> +
    +
    + 이미지 +
    +
    +

    + {name} +

    + +
    +

    + {price ? Number(price).toLocaleString() : 0}원 +

    +
    +

    + 상품소개 +

    +

    + {description} +

    +
    +
    +

    상품태그

    +
    + {tags.map((tag, i) => { + return ( + + #{tag} + + ); + })} +
    +
    + +
    + 프로필 이미지 + +
    +
    +

    + {ownerNickname} +

    +

    + {date} +

    +
    + +
    + 찬하트 +

    + {favoriteCount} +

    +
    +
    +
    +
    +
    +
    +
    + +
    + + + ); +} diff --git a/src/Components/ui/ProductsList/ProductsList.jsx b/src/Components/ui/ProductsList/ProductsList.jsx new file mode 100644 index 00000000..a17a84d4 --- /dev/null +++ b/src/Components/ui/ProductsList/ProductsList.jsx @@ -0,0 +1,52 @@ +"use client"; + +import { useEffect, useState } from "react"; +import ProductsSearchBar from "./ProductsSearchBar"; +import { getProducts } from "@/lib/services/products"; +import ProductsCard from "./ProductsCard"; +import Pagination from "./Pagination"; + +export default function ProductsList() { + const [products, setProducts] = useState([]); + const [loading, setLoading] = useState(false); + const [orderBy, setOrderBy] = useState("recent"); + const [keyword, setKeyword] = useState(""); + const [page, setPage] = useState(1); + const totalPages = 5; + + useEffect(() => { + console.log("현재 검색 키워드:", keyword); + async function fetchData() { + if (page > totalPages) return; + try { + setLoading(true); + const data = await getProducts({ orderBy, keyword, page }); + console.log("api 데이타", data); + setProducts(data.list); + } catch (error) { + console.error("상품 불러오기 실패:", error); + } finally { + setLoading(false); + } + } + fetchData(); + }, [orderBy, keyword, page]); + + return ( +
    + + + {loading ? ( +

    로딩중...

    + ) : ( +
      + {products.map((item) => ( + + ))} +
    + )} + + +
    + ); +} diff --git a/src/Components/ui/ProductsList/ProductsSearchBar.jsx b/src/Components/ui/ProductsList/ProductsSearchBar.jsx new file mode 100644 index 00000000..32273b8d --- /dev/null +++ b/src/Components/ui/ProductsList/ProductsSearchBar.jsx @@ -0,0 +1,86 @@ +import Image from "next/image"; +import React, { useState } from "react"; +import { IoMdArrowDropdown } from "react-icons/io"; +import SearchIcon from "@/public/images/search.png"; +import Link from "next/link"; + +export default function ProductsSearchBar({ onSearch, onOrderChange }) { + const [isOpen, setIsOpen] = useState(false); + const [inputValue, setInputValue] = useState(""); + const [select, setSelect] = useState("최신순"); + + const handleSearch = (e) => { + const inputItem = e.target.value; + setInputValue(inputItem); + console.log("검색 입력:", inputItem); + onSearch(inputItem); + }; + + const handleSelect = (order, label) => { + setSelect(label); + onOrderChange(order); + setIsOpen(false); + }; + + return ( + <> +
    +
    +

    + 판매 중인 상품 +

    +
    +
    +
    + searchIcon + +
    + + + + +
    + + + {isOpen && ( +
      +
    • handleSelect("recent", "최신순")} + className="flex items-center justify-start px-5 py-3 cursor-pointer " + > + 최신순 +
    • +
    • handleSelect("favorite", "좋아요순")} + className="flex items-center justify-start px-5 py-3 cursor-pointer border-t border-gray-200 " + > + 좋아요순 +
    • +
    + )} +
    +
    +
    + + ); +} diff --git a/src/Components/ui/button/LoginBtn.jsx b/src/Components/ui/button/LoginBtn.jsx deleted file mode 100644 index 53af4984..00000000 --- a/src/Components/ui/button/LoginBtn.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -export default function LoginBtn() { - return ( - - ); -} diff --git a/src/Components/ui/button/OptionsMenu.jsx b/src/Components/ui/button/OptionsMenu.jsx new file mode 100644 index 00000000..43c292ed --- /dev/null +++ b/src/Components/ui/button/OptionsMenu.jsx @@ -0,0 +1,16 @@ +import React from "react"; +import { BsThreeDotsVertical } from "react-icons/bs"; + +export default function OptionsMenu() { + return ( +
    + +
    + ); +} diff --git a/src/Components/ui/button/ReserveButton.jsx b/src/Components/ui/button/ReserveButton.jsx index 185d864e..d01f6863 100644 --- a/src/Components/ui/button/ReserveButton.jsx +++ b/src/Components/ui/button/ReserveButton.jsx @@ -1,12 +1,12 @@ import Link from "next/link"; import React from "react"; -export default function ReserveButton() { +export default function ReserveButton({ href = "/" }) { return (
    목록으로 돌아가기 diff --git a/src/Components/ui/comments/ProductsComment.jsx b/src/Components/ui/comments/ProductsComment.jsx new file mode 100644 index 00000000..1b0cd2d6 --- /dev/null +++ b/src/Components/ui/comments/ProductsComment.jsx @@ -0,0 +1,102 @@ +"use client"; +import React, { useState } from "react"; +import OptionsMenu from "../button/OptionsMenu"; +import Image from "next/image"; +import profile from "@/public/images/profile.png"; +import { useAuth } from "@/providers/AuthProvider"; +import { timeAgo } from "@/lib/utils/time"; + +export default function ProductsComment() { + const [content, setContent] = useState(""); + const [comments, setComments] = useState([]); + + const { user } = useAuth(); + + const handleInput = (e) => { + setContent(e.target.value); + }; + + const handleKeyDown = (e) => { + if (e.key === "Enter") { + e.preventDefault(); // ✔ 줄바꿈 방지 + handleSubmit(); + } + }; + + const handleSubmit = () => { + const cleanContent = content.trim(); + + if (cleanContent === "") { + console.log("공백은 등록할 수 없습니다!"); + return; + } + + setComments((prev) => [...prev, { text: cleanContent, date: new Date() }]); + + setContent(""); + }; + + return ( + <> +
    +

    문의하기

    + +
    + +
    +
    +
    +
      + {comments.map((comment, i) => ( +
    • +
      +
      +

      {comment.text}

      + +
      + +
      +
      + 프로필 이미지 +
      + +
      +

      + {user?.nickname || "비로그인 사용자!"} +

      + +

      + {timeAgo(comment.date)} +

      +
      +
      +
      +
    • + ))} +
    +
    + + ); +} diff --git a/src/Components/ui/modal/Modal.jsx b/src/Components/ui/modal/Modal.jsx new file mode 100644 index 00000000..127ca50e --- /dev/null +++ b/src/Components/ui/modal/Modal.jsx @@ -0,0 +1,21 @@ +"use client"; +import React from "react"; + +export default function Modal({ isOpen, message, onClose }) { + if (!isOpen) return null; + + return ( +
    +
    +

    {message}

    + + +
    +
    + ); +} diff --git a/src/app/(auth)/_components/_login/LoginInput.jsx b/src/app/(auth)/_components/_login/LoginInput.jsx index 8edbaa1d..1118c16e 100644 --- a/src/app/(auth)/_components/_login/LoginInput.jsx +++ b/src/app/(auth)/_components/_login/LoginInput.jsx @@ -1,8 +1,13 @@ "use client"; +import { authService } from "@/lib/services/auth"; import clsx from "clsx"; import Image from "next/image"; +import { useRouter } from "next/navigation"; import React, { useState } from "react"; import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai"; +import EasyLogin from "./EasyLogin"; +import { useAuth } from "@/providers/AuthProvider"; +import Modal from "@/components/ui/modal/Modal"; export default function LoginInput() { const [showEye, setShowEye] = useState(false); @@ -10,13 +15,20 @@ export default function LoginInput() { const [emailError, setEmailError] = useState(""); const [password, setPassword] = useState(""); const [passwordError, setPasswordError] = useState(""); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + const [modalOpen, setModalOpen] = useState(false); + const [modalMessage, setModalMessage] = useState(""); + const { login } = useAuth(); + const router = useRouter(); const handleEmail = (e) => { - setEmail(e.target.value); + const emailValue = e.target.value; + setEmail(emailValue); const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (e.target.value && !emailRegex.test(e.target.value)) { + if (emailValue && !emailRegex.test(emailValue)) { setEmailError("잘못된 이메일입니다!"); } else { setEmailError(""); @@ -24,12 +36,13 @@ export default function LoginInput() { }; const handlePassword = (e) => { - setPassword(e.target.value); + const passwordValue = e.target.value; + setPassword(passwordValue); const passwordRegEx = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/; - if (e.target.value && !passwordRegEx.test(e.target.value)) { + if (passwordValue && !passwordRegEx.test(passwordValue)) { setPasswordError( "비밀번호는 8자 이상, 영문·숫자·특수문자를 포함해야 합니다!" ); @@ -38,8 +51,31 @@ export default function LoginInput() { } }; + const handleSubmit = async (e) => { + e.preventDefault(); + + if (emailError || passwordError) { + setError("정보확인요망!"); + return; + } + + try { + setLoading(true); + + const result = await login(email, password); + setModalMessage("로그인 성공!"); + setModalOpen(true); + // router.push("/items"); + } catch (error) { + setModalMessage("로그인에 실패했습니다!"); + setModalOpen(true); + } finally { + setLoading(false); + } + }; + return ( -
    +

    이메일 @@ -51,11 +87,11 @@ export default function LoginInput() { placeholder="이메일을 입력해주세요." className={clsx( "border-0 bg-gray-100 rounded-xl w-[640px] h-14 px-6 py-4", - { "outline-1 outline-red-400": emailError } + { "outline-1 outline-[#F74747]": emailError } )} /> {emailError && ( -

    {emailError}

    +

    {emailError}

    )}
    @@ -70,14 +106,11 @@ export default function LoginInput() { placeholder="비밀번호를 입력해주세요" className={clsx( "border-0 bg-gray-100 w-[640px] items-center rounded-xl h-14 px-6 py-4 ", - { "outline-1 outline-red-400": passwordError } + { "outline-1 outline-[#F74747]": passwordError } )} /> - {passwordError && ( -

    {passwordError}

    - )}
    + + {passwordError && ( +

    {passwordError}

    + )}
    + + + {error &&

    {error}

    } + setModalOpen(false)} + /> ); } diff --git a/src/app/(auth)/_components/_signup/Signp.jsx b/src/app/(auth)/_components/_signup/Signp.jsx index 42f98960..260c3370 100644 --- a/src/app/(auth)/_components/_signup/Signp.jsx +++ b/src/app/(auth)/_components/_signup/Signp.jsx @@ -1,23 +1,123 @@ "use client"; +import { authService } from "@/lib/services/auth"; +import clsx from "clsx"; import Image from "next/image"; +import { useRouter } from "next/navigation"; import React, { useState } from "react"; import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai"; export default function SignInput() { const [showEye, setShowEye] = useState(false); + const [email, setEmail] = useState(""); + const [nickname, setNickname] = useState(""); + const [emailError, setEmailError] = useState(""); + const [password, setPassword] = useState(""); + const [passwordError, setPasswordError] = useState(""); + const [passwordConfirm, setPasswordConfirm] = useState(""); + const [confirmError, setConfirmError] = useState(""); + const [error, setError] = useState(""); + const [loading, setLoading] = useState(false); + const router = useRouter(); + + //email 검증 + const handleEmail = (e) => { + const emailValue = e.target.value; + setEmail(emailValue); + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + + if (emailValue && !emailRegex.test(emailValue)) { + setEmailError("잘못된 이메일입니다."); + } else { + setEmailError(""); + } + }; + + const handleNickName = (e) => { + const nickName = e.target.value; + setNickname(nickName); + }; + + //password 검증 + const handlePassword = (e) => { + const passwordValue = e.target.value; + setPassword(passwordValue); + + const passwordRegEx = + /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/; + + if (passwordValue && !passwordRegEx.test(passwordValue)) { + setPasswordError( + "비밀번호는 8자 이상, 영문·숫자·특수문자를 포함해야 합니다!" + ); + } else { + setPasswordError(""); + } + }; + + //password 확인 + const handlePasswordConfirm = (e) => { + const confirmValue = e.target.value; + setPasswordConfirm(confirmValue); + + if (confirmValue !== password) { + setConfirmError("비밀번호가 일치하지 않습니다."); + } else { + setConfirmError(""); + } + }; + + const handleSignup = async (e) => { + e.preventDefault(); + + if (emailError || passwordError || confirmError) { + setError("입력하신 정보를 다시 확인해주세요."); + return; + } + if (!email || !password || !nickname || !passwordConfirm) { + setError("입력해주세요!"); + return; + } + try { + setError(null); + setLoading(true); + + const result = await authService.signUp( + email, + nickname, + password, + passwordConfirm + ); + console.log("회원가입 완료", result); + alert("회원가입 완료!"); + router.push("/login"); + } catch (error) { + setError(error.message || "회원가입에 실패했습니다."); + } finally { + setLoading(false); + } + }; return ( -
    +

    이메일

    + {emailError && ( +

    {emailError}

    + )}

    @@ -25,6 +125,8 @@ export default function SignInput() {

    @@ -37,8 +139,13 @@ export default function SignInput() {
    + {passwordError && ( +

    {passwordError}

    + )}
    @@ -61,8 +171,13 @@ export default function SignInput() {
    + {confirmError && ( +

    {confirmError}

    + )}
    - -
    + ); } diff --git a/src/app/(auth)/login/page.jsx b/src/app/(auth)/login/page.jsx index 21e798db..8d723f5b 100644 --- a/src/app/(auth)/login/page.jsx +++ b/src/app/(auth)/login/page.jsx @@ -1,6 +1,5 @@ import React from "react"; import LoginInput from "../_components/_login/LoginInput"; -import LoginBtn from "../../../components/ui/button/LoginBtn"; import PandaLogo from "../../../components/ui/logo/PandaLogo"; import EasyLogin from "../_components/_login/EasyLogin"; @@ -10,7 +9,6 @@ export default function LoginPage() {
    -
    diff --git a/src/app/(main)/articles/[id]/page.jsx b/src/app/(main)/articles/[id]/page.jsx index 9597edc1..7c4fc251 100644 --- a/src/app/(main)/articles/[id]/page.jsx +++ b/src/app/(main)/articles/[id]/page.jsx @@ -1,12 +1,10 @@ "use client"; import React, { useEffect, useState } from "react"; import { useParams, useRouter } from "next/navigation"; -import { BsThreeDotsVertical } from "react-icons/bs"; import { getArticleById } from "@/lib/services/articles"; import Image from "next/image"; -import { Flamenco } from "next/font/google"; -import Link from "next/link"; import ReserveButton from "@/components/ui/button/ReserveButton"; +import OptionsMenu from "@/components/ui/button/OptionsMenu"; export default function ArticleDetail() { const { id } = useParams(); @@ -36,13 +34,7 @@ export default function ArticleDetail() {

    {title}

    - +
    @@ -104,13 +96,7 @@ export default function ArticleDetail() {

    혹시 사용기간이 어떻게 되실까요!?

    - +
    @@ -132,7 +118,7 @@ export default function ArticleDetail() {
    - + ); } diff --git a/src/app/(main)/items/[id]/page.jsx b/src/app/(main)/items/[id]/page.jsx new file mode 100644 index 00000000..b20eacf0 --- /dev/null +++ b/src/app/(main)/items/[id]/page.jsx @@ -0,0 +1,16 @@ +import ProductsDetailPage from "@/components/ui/ProductsList/ProductsDetailPage"; +import { getProductById } from "@/lib/services/products"; +import React from "react"; + +export default async function ItemPage({ params }) { + const { id } = await params; + const product = await getProductById(id); + + return ( + <> +
    + +
    + + ); +} diff --git a/src/app/(main)/items/page.jsx b/src/app/(main)/items/page.jsx new file mode 100644 index 00000000..1cfedf77 --- /dev/null +++ b/src/app/(main)/items/page.jsx @@ -0,0 +1,15 @@ +import ProductsBest from "@/components/ui/ProductsList/ProductsBest"; +import ProductsList from "@/components/ui/ProductsList/ProductsList"; + +export default function ItemsPage() { + return ( +
    +
    + +
    +
    + +
    +
    + ); +} diff --git a/src/app/(main)/items/write/page.jsx b/src/app/(main)/items/write/page.jsx new file mode 100644 index 00000000..f479db09 --- /dev/null +++ b/src/app/(main)/items/write/page.jsx @@ -0,0 +1,5 @@ +import React from "react"; + +export default function page() { + return
    페이지 글쓰기
    ; +} diff --git a/src/app/(main)/layout.jsx b/src/app/(main)/layout.jsx index 523850be..8ef1f882 100644 --- a/src/app/(main)/layout.jsx +++ b/src/app/(main)/layout.jsx @@ -1,13 +1,16 @@ +"use client"; + import Footer from "@/components/common/Footer"; import Header from "@/components/common/Header"; +// import AuthProvider from "@/providers/AuthProvider"; import React from "react"; export default function MainLayout({ children }) { return ( <>
    -
    -
    {children}
    +
    +
    {children}