ai 추가
This commit is contained in:
parent
1fa6f72233
commit
b1f0627611
@ -7,7 +7,7 @@ import { Box, Container, Stack, Grid } from '@mui/material';
|
|||||||
// components
|
// components
|
||||||
import RelatedProductsCarousel from 'src/components/_main/product/relatedProducts';
|
import RelatedProductsCarousel from 'src/components/_main/product/relatedProducts';
|
||||||
import ProductDetailTabs from 'src/components/_main/product/tabs';
|
import ProductDetailTabs from 'src/components/_main/product/tabs';
|
||||||
import ProductAdditionalInfo from 'src/components/_main/product/additionalInfo';
|
// import ProductAdditionalInfo from 'src/components/_main/product/additionalInfo';
|
||||||
import ProductDetailsCarousel from 'src/components/carousels/customPaginationSilder';
|
import ProductDetailsCarousel from 'src/components/carousels/customPaginationSilder';
|
||||||
import ProductDetailsSumary from 'src/components/_main/product/summary';
|
import ProductDetailsSumary from 'src/components/_main/product/summary';
|
||||||
import HeaderBreadcrumbs from 'src/components/headerBreadcrumbs';
|
import HeaderBreadcrumbs from 'src/components/headerBreadcrumbs';
|
||||||
@ -81,13 +81,13 @@ export default async function ProductDetail({ params: { slug } }) {
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ProductAdditionalInfo />
|
{/* <ProductAdditionalInfo /> */}
|
||||||
<Suspense fallback={<></>}>
|
<Suspense fallback={<></>}>
|
||||||
<ProductDetailTabs
|
{/* <ProductDetailTabs
|
||||||
product={{ description: data.description, _id: data._id }}
|
product={{ description: data.description, _id: data._id }}
|
||||||
totalRating={totalRating}
|
totalRating={totalRating}
|
||||||
totalReviews={totalReviews}
|
totalReviews={totalReviews}
|
||||||
/>
|
/> */}
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<Suspense fallback={<></>}>
|
<Suspense fallback={<></>}>
|
||||||
<RelatedProductsCarousel id={data._id} category={category?.slug} />
|
<RelatedProductsCarousel id={data._id} category={category?.slug} />
|
||||||
|
@ -244,7 +244,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
({totalRating?.toFixed(1)})
|
({totalRating?.toFixed(1)})
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack direction="row" alignItems="center" spacing={1} color="text.secondary">
|
{/* <Stack direction="row" alignItems="center" spacing={1} color="text.secondary">
|
||||||
<TbMessage size={18} />
|
<TbMessage size={18} />
|
||||||
<Typography variant="subtitle2" color="text.secondary">
|
<Typography variant="subtitle2" color="text.secondary">
|
||||||
{product?.reviews.length}{' '}
|
{product?.reviews.length}{' '}
|
||||||
@ -256,7 +256,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
<Typography variant="subtitle2" color="text.secondary">
|
<Typography variant="subtitle2" color="text.secondary">
|
||||||
{product?.sold} sold
|
{product?.sold} sold
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack> */}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack direction="row" alignItems="center" spacing={1} mt={1.5}>
|
<Stack direction="row" alignItems="center" spacing={1} mt={1.5}>
|
||||||
<Typography variant="subtitle1">Brand:</Typography>
|
<Typography variant="subtitle1">Brand:</Typography>
|
||||||
@ -294,15 +294,15 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={5}>
|
<Grid item xs={12} md={5}>
|
||||||
<Card sx={{ p: 2, position: 'sticky', top: 156 }}>
|
<Card sx={{ p: 2, position: 'sticky', top: 156 }}>
|
||||||
<Typography variant="h4" className="text-price">
|
{/* <Typography variant="h4" className="text-price">
|
||||||
{!isLoading && isLoaded && fCurrency(cCurrency(product?.priceSale))}
|
{!isLoading && isLoaded && fCurrency(cCurrency(product?.priceSale))}
|
||||||
{product?.price <= product?.priceSale ? null : (
|
{product?.price <= product?.priceSale ? null : (
|
||||||
<Typography component="span" className="old-price" color="text.secondary">
|
<Typography component="span" className="old-price" color="text.secondary">
|
||||||
{!isLoading && isLoaded && fCurrency(cCurrency(product?.price))}
|
{!isLoading && isLoaded && fCurrency(cCurrency(product?.price))}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography> */}
|
||||||
<Stack direction="row" alignItems="center" spacing={1} className="incrementer-wrapper" my={2}>
|
{/* <Stack direction="row" alignItems="center" spacing={1} className="incrementer-wrapper" my={2}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Box sx={{ float: 'right' }}>
|
<Box sx={{ float: 'right' }}>
|
||||||
<Skeleton variant="rounded" width={120} height={40} />
|
<Skeleton variant="rounded" width={120} height={40} />
|
||||||
@ -327,10 +327,10 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
>
|
>
|
||||||
{product?.available > 0 ? `${product?.available} Items` : <span>Out of stock</span>}
|
{product?.available > 0 ? `${product?.available} Items` : <span>Out of stock</span>}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack> */}
|
||||||
|
|
||||||
<Stack spacing={1} className="contained-buttons" mb={2}>
|
<Stack spacing={1} className="contained-buttons" mb={2}>
|
||||||
<Button
|
{/* <Button
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={isMaxQuantity || isLoading || product?.available < 1}
|
disabled={isMaxQuantity || isLoading || product?.available < 1}
|
||||||
// size={isMobile ? 'medium' : 'large'}
|
// size={isMobile ? 'medium' : 'large'}
|
||||||
@ -383,9 +383,9 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
>
|
>
|
||||||
Add to Wishlist
|
Add to Wishlist
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
{compareProducts?.filter((v) => v._id === product._id).length > 0 ? (
|
{/* {compareProducts?.filter((v) => v._id === product._id).length > 0 ? (
|
||||||
<Button
|
<Button
|
||||||
startIcon={<GoGitCompare />}
|
startIcon={<GoGitCompare />}
|
||||||
fullWidth
|
fullWidth
|
||||||
@ -407,7 +407,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
>
|
>
|
||||||
Add to Compare
|
Add to Compare
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
<Stack direction="row" spacing={0.5} justifyContent={'center'}>
|
<Stack direction="row" spacing={0.5} justifyContent={'center'}>
|
||||||
<Tooltip title="Copy Prooduct URL">
|
<Tooltip title="Copy Prooduct URL">
|
||||||
@ -423,14 +423,14 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{isClient && (
|
{isClient && (
|
||||||
<>
|
<>
|
||||||
<Tooltip title="Share on WhatsApp">
|
<Tooltip title="문의하기">
|
||||||
<WhatsappShareButton url={window?.location.href || ''}>
|
<WhatsappShareButton url={window?.location.href || ''}>
|
||||||
<IconButton sx={{ color: '#42BC50' }} aria-label="whatsapp">
|
<IconButton sx={{ color: '#42BC50' }} aria-label="whatsapp">
|
||||||
<IoLogoWhatsapp size={24} />
|
<IoLogoWhatsapp size={24} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</WhatsappShareButton>
|
</WhatsappShareButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="Share on Facebook">
|
{/* <Tooltip title="Share on Facebook">
|
||||||
<FacebookShareButton url={window?.location.href || ''}>
|
<FacebookShareButton url={window?.location.href || ''}>
|
||||||
<IconButton sx={{ color: '#1373EC' }} aria-label="facebook">
|
<IconButton sx={{ color: '#1373EC' }} aria-label="facebook">
|
||||||
<FaFacebook size={24} />
|
<FaFacebook size={24} />
|
||||||
@ -450,13 +450,13 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
<FaLinkedin size={24} />
|
<FaLinkedin size={24} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</LinkedinShareButton>
|
</LinkedinShareButton>
|
||||||
</Tooltip>
|
</Tooltip> */}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Divider />
|
{/* <Divider />
|
||||||
{shippingData.map((item, index) => (
|
{shippingData.map((item, index) => (
|
||||||
<Stack
|
<Stack
|
||||||
key={index}
|
key={index}
|
||||||
@ -473,7 +473,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
|||||||
{item.name}
|
{item.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
))}
|
))} */}
|
||||||
</Card>
|
</Card>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { useState, useRef } from 'react';
|
||||||
// mui
|
// mui
|
||||||
import { alpha, styled } from '@mui/material/styles';
|
import { alpha, styled } from '@mui/material/styles';
|
||||||
import { Box, List, Stack, Paper, Button, ListItem, Typography, Skeleton, IconButton } from '@mui/material';
|
import { Box, List, Stack, Paper, Button, ListItem, Typography, Skeleton, IconButton, Modal } from '@mui/material';
|
||||||
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
@ -43,11 +43,79 @@ UploadMultiFile.propTypes = {
|
|||||||
|
|
||||||
export default function UploadMultiFile({ ...props }) {
|
export default function UploadMultiFile({ ...props }) {
|
||||||
const { error, files, onRemove, blob, isEdit, onRemoveAll, loading, sx, ...other } = props;
|
const { error, files, onRemove, blob, isEdit, onRemoveAll, loading, sx, ...other } = props;
|
||||||
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
const [image, setImage] = useState('');
|
||||||
|
const [predictions, setPredictions] = useState([]); // Roboflow 결과 저장
|
||||||
|
const canvasRef = useRef(null); // 캔버스 참조
|
||||||
const hasFile = files.length > 0;
|
const hasFile = files.length > 0;
|
||||||
const { getRootProps, getInputProps, isDragActive, isDragReject } = useDropzone({
|
const { getRootProps, getInputProps, isDragActive, isDragReject } = useDropzone({
|
||||||
...other
|
...other
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Roboflow API로 객체 탐지
|
||||||
|
const detectWithRoboflow = async () => {
|
||||||
|
if (!image) return;
|
||||||
|
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas?.getContext('2d');
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
img.src = image;
|
||||||
|
|
||||||
|
img.onload = async () => {
|
||||||
|
// 캔버스 크기 설정
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
|
||||||
|
// 이미지를 Blob으로 변환
|
||||||
|
const response = await fetch(image);
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
// API 호출
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', blob);
|
||||||
|
|
||||||
|
const apiEndpoint = `https://detect.roboflow.com/picup/1?api_key=s9OJq0UPljSqkPsJY6xP`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const apiResponse = await fetch(apiEndpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await apiResponse.json();
|
||||||
|
console.log('Roboflow Predictions:', result);
|
||||||
|
|
||||||
|
if (result.predictions) {
|
||||||
|
setPredictions(result.predictions);
|
||||||
|
|
||||||
|
// 바운딩 박스와 라벨 표시
|
||||||
|
result.predictions.forEach((prediction) => {
|
||||||
|
const { x, y, width, height, class: label, confidence } = prediction;
|
||||||
|
|
||||||
|
// 바운딩 박스
|
||||||
|
ctx.strokeStyle = 'red';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeRect(x, y, width, height);
|
||||||
|
|
||||||
|
// 라벨
|
||||||
|
ctx.fillStyle = 'red';
|
||||||
|
ctx.font = '16px Arial';
|
||||||
|
ctx.fillText(`${label} (${(confidence * 100).toFixed(2)}%)`, x, y > 10 ? y - 5 : 10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error detecting objects:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// AI 상품 검출 트리거
|
||||||
|
const objectDetect = () => {
|
||||||
|
detectWithRoboflow();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ width: '100%', ...sx }}>
|
<Box sx={{ width: '100%', ...sx }}>
|
||||||
<DropZoneStyle
|
<DropZoneStyle
|
||||||
@ -127,6 +195,10 @@ export default function UploadMultiFile({ ...props }) {
|
|||||||
<Paper
|
<Paper
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
component="img"
|
component="img"
|
||||||
|
onClick={() => {
|
||||||
|
setModalOpen(true);
|
||||||
|
setImage(file.url);
|
||||||
|
}}
|
||||||
src={!file.blob ? file.url : file.blob}
|
src={!file.blob ? file.url : file.blob}
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -140,6 +212,29 @@ export default function UploadMultiFile({ ...props }) {
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</List>
|
</List>
|
||||||
|
<Modal open={modalOpen} sx={{ position: 'relative' }}>
|
||||||
|
<Stack
|
||||||
|
direction="col"
|
||||||
|
sx={{ background: '#fff', position: 'fixed', left: '50%', top: '50%', transform: 'translate(-50%, -50%)' }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
sx={{ position: 'absolute;', right: 0, top: 0 }}
|
||||||
|
onClick={() => {
|
||||||
|
setModalOpen(false);
|
||||||
|
setImage('');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseRoundedIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
<Button
|
||||||
|
sx={{ position: 'absolute', bottom: 0, left: '50%', transform: 'translate(-50%, -30px)' }}
|
||||||
|
onClick={() => objectDetect()}
|
||||||
|
>
|
||||||
|
AI 상품 검출
|
||||||
|
</Button>
|
||||||
|
<canvas ref={canvasRef} style={{ width: '100%' }} />
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
{hasFile && (
|
{hasFile && (
|
||||||
<Stack direction="row" justifyContent="flex-end">
|
<Stack direction="row" justifyContent="flex-end">
|
||||||
|
Loading…
Reference in New Issue
Block a user