ai 추가
This commit is contained in:
parent
1fa6f72233
commit
b1f0627611
@ -7,7 +7,7 @@ import { Box, Container, Stack, Grid } from '@mui/material';
|
||||
// components
|
||||
import RelatedProductsCarousel from 'src/components/_main/product/relatedProducts';
|
||||
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 ProductDetailsSumary from 'src/components/_main/product/summary';
|
||||
import HeaderBreadcrumbs from 'src/components/headerBreadcrumbs';
|
||||
@ -81,13 +81,13 @@ export default async function ProductDetail({ params: { slug } }) {
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ProductAdditionalInfo />
|
||||
{/* <ProductAdditionalInfo /> */}
|
||||
<Suspense fallback={<></>}>
|
||||
<ProductDetailTabs
|
||||
{/* <ProductDetailTabs
|
||||
product={{ description: data.description, _id: data._id }}
|
||||
totalRating={totalRating}
|
||||
totalReviews={totalReviews}
|
||||
/>
|
||||
/> */}
|
||||
</Suspense>
|
||||
<Suspense fallback={<></>}>
|
||||
<RelatedProductsCarousel id={data._id} category={category?.slug} />
|
||||
|
@ -244,7 +244,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
({totalRating?.toFixed(1)})
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" alignItems="center" spacing={1} color="text.secondary">
|
||||
{/* <Stack direction="row" alignItems="center" spacing={1} color="text.secondary">
|
||||
<TbMessage size={18} />
|
||||
<Typography variant="subtitle2" color="text.secondary">
|
||||
{product?.reviews.length}{' '}
|
||||
@ -256,7 +256,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
<Typography variant="subtitle2" color="text.secondary">
|
||||
{product?.sold} sold
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack> */}
|
||||
</Stack>
|
||||
<Stack direction="row" alignItems="center" spacing={1} mt={1.5}>
|
||||
<Typography variant="subtitle1">Brand:</Typography>
|
||||
@ -294,15 +294,15 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
</Grid>
|
||||
<Grid item xs={12} md={5}>
|
||||
<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))}
|
||||
{product?.price <= product?.priceSale ? null : (
|
||||
<Typography component="span" className="old-price" color="text.secondary">
|
||||
{!isLoading && isLoaded && fCurrency(cCurrency(product?.price))}
|
||||
</Typography>
|
||||
)}
|
||||
</Typography>
|
||||
<Stack direction="row" alignItems="center" spacing={1} className="incrementer-wrapper" my={2}>
|
||||
</Typography> */}
|
||||
{/* <Stack direction="row" alignItems="center" spacing={1} className="incrementer-wrapper" my={2}>
|
||||
{isLoading ? (
|
||||
<Box sx={{ float: 'right' }}>
|
||||
<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>}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack> */}
|
||||
|
||||
<Stack spacing={1} className="contained-buttons" mb={2}>
|
||||
<Button
|
||||
{/* <Button
|
||||
fullWidth
|
||||
disabled={isMaxQuantity || isLoading || product?.available < 1}
|
||||
// size={isMobile ? 'medium' : 'large'}
|
||||
@ -383,9 +383,9 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
>
|
||||
Add to Wishlist
|
||||
</LoadingButton>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
{compareProducts?.filter((v) => v._id === product._id).length > 0 ? (
|
||||
{/* {compareProducts?.filter((v) => v._id === product._id).length > 0 ? (
|
||||
<Button
|
||||
startIcon={<GoGitCompare />}
|
||||
fullWidth
|
||||
@ -407,7 +407,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
>
|
||||
Add to Compare
|
||||
</Button>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
<Stack direction="row" spacing={0.5} justifyContent={'center'}>
|
||||
<Tooltip title="Copy Prooduct URL">
|
||||
@ -423,14 +423,14 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
</Tooltip>
|
||||
{isClient && (
|
||||
<>
|
||||
<Tooltip title="Share on WhatsApp">
|
||||
<Tooltip title="문의하기">
|
||||
<WhatsappShareButton url={window?.location.href || ''}>
|
||||
<IconButton sx={{ color: '#42BC50' }} aria-label="whatsapp">
|
||||
<IoLogoWhatsapp size={24} />
|
||||
</IconButton>
|
||||
</WhatsappShareButton>
|
||||
</Tooltip>
|
||||
<Tooltip title="Share on Facebook">
|
||||
{/* <Tooltip title="Share on Facebook">
|
||||
<FacebookShareButton url={window?.location.href || ''}>
|
||||
<IconButton sx={{ color: '#1373EC' }} aria-label="facebook">
|
||||
<FaFacebook size={24} />
|
||||
@ -450,13 +450,13 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
<FaLinkedin size={24} />
|
||||
</IconButton>
|
||||
</LinkedinShareButton>
|
||||
</Tooltip>
|
||||
</Tooltip> */}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Divider />
|
||||
{/* <Divider />
|
||||
{shippingData.map((item, index) => (
|
||||
<Stack
|
||||
key={index}
|
||||
@ -473,7 +473,7 @@ export default function ProductDetailsSumary({ ...props }) {
|
||||
{item.name}
|
||||
</Typography>
|
||||
</Stack>
|
||||
))}
|
||||
))} */}
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useState, useRef } from 'react';
|
||||
// mui
|
||||
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';
|
||||
|
||||
// components
|
||||
@ -43,11 +43,79 @@ UploadMultiFile.propTypes = {
|
||||
|
||||
export default function UploadMultiFile({ ...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 { getRootProps, getInputProps, isDragActive, isDragReject } = useDropzone({
|
||||
...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 (
|
||||
<Box sx={{ width: '100%', ...sx }}>
|
||||
<DropZoneStyle
|
||||
@ -127,6 +195,10 @@ export default function UploadMultiFile({ ...props }) {
|
||||
<Paper
|
||||
variant="outlined"
|
||||
component="img"
|
||||
onClick={() => {
|
||||
setModalOpen(true);
|
||||
setImage(file.url);
|
||||
}}
|
||||
src={!file.blob ? file.url : file.blob}
|
||||
sx={{
|
||||
width: '100%',
|
||||
@ -140,6 +212,29 @@ export default function UploadMultiFile({ ...props }) {
|
||||
</React.Fragment>
|
||||
))}
|
||||
</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 && (
|
||||
<Stack direction="row" justifyContent="flex-end">
|
||||
|
Loading…
Reference in New Issue
Block a user