상품 수정 삭제 가능

This commit is contained in:
익희 김 2025-01-22 03:52:49 +09:00
parent 05e565a02c
commit 3f04849bb9
3 changed files with 120 additions and 64 deletions

View File

@ -29,6 +29,7 @@ export default function EditProduct({ brands, categories, slug, shops, isVendor
<div>
<ProductForm
shops={shops}
isEdit={true}
brands={brands}
categories={categories}
currentProduct={data?.data}

View File

@ -49,6 +49,7 @@ const LabelStyle = styled(Typography)(({ theme }) => ({
const now = new Date();
export default function ProductForm({
categories,
isEdit = false,
currentProduct,
categoryLoading = false,
isInitialized = false,
@ -80,10 +81,6 @@ export default function ProductForm({
}
);
useEffect(() => {
console.log('currentProduct', currentProduct)
}, [currentProduct])
const NewProductSchema = Yup.object().shape({
name: Yup.string().required('Product name is required'),
code: Yup.string().required('Product code is required'),
@ -212,7 +209,6 @@ export default function ProductForm({
formik.setFieldValue('slug', slug); // set the value of slug in the formik state
formik.handleChange(event); // handle the change in formik
};
return (
<Stack spacing={3}>
<FormikProvider value={formik}>
@ -620,6 +616,8 @@ export default function ProductForm({
accept="image/*"
files={values?.images}
loading={loading}
isEdit={isEdit}
metaDescription={currentProduct?.metaDescription}
onDrop={handleDrop}
onRemove={handleRemove}
onRemoveAll={handleRemoveAll}
@ -668,7 +666,7 @@ export default function ProductForm({
/>
)}
</div>
<div style={{ display: 'none' }}>
<div style={{display:'none'}}>
{isInitialized ? (
<Skeleton variant="text" width={140} />
) : (

View File

@ -39,10 +39,11 @@ UploadMultiFile.propTypes = {
isInitialized: PropTypes.bool.isRequired,
isEdit: PropTypes.bool.isRequired,
loading: PropTypes.bool.isRequired,
onAnnotationsChange: PropTypes.func.isRequired
onAnnotationsChange: PropTypes.func.isRequired,
metaDescription: PropTypes.string
};
export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
export default function UploadMultiFile({ onAnnotationsChange, metaDescription, ...props }) {
const { error, files, onRemove, blob, isEdit, onRemoveAll, loading, sx, ...other } = props;
const [modalOpen, setModalOpen] = useState(false);
@ -53,6 +54,9 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
const [image, setImage] = useState('');
const [predictions, setPredictions] = useState([]); // Roboflow
const [selectedAnnotation, setSelectedAnnotation] = useState(null);
const [editPrice, setEditPrice] = useState('');
const [showAnnotations, setShowAnnotations] = useState(false);
const canvasRef = useRef(null); //
const hasFile = files.length > 0;
const { getRootProps, getInputProps, isDragActive, isDragReject } = useDropzone({
@ -80,40 +84,43 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
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`;
if (!isEdit) {
const formData = new FormData();
formData.append('file', blob);
try {
const apiResponse = await fetch(apiEndpoint, {
method: 'POST',
body: formData
});
const apiEndpoint = `https://detect.roboflow.com/picup/1?api_key=s9OJq0UPljSqkPsJY6xP`;
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 = 1;
ctx.strokeRect(x - 50, y - 80, width, height);
//
ctx.fillStyle = 'red';
ctx.font = '16px Arial';
ctx.fillText(`${label} (${(confidence * 100).toFixed(2)}%)`, x - 50, y - 80 > 10 ? y - 5 : 10);
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 = 1;
ctx.strokeRect(x - 50, y - 80, width, height);
//
ctx.fillStyle = 'red';
ctx.font = '16px Arial';
ctx.fillText(`${label} (${(confidence * 100).toFixed(2)}%)`, x - 50, y - 80 > 10 ? y - 5 : 10);
});
}
} catch (error) {
console.error('Error detecting objects:', error);
}
} catch (error) {
console.error('Error detecting objects:', error);
}
};
};
@ -121,6 +128,7 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
// AI
const objectDetect = () => {
detectWithRoboflow();
setShowAnnotations(true);
};
//
@ -133,30 +141,40 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
setPriceModalOpen(true); //
};
//
// const handleSavePrice = () => {
// const updatedAnnotations = [...annotations, { x: clientPosition.x, y: clientPosition.y, price }];
// setAnnotations(updatedAnnotations);
// setPriceModalOpen(false); //
// setPrice(''); //
// if (onAnnotationsChange) {
// onAnnotationsChange(updatedAnnotations); //
// }
// };
const handleSavePrice = () => {
const newId = annotations.length > 0 ? annotations[annotations.length - 1].id + 1 : 1;
if (selectedAnnotation) {
const updatedAnnotations = annotations.map((ann) =>
ann.id === selectedAnnotation.id ? { ...ann, price: editPrice } : ann
);
setAnnotations(updatedAnnotations);
} else {
const newId = annotations.length > 0 ? annotations[annotations.length - 1].id + 1 : 1;
const updatedAnnotations = [...annotations, { id: newId, x: clientPosition.x, y: clientPosition.y, price }];
setAnnotations(updatedAnnotations);
}
const updatedAnnotations = [...annotations, { id: newId, x: clientPosition.x, y: clientPosition.y, price }];
setPriceModalOpen(false);
setPrice('');
setSelectedAnnotation(null);
if (onAnnotationsChange) {
onAnnotationsChange(annotations);
}
};
const handleEditAnnotation = (annotation) => {
setSelectedAnnotation(annotation);
setEditPrice(annotation.price);
setPriceModalOpen(true);
};
const handleDeleteAnnotation = (id) => {
const updatedAnnotations = annotations.filter((ann) => ann.id !== id);
setAnnotations(updatedAnnotations);
setPriceModalOpen(false); //
setPrice(''); //
setPriceModalOpen(false);
setSelectedAnnotation(null);
if (onAnnotationsChange) {
onAnnotationsChange(updatedAnnotations); //
onAnnotationsChange(updatedAnnotations);
}
};
@ -166,6 +184,21 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
}
}, [annotations, onAnnotationsChange, other.metaDescription]);
useEffect(() => {
if (metaDescription) {
try {
const parsedAnnotations = JSON.parse(metaDescription);
setAnnotations(parsedAnnotations);
} catch (error) {
console.error('Failed to parse metaDescription:', error);
}
}
}, [metaDescription]);
console.log(showAnnotations)
return (
<Box sx={{ width: '100%', ...sx }}>
<DropZoneStyle
@ -247,7 +280,11 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
component="img"
onClick={() => {
setModalOpen(true);
setImage(file.url);
if (isEdit) {
setImage(files[0].url);
} else {
setImage(file.url);
}
}}
src={!file.blob ? file.url : file.blob}
sx={{
@ -271,6 +308,7 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
sx={{ position: 'absolute;', right: 0, top: 0 }}
onClick={() => {
setModalOpen(false);
setShowAnnotations(false);
setImage('');
}}
>
@ -280,7 +318,7 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
sx={{ position: 'absolute', bottom: 0, left: '50%', transform: 'translate(-50%, -30px)' }}
onClick={() => objectDetect()}
>
AI 상품 검출
{isEdit ? '상품 보기' : 'AI 상품 검출 '}
</Button>
{/* 금액 입력 모달 */}
<Modal open={priceModalOpen} onClose={() => setPriceModalOpen(false)}>
@ -300,14 +338,27 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
<Typography variant="h6">Enter Price</Typography>
<input
type="text"
value={price}
onChange={(e) => setPrice(e.target.value)}
value={editPrice || price}
onChange={(e) => {
isEdit ? setEditPrice(e.target.value) : setPrice(e.target.value);
}}
placeholder="Enter price"
style={{ padding: '8px', borderRadius: '4px', border: '1px solid #ddd' }}
/>
<Button variant="contained" onClick={handleSavePrice}>
Save
</Button>
<Stack direction="row" spacing={2}>
<Button variant="contained" onClick={handleSavePrice}>
Save
</Button>
{selectedAnnotation && (
<Button
variant="outlined"
color="error"
onClick={() => handleDeleteAnnotation(selectedAnnotation.id)}
>
Delete
</Button>
)}
</Stack>
</Stack>
</Modal>
<canvas ref={canvasRef} style={{ width: '100%' }} onClick={(event) => priceTagHandler(event)} />
@ -315,7 +366,9 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
{annotations.map((annotation, idx) => (
<Typography
key={idx}
onClick={() => handleEditAnnotation(annotation)} //
sx={{
display:showAnnotations ? 'block' : 'none',
position: 'absolute',
top: `${annotation.y * 100}%`, //
left: `${annotation.x * 100}%`,
@ -323,10 +376,14 @@ export default function UploadMultiFile({ onAnnotationsChange, ...props }) {
backgroundColor: 'rgba(0,0,0,0.7)',
color: '#fff',
padding: '2px 5px',
borderRadius: '4px'
borderRadius: '4px',
cursor: 'pointer',
'&:hover': {
backgroundColor: 'rgba(0,0,0,0.9)'
}
}}
>
{ annotation.id}: {annotation.price}
{annotation.id}: {annotation.price}
</Typography>
))}
</Stack>