diff --git a/app/Http/Controllers/Video/Veo3Controller.php b/app/Http/Controllers/Video/Veo3Controller.php index f3e22ca4..4661fa80 100644 --- a/app/Http/Controllers/Video/Veo3Controller.php +++ b/app/Http/Controllers/Video/Veo3Controller.php @@ -220,4 +220,24 @@ public function history(Request $request): JsonResponse 'data' => $videos, ]); } + + /** + * 생성 이력 삭제 (복수) + */ + public function destroy(Request $request): JsonResponse + { + $request->validate([ + 'ids' => 'required|array|min:1', + 'ids.*' => 'integer', + ]); + + $deleted = VideoGeneration::where('user_id', auth()->id()) + ->whereIn('id', $request->input('ids')) + ->delete(); + + return response()->json([ + 'success' => true, + 'deleted' => $deleted, + ]); + } } diff --git a/resources/views/video/veo3/index.blade.php b/resources/views/video/veo3/index.blade.php index 637dd038..aa061cc2 100644 --- a/resources/views/video/veo3/index.blade.php +++ b/resources/views/video/veo3/index.blade.php @@ -532,13 +532,51 @@ className="bg-indigo-600 text-white px-8 py-3 rounded-lg font-medium hover:bg-in const HistoryTable = ({ onSelect }) => { const [history, setHistory] = useState([]); const [loading, setLoading] = useState(true); + const [checked, setChecked] = useState(new Set()); + const [deleting, setDeleting] = useState(false); - useEffect(() => { + const fetchHistory = () => { api('/video/veo3/history') - .then(data => setHistory(data.data || [])) + .then(data => { setHistory(data.data || []); setChecked(new Set()); }) .catch(() => {}) .finally(() => setLoading(false)); - }, []); + }; + + useEffect(() => { fetchHistory(); }, []); + + const toggleCheck = (id) => { + setChecked(prev => { + const next = new Set(prev); + next.has(id) ? next.delete(id) : next.add(id); + return next; + }); + }; + + const toggleAll = () => { + if (checked.size === history.length) { + setChecked(new Set()); + } else { + setChecked(new Set(history.map(h => h.id))); + } + }; + + const handleDelete = async () => { + if (checked.size === 0) return; + if (!confirm(`선택한 ${checked.size}개 이력을 삭제하시겠습니까?`)) return; + + setDeleting(true); + try { + await api('/video/veo3/history', { + method: 'DELETE', + body: JSON.stringify({ ids: Array.from(checked) }), + }); + fetchHistory(); + } catch (err) { + alert('삭제 실패: ' + err.message); + } finally { + setDeleting(false); + } + }; if (loading) return
이력 로딩 중...
; if (history.length === 0) return null; @@ -562,11 +600,33 @@ className="bg-indigo-600 text-white px-8 py-3 rounded-lg font-medium hover:bg-in return (
-

생성 이력

+
+

생성 이력

+ {checked.size > 0 && ( + + )} +
+ @@ -577,7 +637,15 @@ className="bg-indigo-600 text-white px-8 py-3 rounded-lg font-medium hover:bg-in {history.map((item) => ( - + + diff --git a/routes/web.php b/routes/web.php index 5311c798..d1fb5c63 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1471,6 +1471,7 @@ Route::get('/download/{id}', [\App\Http\Controllers\Video\Veo3Controller::class, 'download'])->name('download'); Route::get('/preview/{id}', [\App\Http\Controllers\Video\Veo3Controller::class, 'preview'])->name('preview'); Route::get('/history', [\App\Http\Controllers\Video\Veo3Controller::class, 'history'])->name('history'); + Route::delete('/history', [\App\Http\Controllers\Video\Veo3Controller::class, 'destroy'])->name('destroy'); }); /*
+ 0 && checked.size === history.length} + onChange={toggleAll} + className="w-4 h-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer" + /> + 날짜 키워드 제목
+ toggleCheck(item.id)} + className="w-4 h-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 cursor-pointer" + /> + {new Date(item.created_at).toLocaleDateString('ko-KR')} {item.keyword} {item.title || '-'}