From 3af1d68b850ee27f49ae314894297d20050fea8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B5=ED=9D=AC=20=EA=B9=80?= Date: Sat, 18 Jan 2025 00:43:36 +0900 Subject: [PATCH] =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 22 ++ package-lock.json | 22 ++ package.json | 1 + src/app/(user)/cart/page.jsx | 45 ++-- src/app/(user)/layout.jsx | 6 +- src/app/(user)/products/page.jsx | 20 +- src/app/(user)/search/page.jsx | 17 ++ src/app/admin/chats/page.jsx | 2 +- .../customPaginationSilder/index.jsx | 198 ++++++++++-------- src/components/chat.jsx | 31 ++- 10 files changed, 234 insertions(+), 130 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4ab2e3c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#fbed80", + "activityBar.background": "#fbed80", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#06b9a5", + "activityBarBadge.foreground": "#15202b", + "commandCenter.border": "#15202b99", + "sash.hoverBorder": "#fbed80", + "statusBar.background": "#f9e64f", + "statusBar.foreground": "#15202b", + "statusBarItem.hoverBackground": "#f7df1e", + "statusBarItem.remoteBackground": "#f9e64f", + "statusBarItem.remoteForeground": "#15202b", + "titleBar.activeBackground": "#f9e64f", + "titleBar.activeForeground": "#15202b", + "titleBar.inactiveBackground": "#f9e64f99", + "titleBar.inactiveForeground": "#15202b99" + }, + "peacock.color": "#f9e64f" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7e2269f..bf81bb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "react-countdown": "^2.3.5", "react-dom": "^18.3.1", "react-dropzone": "^14.2.3", + "react-easy-panzoom": "^0.4.4", "react-hot-toast": "^2.4.1", "react-icons": "^4.12.0", "react-otp-input": "^3.1.0", @@ -6637,6 +6638,18 @@ "react": ">= 16.8 || 18.0.0" } }, + "node_modules/react-easy-panzoom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/react-easy-panzoom/-/react-easy-panzoom-0.4.4.tgz", + "integrity": "sha512-1zgT6boDVPcrR3Egcz8KEVpM3fs50o22iIWPRlAqvev0/4nw5RnUNFsvmOJ/b5M2nd8MDGknLmyfBdhjoLB6+g==", + "license": "MIT", + "dependencies": { + "warning": "4.0.3" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, "node_modules/react-fast-compare": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", @@ -8008,6 +8021,15 @@ "node": ">= 6" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index cf6e227..e5e70a7 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "react-countdown": "^2.3.5", "react-dom": "^18.3.1", "react-dropzone": "^14.2.3", + "react-easy-panzoom": "^0.4.4", "react-hot-toast": "^2.4.1", "react-icons": "^4.12.0", "react-otp-input": "^3.1.0", diff --git a/src/app/(user)/cart/page.jsx b/src/app/(user)/cart/page.jsx index b4125d3..28e1b28 100644 --- a/src/app/(user)/cart/page.jsx +++ b/src/app/(user)/cart/page.jsx @@ -1,3 +1,5 @@ +'use client'; + import React from 'react'; // mui @@ -5,38 +7,27 @@ import { Container } from '@mui/material'; // component import CartMain from 'src/components/_main/cart'; -import HeaderBreadcrumbs from 'src/components/headerBreadcrumbs'; +import { useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import { useRouter } from 'next-nprogress-bar'; + -// Meta information -export const metadata = { - title: 'Nextall Shopping Cart | Nextall - Convenient Shopping Cart for Easy Checkout', - description: - 'View your shopping cart on Nextall for easy checkout. Add, remove, and manage items effortlessly. Enjoy a seamless shopping experience with secure transactions and personalized recommendations. Explore your cart now!', - applicationName: 'Nextall', - authors: 'Nextall', - keywords: - 'shopping cart, Nextall, view cart, cart items, add to cart, remove from cart, manage cart, checkout, online shopping, secure transactions, personalized recommendations, seamless shopping, convenient shopping' -}; export default async function Cart() { + const router = useRouter(); + const { isAuthenticated, user } = useSelector(({ user }) => user); + useEffect(() => { + if (isAuthenticated === false) { + router.push('/auth/login'); + } else { + if (user.role === 'vendor') { + alert('셀러는 사용자 화면을 사용 할 수 없습니다. 관리 화면으로 이동합니다'); + router.push('/vendor/dashboard'); + } + } + }, [isAuthenticated]); return ( - {/* */} ); diff --git a/src/app/(user)/layout.jsx b/src/app/(user)/layout.jsx index 29f916b..5e0f6a3 100644 --- a/src/app/(user)/layout.jsx +++ b/src/app/(user)/layout.jsx @@ -11,10 +11,10 @@ import ActionBar from 'src/layout/_main/actionbar'; // Meta information export const metadata = { - title: 'PICSHOP', + title: 'UIE AI Accessories', description: 'AI Accessories', - applicationName: 'PICSHOP', - authors: 'PICSHOP', + applicationName: 'UIE AI Accessories', + authors: 'UIE', keywords: '', icons: { icon: '/favicon.png' diff --git a/src/app/(user)/products/page.jsx b/src/app/(user)/products/page.jsx index df69013..98705d2 100644 --- a/src/app/(user)/products/page.jsx +++ b/src/app/(user)/products/page.jsx @@ -1,11 +1,27 @@ +'use client'; // mui import { Box, Container } from '@mui/material'; - +import { useRouter } from 'next-nprogress-bar'; +import { useSelector } from 'react-redux'; // components // import HeaderBreadcrumbs from 'src/components/headerBreadcrumbs'; +import { useEffect } from 'react'; import ProductList from 'src/components/_main/products'; -export default async function Listing() { +export default function Listing() { + const router = useRouter(); + const { isAuthenticated, user } = useSelector(({ user }) => user); + useEffect(() => { + if (isAuthenticated === false) { + router.push('/auth/login'); + } else { + if (user.role === 'vendor') { + alert('셀러는 사용자 화면을 사용 할 수 없습니다. 관리 화면으로 이동합니다') + router.push('/vendor/dashboard'); + } + }; + + }, [isAuthenticated]); return ( diff --git a/src/app/(user)/search/page.jsx b/src/app/(user)/search/page.jsx index 96def42..f361709 100644 --- a/src/app/(user)/search/page.jsx +++ b/src/app/(user)/search/page.jsx @@ -1,9 +1,26 @@ +'use client'; + import React from 'react'; import dynamic from 'next/dynamic'; +import { useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import { useRouter } from 'next-nprogress-bar'; // dynamic import const Search = dynamic(() => import('src/components/dialog/search/search')); export default function Searchs() { + const router = useRouter(); + const { isAuthenticated, user } = useSelector(({ user }) => user); + useEffect(() => { + if (isAuthenticated === false) { + router.push('/auth/login'); + } else { + if (user.role === 'vendor') { + alert('셀러는 사용자 화면을 사용 할 수 없습니다. 관리 화면으로 이동합니다'); + router.push('/vendor/dashboard'); + } + } + }, [isAuthenticated]); return ; } diff --git a/src/app/admin/chats/page.jsx b/src/app/admin/chats/page.jsx index eddf99a..fcebc06 100644 --- a/src/app/admin/chats/page.jsx +++ b/src/app/admin/chats/page.jsx @@ -29,7 +29,7 @@ function AdminChatPage() { return () => { socket.off('message'); }; - }, []); + }, [message]); const sendMessage = () => { diff --git a/src/components/carousels/customPaginationSilder/index.jsx b/src/components/carousels/customPaginationSilder/index.jsx index d59241a..ddd257f 100644 --- a/src/components/carousels/customPaginationSilder/index.jsx +++ b/src/components/carousels/customPaginationSilder/index.jsx @@ -13,6 +13,9 @@ import { motion, AnimatePresence } from 'framer-motion'; import RootStyled from './styled'; import ChatApp from 'src/components/chat'; +import PanZoom from 'react-easy-panzoom'; +import Image from 'next/image'; + const variants = { enter: (direction) => { return { @@ -47,81 +50,93 @@ const swipePower = (offset, velocity) => { // ---------------------------------------------------------------------- ProductDetailsCarousel.propTypes = { + slug: PropTypes.string, item: PropTypes.object.isRequired, meta: PropTypes.string }; function ProductDetailsCarousel({ ...props }) { - const { item, meta } = props; + const { slug, item, meta } = props; const [modal, setModal] = useState(); + const [priceTag, setPriceTag] = useState(true); // JSON 문자열인 meta를 객체 배열로 파싱 const annotations = meta && typeof meta === 'string' ? JSON.parse(meta) : meta; return ( -
- {item && ( - - )} - - {/* 저장된 금액 표시 */} - - {annotations ? annotations.map((annotation, idx) => ( - - )) : ''} - { modal && - - - - setModal(false)} - sx={{ - position: 'absolute', - right: 5, - top: 5, - zIndex: 111 - }} - > - - - - - - - } +
+
+ + + {item && ( + hero-carousel + )} + + {/* 저장된 금액 표시 */} + + {annotations + ? annotations.map((annotation, idx) => ( + + )) + : ''} + {modal && ( + + + + setModal(false)} + sx={{ + position: 'absolute', + right: 5, + top: 5, + zIndex: 111 + }} + > + + + + + + + )} +
); } - export default function CarouselAnimation({ ...props }) { - const { product, data } = props; + const { slug, product, data } = props; const images = product?.images; const [[page, direction], setPage] = useState([0, 0]); @@ -133,36 +148,36 @@ export default function CarouselAnimation({ ...props }) { return ( -
- - { - const swipe = swipePower(offset.x, velocity.x); - if (swipe < -swipeConfidenceThreshold) { - paginate(1); - } else if (swipe > swipeConfidenceThreshold) { - paginate(-1); - } - }} - > - - - - + + { + const swipe = swipePower(offset.x, velocity.x); + if (swipe < -swipeConfidenceThreshold) { + paginate(1); + } else if (swipe > swipeConfidenceThreshold) { + paginate(-1); + } + }} + > + + + + {/* ))} - -
+ */} +
); } CarouselAnimation.propTypes = { + slug: PropTypes.string, product: PropTypes.object, data: PropTypes.object }; diff --git a/src/components/chat.jsx b/src/components/chat.jsx index d792c6a..c8aa51b 100644 --- a/src/components/chat.jsx +++ b/src/components/chat.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import { Stack } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { io } from 'socket.io-client'; @@ -5,7 +6,13 @@ import { useSelector } from 'react-redux'; const socket = io(process.env.BASE_URL); // 서버 URL -function ChatApp() { + +ChatApp.propTypes = { + slug: PropTypes.string +}; + +function ChatApp({ ...props }) { + const { slug } = props; const { user } = useSelector(({ user }) => user); const userEmail = user.email; const adminEmail = 'arkiun@naver.com'; // 관리자 이메일 @@ -18,22 +25,34 @@ function ChatApp() { socket.emit('join', userEmail); // 기존 메시지 가져오기 - fetch(`${process.env.BASE_URL}/v1/chat/messages?email=${username},${adminEmail}`) - .then((res) => res.json()) - .then((data) => setMessages(data)); + fetch(`${process.env.BASE_URL}/v1/chat/messages?email=${userEmail},${adminEmail}`) + .then((res) => { + if (!res.ok) { + throw new Error('Failed to fetch messages'); + } + return res.json(); + }) + .then((data) => setMessages(data)) + .catch((error) => { + console.error('Error fetching messages:', error); + }); // 실시간 메시지 수신 socket.on('message', (data) => { setMessages((prev) => [...prev, data]); }); + // Cleanup return () => { socket.off('message'); }; - }, [username, adminEmail]); + }, [userEmail, adminEmail, slug, username, messages]); const sendMessage = () => { - const msg = { username: username, message, targetEmail: adminEmail }; // 관리자 이메일 + const trimmedMessage = message.trim(); + if (trimmedMessage === '') return; + + const msg = { username, message: trimmedMessage, targetEmail: adminEmail }; socket.emit('message', msg); setMessage(''); };