Merge branch 'main' of https://gitea.o2o.kr/castad/o2o-castad-frontend into feature-dashboard

feature-dashboard
김성경 2026-02-26 16:08:30 +09:00
commit ba4d143189
3 changed files with 28 additions and 6 deletions

View File

@ -21,6 +21,7 @@ interface SavedVideoState {
songTaskId: string; songTaskId: string;
status: VideoStatus; status: VideoStatus;
videoUrl: string | null; videoUrl: string | null;
videoDbId?: number;
timestamp: number; timestamp: number;
} }
@ -45,6 +46,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
// 소셜 미디어 포스팅 모달 // 소셜 미디어 포스팅 모달
const [showSocialModal, setShowSocialModal] = useState(false); const [showSocialModal, setShowSocialModal] = useState(false);
const [videoDbId, setVideoDbId] = useState<number | null>(null);
// 저장된 완료 데이터 // 저장된 완료 데이터
const [songCompletionData, setSongCompletionData] = useState<{ const [songCompletionData, setSongCompletionData] = useState<{
@ -69,12 +71,13 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
} }
}, [renderProgress, onVideoProgressChange]); }, [renderProgress, onVideoProgressChange]);
const saveToStorage = (videoTaskId: string, currentSongTaskId: string, status: VideoStatus, url: string | null) => { const saveToStorage = (videoTaskId: string, currentSongTaskId: string, status: VideoStatus, url: string | null, dbId?: number) => {
const data: SavedVideoState = { const data: SavedVideoState = {
videoTaskId, videoTaskId,
songTaskId: currentSongTaskId, songTaskId: currentSongTaskId,
status, status,
videoUrl: url, videoUrl: url,
videoDbId: dbId,
timestamp: Date.now(), timestamp: Date.now(),
}; };
localStorage.setItem(VIDEO_STORAGE_KEY, JSON.stringify(data)); localStorage.setItem(VIDEO_STORAGE_KEY, JSON.stringify(data));
@ -83,6 +86,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
const completeData = { const completeData = {
songTaskId: currentSongTaskId, songTaskId: currentSongTaskId,
videoUrl: url, videoUrl: url,
videoDbId: dbId,
completedAt: Date.now(), completedAt: Date.now(),
}; };
localStorage.setItem(VIDEO_COMPLETE_KEY, JSON.stringify(completeData)); localStorage.setItem(VIDEO_COMPLETE_KEY, JSON.stringify(completeData));
@ -93,7 +97,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
localStorage.removeItem(VIDEO_STORAGE_KEY); localStorage.removeItem(VIDEO_STORAGE_KEY);
}; };
const loadCompleteVideo = (): { songTaskId: string; videoUrl: string } | null => { const loadCompleteVideo = (): { songTaskId: string; videoUrl: string; videoDbId?: number } | null => {
try { try {
const saved = localStorage.getItem(VIDEO_COMPLETE_KEY); const saved = localStorage.getItem(VIDEO_COMPLETE_KEY);
if (!saved) return null; if (!saved) return null;
@ -202,10 +206,14 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
const videoUrlFromResponse = statusResponse.render_data?.url; const videoUrlFromResponse = statusResponse.render_data?.url;
if (videoUrlFromResponse) { if (videoUrlFromResponse) {
const videoId = statusResponse.render_data?.video_id;
setVideoUrl(videoUrlFromResponse); setVideoUrl(videoUrlFromResponse);
if (videoId) {
setVideoDbId(videoId);
}
setVideoStatus('complete'); setVideoStatus('complete');
setStatusMessage(''); setStatusMessage('');
saveToStorage(videoTaskId, currentSongTaskId, 'complete', videoUrlFromResponse); saveToStorage(videoTaskId, currentSongTaskId, 'complete', videoUrlFromResponse, videoId);
} else { } else {
throw new Error(t('completion.videoUrlMissing')); throw new Error(t('completion.videoUrlMissing'));
} }
@ -232,6 +240,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
const completeVideo = loadCompleteVideo(); const completeVideo = loadCompleteVideo();
if (completeVideo && completeVideo.songTaskId === songTaskId && completeVideo.videoUrl) { if (completeVideo && completeVideo.songTaskId === songTaskId && completeVideo.videoUrl) {
setVideoUrl(completeVideo.videoUrl); setVideoUrl(completeVideo.videoUrl);
if (completeVideo.videoDbId) setVideoDbId(completeVideo.videoDbId);
setVideoStatus('complete'); setVideoStatus('complete');
hasStartedGeneration.current = true; hasStartedGeneration.current = true;
return; return;
@ -242,6 +251,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
if (savedState && savedState.songTaskId === songTaskId) { if (savedState && savedState.songTaskId === songTaskId) {
if (savedState.status === 'complete' && savedState.videoUrl) { if (savedState.status === 'complete' && savedState.videoUrl) {
setVideoUrl(savedState.videoUrl); setVideoUrl(savedState.videoUrl);
if (savedState.videoDbId) setVideoDbId(savedState.videoDbId);
setVideoStatus('complete'); setVideoStatus('complete');
hasStartedGeneration.current = true; hasStartedGeneration.current = true;
} else if (savedState.status === 'polling') { } else if (savedState.status === 'polling') {
@ -501,7 +511,7 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
</button> </button>
<button <button
onClick={handleOpenSocialConnect} onClick={handleOpenSocialConnect}
disabled={videoStatus !== 'complete'} disabled={videoStatus !== 'complete' || !videoDbId}
className="comp2-btn comp2-btn-primary" className="comp2-btn comp2-btn-primary"
> >
{t('completion.uploadToSocial', { defaultValue: '소셜 채널 업로드' })} {t('completion.uploadToSocial', { defaultValue: '소셜 채널 업로드' })}
@ -515,8 +525,8 @@ const CompletionContent: React.FC<CompletionContentProps> = ({
<SocialPostingModal <SocialPostingModal
isOpen={showSocialModal} isOpen={showSocialModal}
onClose={handleCloseSocialConnect} onClose={handleCloseSocialConnect}
video={videoUrl ? { video={videoUrl && videoDbId ? {
video_id: 0, video_id: videoDbId,
store_name: songCompletionData?.businessName || '', store_name: songCompletionData?.businessName || '',
region: '', region: '',
task_id: songTaskId || '', task_id: songTaskId || '',

View File

@ -158,6 +158,7 @@ export interface VideoStatusResponse {
status: string; status: string;
url: string | null; url: string | null;
snapshot_url: string | null; snapshot_url: string | null;
video_id?: number;
} | null; } | null;
raw_response?: Record<string, unknown>; raw_response?: Record<string, unknown>;
error_message: string | null; error_message: string | null;

View File

@ -301,6 +301,17 @@ export async function getVideosList(page: number = 1, pageSize: number = 10): Pr
return response.json(); return response.json();
} }
// task_id로 video_id 조회 (소셜 업로드용)
export async function getVideoIdByTaskId(taskId: string): Promise<number | null> {
try {
const response = await getVideosList(1, 50);
const found = response.items.find(v => v.task_id === taskId);
return found?.video_id ?? null;
} catch {
return null;
}
}
// 비디오 삭제 API (개별 비디오 삭제) // 비디오 삭제 API (개별 비디오 삭제)
export async function deleteVideo(videoId: number): Promise<void> { export async function deleteVideo(videoId: number): Promise<void> {
const response = await authenticatedFetch(`${API_URL}/archive/videos/${videoId}`, { const response = await authenticatedFetch(`${API_URL}/archive/videos/${videoId}`, {