업데이트

This commit is contained in:
익희 김 2025-01-18 00:43:36 +09:00
parent e0959e1d15
commit 3af1d68b85
10 changed files with 234 additions and 130 deletions

22
.vscode/settings.json vendored Normal file
View 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
View File

@ -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",

View File

@ -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",

View File

@ -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>
);

View File

@ -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'

View File

@ -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' }}>

View File

@ -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 />;
}

View File

@ -29,7 +29,7 @@ function AdminChatPage() {
return () => {
socket.off('message');
};
}, []);
}, [message]);
const sendMessage = () => {

View File

@ -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
};

View File

@ -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('');
};