업데이트

This commit is contained in:
익희 김 2025-01-20 20:03:24 +09:00
parent 91e77526b4
commit c1bca972f5
5 changed files with 84 additions and 206 deletions

2
package-lock.json generated
View File

@ -26,7 +26,7 @@
"axios": "^1.6.7", "axios": "^1.6.7",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"change-case": "^5.2.0", "change-case": "^5.2.0",
"cloudinary": "^1.41.0", "cloudinary": "^1.41.3",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"eslint": "latest", "eslint": "latest",
"eslint-config-next": "latest", "eslint-config-next": "latest",

View File

@ -28,7 +28,7 @@
"axios": "^1.6.7", "axios": "^1.6.7",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"change-case": "^5.2.0", "change-case": "^5.2.0",
"cloudinary": "^1.41.0", "cloudinary": "^1.41.3",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"eslint": "latest", "eslint": "latest",
"eslint-config-next": "latest", "eslint-config-next": "latest",

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="0 0 256 256"><path fill="#FFE812" d="M256 236c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20V20C0 8.954 8.954 0 20 0h216c11.046 0 20 8.954 20 20v216z"/><path d="M128 36C70.562 36 24 72.713 24 118c0 29.279 19.466 54.97 48.748 69.477-1.593 5.494-10.237 35.344-10.581 37.689 0 0-.207 1.762.934 2.434s2.483.15 2.483.15c3.272-.457 37.943-24.811 43.944-29.04 5.995.849 12.168 1.29 18.472 1.29 57.438 0 104-36.712 104-82 0-45.287-46.562-82-104-82z"/><path fill="#FFE812" d="M70.5 146.625c-3.309 0-6-2.57-6-5.73V105.25h-9.362c-3.247 0-5.888-2.636-5.888-5.875s2.642-5.875 5.888-5.875h30.724c3.247 0 5.888 2.636 5.888 5.875s-2.642 5.875-5.888 5.875H76.5v35.645c0 3.16-2.691 5.73-6 5.73zM123.112 146.547c-2.502 0-4.416-1.016-4.993-2.65l-2.971-7.778-18.296-.001-2.973 7.783c-.575 1.631-2.488 2.646-4.99 2.646a9.155 9.155 0 0 1-3.814-.828c-1.654-.763-3.244-2.861-1.422-8.52l14.352-37.776c1.011-2.873 4.082-5.833 7.99-5.922 3.919.088 6.99 3.049 8.003 5.928l14.346 37.759c1.826 5.672.236 7.771-1.418 8.532a9.176 9.176 0 0 1-3.814.827c-.001 0 0 0 0 0zm-11.119-21.056L106 108.466l-5.993 17.025h11.986zM138 145.75c-3.171 0-5.75-2.468-5.75-5.5V99.5c0-3.309 2.748-6 6.125-6s6.125 2.691 6.125 6v35.25h12.75c3.171 0 5.75 2.468 5.75 5.5s-2.579 5.5-5.75 5.5H138zM171.334 146.547c-3.309 0-6-2.691-6-6V99.5c0-3.309 2.691-6 6-6s6 2.691 6 6v12.896l16.74-16.74c.861-.861 2.044-1.335 3.328-1.335 1.498 0 3.002.646 4.129 1.772 1.051 1.05 1.678 2.401 1.764 3.804.087 1.415-.384 2.712-1.324 3.653l-13.673 13.671 14.769 19.566a5.951 5.951 0 0 1 1.152 4.445 5.956 5.956 0 0 1-2.328 3.957 5.94 5.94 0 0 1-3.609 1.211 5.953 5.953 0 0 1-4.793-2.385l-14.071-18.644-2.082 2.082v13.091a6.01 6.01 0 0 1-6.002 6.003z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -17,7 +17,7 @@ import {
Rating, Rating,
Tooltip, Tooltip,
Grid, Grid,
Card, Card
// alpha, // alpha,
// Divider // Divider
} from '@mui/material'; } from '@mui/material';
@ -52,6 +52,8 @@ import { MdContentCopy } from 'react-icons/md';
import { LiaShippingFastSolid } from 'react-icons/lia'; import { LiaShippingFastSolid } from 'react-icons/lia';
import { MdLockOutline } from 'react-icons/md'; import { MdLockOutline } from 'react-icons/md';
import { FaRegStar } from 'react-icons/fa'; import { FaRegStar } from 'react-icons/fa';
import Image from 'next/image';
import Kakao from '../../../../../public/images/KakaoTalk_logo.svg';
// import { TbMessage } from 'react-icons/tb'; // import { TbMessage } from 'react-icons/tb';
// import { MdOutlineShoppingBasket } from 'react-icons/md'; // import { MdOutlineShoppingBasket } from 'react-icons/md';
// import { FiShoppingCart } from 'react-icons/fi'; // import { FiShoppingCart } from 'react-icons/fi';
@ -271,7 +273,7 @@ export default function ProductDetailsSumary({ ...props }) {
{category?.name || 'Nextall'} {category?.name || 'Nextall'}
</Typography> </Typography>
</Stack> </Stack>
{/* {product?.price > product?.priceSale && ( {/* {product?.price > product?.priceSale && (
<Stack direction="row" alignItems="center" spacing={1}> <Stack direction="row" alignItems="center" spacing={1}>
<Typography variant="subtitle1">Discount:</Typography> <Typography variant="subtitle1">Discount:</Typography>
@ -411,7 +413,7 @@ export default function ProductDetailsSumary({ ...props }) {
</Button> </Button>
)} */} )} */}
<Stack direction="row" spacing={0.5} justifyContent={'center'}> <Stack direction="row" spacing={0.5} justifyContent={'center'} alignItems={'center'}>
<Tooltip title="Copy Prooduct URL"> <Tooltip title="Copy Prooduct URL">
<IconButton <IconButton
aria-label="copy" aria-label="copy"
@ -432,6 +434,16 @@ export default function ProductDetailsSumary({ ...props }) {
</IconButton> </IconButton>
</WhatsappShareButton> </WhatsappShareButton>
</Tooltip> </Tooltip>
<Tooltip
title="카카오톡"
onClick={() => {
window?.open('http://pf.kakao.com/_xlxhIxbn/chat');
}}
>
<IconButton sx={{ p: '8px' }}>
<Image src={Kakao} alt="카카오톡" width={24} height={24} />
</IconButton>
</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">

View File

@ -10,13 +10,12 @@ import Typography from '@mui/material/Typography';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import TextField from '@mui/material/TextField'; import TextField from '@mui/material/TextField';
import Skeleton from '@mui/material/Skeleton'; import Skeleton from '@mui/material/Skeleton';
import { InputAdornment, Stack, Button, Modal, IconButton } from '@mui/material'; import { InputAdornment, Stack, Button } from '@mui/material';
import MenuList from '@mui/material/MenuList'; import MenuList from '@mui/material/MenuList';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem';
import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemIcon from '@mui/material/ListItemIcon';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import Divider from '@mui/material/Divider'; import Divider from '@mui/material/Divider';
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
// components // components
import NoDataFound from 'src/illustrations/dataNotFound'; import NoDataFound from 'src/illustrations/dataNotFound';
@ -26,68 +25,12 @@ import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select'; import Select from '@mui/material/Select';
import { useCurrencyConvert } from 'src/hooks/convertCurrency'; import { useCurrencyConvert } from 'src/hooks/convertCurrency';
import { useCurrencyFormatter } from 'src/hooks/formatCurrency'; import { useCurrencyFormatter } from 'src/hooks/formatCurrency';
const axios = require("axios");
const fs = require("fs");
const FormData = require('form-data');
// api // api
import * as api from 'src/services'; import * as api from 'src/services';
const axios = require('axios');
const getImageUrl = async (ids) => {
const requestOptions = {
method: 'GET',
redirect: 'follow'
};
try {
const responses = await Promise.all(
ids.map(async (id) => {
const response = await fetch(
`https://api.roboflow.com/picup/picup/images/${id}?api_key=s9OJq0UPljSqkPsJY6xP`,
requestOptions
);
if (!response.ok) {
throw new Error(`Error fetching image for ID: ${id}, Status: ${response.status}`);
}
return response.json(); // JSON
})
);
return responses; //
} catch (error) {
console.error('Error fetching image URLs:', error);
return null; // null
}
};
const searchImages = async (imageBase64) => {
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'application/json');
const raw = JSON.stringify({
like_image: 'MV09CPgMSn9uhQ5D2nl5',
limit: 3,
in_dataset: true
});
const requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
try {
const response = await fetch(
'https://api.roboflow.com/picup/picup/search?api_key=s9OJq0UPljSqkPsJY6xP',
requestOptions
);
const result = await response.json();
return result; //
} catch (error) {
console.error('Error fetching images:', error);
throw error;
}
};
Search.propTypes = { Search.propTypes = {
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
mobile: PropTypes.bool.isRequired mobile: PropTypes.bool.isRequired
@ -116,9 +59,6 @@ export default function Search({ ...props }) {
const router = useRouter(); const router = useRouter();
const [search, setSearch] = React.useState(''); const [search, setSearch] = React.useState('');
const [image, setImage] = React.useState(null);
const [imagesResultID, setImagesResultID] = React.useState(null);
const [roboFlowUrlLists, setRoboFlowUrlLists] = React.useState([]);
const { data: filters, isLoading: filtersLoading } = useQuery(['get-search-filters'], () => api.getSearchFilters()); const { data: filters, isLoading: filtersLoading } = useQuery(['get-search-filters'], () => api.getSearchFilters());
const { mutate, isLoading } = useMutation('search', api.search, { const { mutate, isLoading } = useMutation('search', api.search, {
@ -171,45 +111,58 @@ export default function Search({ ...props }) {
} }
}; };
//
// const handleFileChange = (event) => {
// const file = event.target.files?.[0];
// if (file) {
// console.log('Selected File:', file);
// // :
// const reader = new FileReader();
// reader.onload = () => {
// console.log('Image Preview URL:', reader.result);
// };
// reader.readAsDataURL(file);
// }
// };
const handleFileChange = async (event) => { const handleFileChange = async (event) => {
const file = event.target.files?.[0]; const file = event.target.files[0];
if (file) { if (file) {
const reader = new FileReader(); const formData = new FormData();
reader.onloadend = async () => { formData.append('name', file.name);
const base64Image = reader.result.split(',')[1]; // base64 formData.append('file', file);
setImage(reader.result); formData.append('split', 'train');
};
reader.readAsDataURL(file); console.log(formData)
axios({
method: 'POST',
url: 'https://api.roboflow.com/dataset/picup/upload',
params: {
api_key: 's9OJq0UPljSqkPsJY6xP'
},
data: formData,
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error.message);
});
} }
}; };
const objectDetect = async (image) => { const handleSubmit = (event) => {
try { event.preventDefault();
const result = await searchImages(image); if (fileInputRef.current && fileInputRef.current.files.length > 0) {
handleFileChange({ target: { files: fileInputRef.current.files } });
// id
const imageIds = result.results.map((r) => r.id);
//
setImagesResultID(imageIds);
console.log('Image IDs:', imageIds); //
} catch (error) {
console.error('Error in image search:', error);
} }
}; };
React.useEffect(() => {
if (imagesResultID && imagesResultID.length > 0) {
getImageUrl(imagesResultID).then((url) => {
setRoboFlowUrlLists(url);
}).finally(() => {
setImage(null)
});
}
}, [imagesResultID]);
return ( return (
<> <>
@ -255,17 +208,22 @@ export default function Search({ ...props }) {
} }
}} }}
/> />
<Button variant="contained" sx={{ width: '100%', borderRadius: 0 }} onClick={handleImageSearch}> <form style={{ display: 'flex' }} onSubmit={handleSubmit}>
이미지 검색 <Button variant="contained" sx={{ width: '80%', borderRadius: 0 }} onClick={handleImageSearch}>
</Button> 이미지 찾기
<input </Button>
type="file" <input
accept="image/*" type="file"
capture="camera" accept="image/*"
ref={fileInputRef} capture="camera"
onChange={handleFileChange} ref={fileInputRef}
style={{ display: 'none' }} onChange={handleFileChange}
/> style={{ display: 'none' }}
/>
<button type="submit" style={{ width: '20%', height: '40px', border: '0' }}>
검색
</button>
</form>
<Stack gap={1} direction="row" p={1}> <Stack gap={1} direction="row" p={1}>
<FormControl fullWidth> <FormControl fullWidth>
<LabelStyle component={'label'} htmlFor="shops"> <LabelStyle component={'label'} htmlFor="shops">
@ -422,13 +380,13 @@ export default function Search({ ...props }) {
{isLoading ? <Skeleton variant="text" width="200px" /> : product.category} {isLoading ? <Skeleton variant="text" width="200px" /> : product.category}
</Typography> </Typography>
</div> </div>
{/* <Typography variant="subtitle1" color="text.primary" noWrap> <Typography variant="subtitle1" color="text.primary" noWrap>
{isLoading ? ( {isLoading ? (
<Skeleton variant="text" width="100px" /> <Skeleton variant="text" width="100px" />
) : ( ) : (
fCurrency(cCurrency(product.priceSale)) fCurrency(cCurrency(product.priceSale))
)} )}
</Typography> */} </Typography>
</Stack> </Stack>
</ListItemText> </ListItemText>
</MenuItem> </MenuItem>
@ -437,63 +395,6 @@ export default function Search({ ...props }) {
</> </>
)} )}
</Box>{' '} </Box>{' '}
{roboFlowUrlLists &&
roboFlowUrlLists.map((i, index) => (
<MenuList
key={`search-by-images-${i.image.annotation.key}-${index.toString()}`}
sx={{
pt: 0,
mt: 1,
overflow: 'auto',
px: 1,
li: {
borderRadius: '8px',
border: `1px solid transparent`,
'&:hover, &.Mui-focusVisible, &.Mui-selected ': {
border: (theme) => `1px solid ${theme.palette.primary.main}`,
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.16),
h6: {
color: 'primary.main'
}
},
'&.active': {
border: (theme) => `1px solid ${theme.palette.primary.main}`,
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.16),
h6: {
color: 'primary.main'
}
}
}
}}
autoFocusItem={!focus}
>
<MenuItem>
<ListItemIcon>
<img
alt="이미지"
src={i.image.urls.original}
style={{ width: '40px', height: '40px', borderRadius: '100%' }}
/>
</ListItemIcon>
<ListItemText>
<Stack direction="row" gap={1} alignItems={'center'} justifyContent={'space-between'}>
<div>
<Typography variant="subtitle1" color="text.primary" noWrap>
Object Data Set {index}
</Typography>
<Typography variant="body2" color="text.secondary" noWrap>
{i.image.tags.map((i) => {
return i;
})}
{` `}
<span>{i.image.r * 100}%</span>
</Typography>
</div>
</Stack>
</ListItemText>
</MenuItem>
</MenuList>
))}
{multiSelect && ( {multiSelect && (
<Stack gap={1} direction={'row'} p={1} justifyContent={'end'}> <Stack gap={1} direction={'row'} p={1} justifyContent={'end'}>
<Button variant="outlined" color="primary" onClick={() => handleSave(selectedProducts)}> <Button variant="outlined" color="primary" onClick={() => handleSave(selectedProducts)}>
@ -505,42 +406,6 @@ export default function Search({ ...props }) {
</Stack> </Stack>
)} )}
</Box> </Box>
{/* 이미지 미리보기 렌더링 */}
{image && (
<Modal
open={image}
sx={{ height: '100vh', width: '100vw', display: 'flex', justifyContent: 'center', alignItems: 'center' }}
>
<Box sx={{ position: 'relative' }}>
<Box mt={2} display="flex" justifyContent="center">
<IconButton
sx={{ position: 'absolute;', right: 0, top: 10, mixBlendMode: 'difference' }}
onClick={() => {
setImage(null);
}}
>
<CloseRoundedIcon fontSize="small" />
</IconButton>
<img
src={image}
style={{
maxWidth: '100%',
maxHeight: '300px',
borderRadius: '8px',
border: '1px solid #ddd'
}}
/>
<Button
sx={{ position: 'absolute', bottom: 0, left: '50%', transform: 'translate(-50%, 0px)' }}
onClick={() => objectDetect(image)}
>
이미지 상품 검색
</Button>
</Box>
</Box>
</Modal>
)}
</> </>
); );
} }