From 23938871b0d0f3b7eb6fd09786899c671b94f26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=B1=EA=B2=BD?= Date: Tue, 11 Aug 2026 17:07:57 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=EB=A5=BC=20=EC=95=95=EC=B6=95=20=ED=9B=84=20?= =?UTF-8?q?=EC=88=9C=EC=B0=A8=20=EC=A0=84=EC=86=A1=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.json | 2 + src/locales/ko.json | 2 + .../Dashboard/AssetManagementContent.tsx | 55 +++- src/styles/contents-social.css | 4 - src/styles/studio-assets.css | 66 +++++ src/utils/api.ts | 23 +- src/utils/imageCompression.ts | 249 ++++++++++++++++++ src/utils/imageUpload.ts | 83 ++++++ 8 files changed, 459 insertions(+), 25 deletions(-) create mode 100644 src/utils/imageCompression.ts create mode 100644 src/utils/imageUpload.ts diff --git a/src/locales/en.json b/src/locales/en.json index 3c4d0fc..edf6e91 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -269,6 +269,8 @@ "back": "Go Back", "loadMore": "Load more", "uploadFailed": "Image upload failed.", + "uploadErrorTitle": "Image Upload Error", + "uploadErrorConfirm": "OK", "uploading": "Uploading... (30 sec – 1 min)", "nextStep": "Next Step" }, diff --git a/src/locales/ko.json b/src/locales/ko.json index 8afb089..16728d7 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -268,6 +268,8 @@ "back": "뒤로가기", "loadMore": "더보기", "uploadFailed": "이미지 업로드에 실패했습니다.", + "uploadErrorTitle": "이미지 업로드 오류", + "uploadErrorConfirm": "확인", "uploading": "업로드 중 (30~60초 소요)", "nextStep": "다음 단계" }, diff --git a/src/pages/Dashboard/AssetManagementContent.tsx b/src/pages/Dashboard/AssetManagementContent.tsx index 50e57a4..1c74671 100755 --- a/src/pages/Dashboard/AssetManagementContent.tsx +++ b/src/pages/Dashboard/AssetManagementContent.tsx @@ -3,6 +3,7 @@ import React, { useRef, useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { ImageItem, ImageUrlItem } from '../../types/api'; import { uploadImages } from '../../utils/api'; +import { isImageInputFile } from '../../utils/imageCompression.ts'; interface AssetManagementContentProps { onNext: (imageTaskId: string) => void; @@ -38,6 +39,20 @@ const AssetManagementContent: React.FC = ({ } }, []); + useEffect(() => { + if (!uploadError) return; + + const handleEscape = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + event.preventDefault(); + setUploadError(null); + } + }; + + window.addEventListener('keydown', handleEscape); + return () => window.removeEventListener('keydown', handleEscape); + }, [uploadError]); + const handleVideoRatioChange = (ratio: VideoRatio) => { setVideoRatio(ratio); localStorage.setItem('castad_video_ratio', ratio); @@ -100,9 +115,7 @@ const AssetManagementContent: React.FC = ({ const handleDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); - const files = Array.from(e.dataTransfer.files).filter((file: File) => - file.type.startsWith('image/') - ); + const files = Array.from(e.dataTransfer.files).filter(isImageInputFile); if (files.length > 0) onAddImages(files); }; @@ -146,6 +159,37 @@ const AssetManagementContent: React.FC = ({ )} + {uploadError && ( +
setUploadError(null)} + > +
event.stopPropagation()} + > +

+ {t('assetManagement.uploadErrorTitle')} +

+

+ {uploadError} +

+ +
+
+ )} + {/* Fixed Header - 뒤로가기 버튼 */}
{onBack && ( @@ -268,9 +312,6 @@ const AssetManagementContent: React.FC = ({ {/* Fixed Footer - 다음 단계 버튼 */}
- {uploadError && ( -

{uploadError}

- )}