업데이트
This commit is contained in:
parent
e0959e1d15
commit
3af1d68b85
22
.vscode/settings.json
vendored
Normal file
22
.vscode/settings.json
vendored
Normal file
@ -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"
|
||||
}
|
22
package-lock.json
generated
22
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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 (
|
||||
<Container maxWidth="xl">
|
||||
{/* <HeaderBreadcrumbs
|
||||
heading="Cart"
|
||||
links={[
|
||||
{
|
||||
name: 'Home',
|
||||
href: '/'
|
||||
},
|
||||
{
|
||||
name: 'Products',
|
||||
href: '/products'
|
||||
},
|
||||
{
|
||||
name: 'Cart'
|
||||
}
|
||||
]}
|
||||
/> */}
|
||||
<CartMain />
|
||||
</Container>
|
||||
);
|
||||
|
@ -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'
|
||||
|
@ -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 (
|
||||
<Box>
|
||||
<Box sx={{ bgcolor: 'background.default' }}>
|
||||
|
@ -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 <Search mobile />;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ function AdminChatPage() {
|
||||
return () => {
|
||||
socket.off('message');
|
||||
};
|
||||
}, []);
|
||||
}, [message]);
|
||||
|
||||
|
||||
const sendMessage = () => {
|
||||
|
@ -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 (
|
||||
<div className="slide-wrapper">
|
||||
{item && (
|
||||
<BlurImage
|
||||
priority
|
||||
fill
|
||||
objectFit="cover"
|
||||
sizes="50%"
|
||||
src={item?.url || item?.src}
|
||||
alt="hero-carousel"
|
||||
blurDataURL={item.blurDataURL}
|
||||
/>
|
||||
)}
|
||||
<Box className="bg-overlay" />
|
||||
{/* 저장된 금액 표시 */}
|
||||
|
||||
{annotations ? annotations.map((annotation, idx) => (
|
||||
<Button
|
||||
key={idx}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: `${annotation.y * 100}%`, // 상대값을 퍼센트로 변환
|
||||
left: `${annotation.x * 100}%`,
|
||||
transform: 'translate(-50%, -50%)', // 중앙 정렬
|
||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||
color: '#fff',
|
||||
padding: '2px 5px',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
onClick={()=>setModal(true)}
|
||||
>
|
||||
<Typography>
|
||||
{annotation.price}
|
||||
</Typography>
|
||||
</Button>
|
||||
)) : ''}
|
||||
{ modal &&
|
||||
<Modal open={modal}>
|
||||
<Stack direction={'column'} sx={{backgroundColor:'#fff'}}>
|
||||
<Stack direction='row'>
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={()=>setModal(false)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: 5,
|
||||
top: 5,
|
||||
zIndex: 111
|
||||
}}
|
||||
>
|
||||
<MdClear />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
<ChatApp />
|
||||
</Stack>
|
||||
</Modal>
|
||||
}
|
||||
<div>
|
||||
<div className="slide-wrapper">
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => setPriceTag(!priceTag)}
|
||||
sx={{ position: 'absolute', right: 0, top: 0, zIndex: 3 }}
|
||||
>
|
||||
가격표시
|
||||
</Button>
|
||||
|
||||
{item && (
|
||||
<Image
|
||||
priority
|
||||
fill
|
||||
objectFit="cover"
|
||||
sizes="50%"
|
||||
src={item?.url || item?.src}
|
||||
alt="hero-carousel"
|
||||
blurDataURL={item.blurDataURL}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 저장된 금액 표시 */}
|
||||
|
||||
{annotations
|
||||
? annotations.map((annotation, idx) => (
|
||||
<Button
|
||||
key={idx}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: `${annotation.y * 100}%`, // 상대값을 퍼센트로 변환
|
||||
left: `${annotation.x * 100}%`,
|
||||
transform: 'translate(-50%, -50%)', // 중앙 정렬
|
||||
backgroundColor: 'rgba(0,0,0,0.7)',
|
||||
color: '#fff',
|
||||
padding: '2px 5px',
|
||||
borderRadius: '4px',
|
||||
display: priceTag ? 'block' : 'none'
|
||||
}}
|
||||
onClick={() => setModal(true)}
|
||||
>
|
||||
<Typography>{annotation.price}</Typography>
|
||||
</Button>
|
||||
))
|
||||
: ''}
|
||||
{modal && (
|
||||
<Modal open={modal}>
|
||||
<Stack direction={'column'} sx={{ backgroundColor: '#fff' }}>
|
||||
<Stack direction="row">
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={() => setModal(false)}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: 5,
|
||||
top: 5,
|
||||
zIndex: 111
|
||||
}}
|
||||
>
|
||||
<MdClear />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
<ChatApp slug={slug} />
|
||||
</Stack>
|
||||
</Modal>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
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 (
|
||||
<RootStyled>
|
||||
<div className="carousel-wrap">
|
||||
<AnimatePresence initial={false} custom={direction}>
|
||||
<motion.div
|
||||
className="motion-dev"
|
||||
key={page}
|
||||
custom={direction}
|
||||
variants={variants}
|
||||
initial="enter"
|
||||
animate="center"
|
||||
exit="exit"
|
||||
transition={{
|
||||
x: { type: 'spring', stiffness: 300, damping: 30 },
|
||||
opacity: { duration: 0.2 }
|
||||
}}
|
||||
drag="x"
|
||||
dragConstraints={{ left: 0, right: 0 }}
|
||||
dragElastic={1}
|
||||
onDragEnd={(e, { offset, velocity }) => {
|
||||
const swipe = swipePower(offset.x, velocity.x);
|
||||
if (swipe < -swipeConfidenceThreshold) {
|
||||
paginate(1);
|
||||
} else if (swipe > swipeConfidenceThreshold) {
|
||||
paginate(-1);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ProductDetailsCarousel item={images[imageIndex]} meta={data.metaDescription} />
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
<Stack
|
||||
<div className="carousel-wrap">
|
||||
<AnimatePresence initial={false} custom={direction}>
|
||||
<motion.div
|
||||
className="motion-dev"
|
||||
key={page}
|
||||
custom={direction}
|
||||
variants={variants}
|
||||
initial="enter"
|
||||
animate="center"
|
||||
exit="exit"
|
||||
transition={{
|
||||
x: { type: 'spring', stiffness: 300, damping: 30 },
|
||||
opacity: { duration: 0.2 }
|
||||
}}
|
||||
drag="x"
|
||||
dragConstraints={{ left: 0, right: 0 }}
|
||||
dragElastic={1}
|
||||
onDragEnd={(e, { offset, velocity }) => {
|
||||
const swipe = swipePower(offset.x, velocity.x);
|
||||
if (swipe < -swipeConfidenceThreshold) {
|
||||
paginate(1);
|
||||
} else if (swipe > swipeConfidenceThreshold) {
|
||||
paginate(-1);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ProductDetailsCarousel slug={slug} item={images[imageIndex]} meta={data.metaDescription} />
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
{/* <Stack
|
||||
direction="row"
|
||||
justifyContent={images.length < 6 ? 'center' : 'left'}
|
||||
spacing={1}
|
||||
@ -187,12 +202,13 @@ export default function CarouselAnimation({ ...props }) {
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Stack>
|
||||
</div>
|
||||
</Stack> */}
|
||||
</div>
|
||||
</RootStyled>
|
||||
);
|
||||
}
|
||||
CarouselAnimation.propTypes = {
|
||||
slug: PropTypes.string,
|
||||
product: PropTypes.object,
|
||||
data: PropTypes.object
|
||||
};
|
||||
|
@ -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('');
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user