diff --git a/TODO.md b/TODO.md index 3be2386..372a72c 100644 --- a/TODO.md +++ b/TODO.md @@ -1,28 +1,9 @@ -# TODO: Convert Video Create to Cute Staged Popup Modal +# TODO: Convert Edit Video Page to Modal -## Implementation Plan - -### 1. Create Upload Modal Partial -- [x] Create `resources/views/layouts/partials/upload-modal.blade.php` -- [x] Implement cute staged pop-up animation (scale + fade with bounce) -- [x] Create multi-step form (Title → Video → Thumbnail → Privacy → Upload) -- [x] Add progress indicator for current step -- [x] Style with dark theme + cute accents - -### 2. Update Header -- [x] Modify `resources/views/layouts/partials/header.blade.php` -- [x] Change Upload button from link to trigger modal via JavaScript - -### 3. Include Modal in Views -- [x] Update `resources/views/videos/index.blade.php` to include upload modal -- [x] Update `resources/views/layouts/app.blade.php` to include modal globally for auth users - -### 4. Keep Fallback Route -- [x] Keep existing `/videos/create` route for direct access (no changes needed) - -## Implementation Notes -- Uses Bootstrap modal as base -- Custom CSS for cute staged animation effects -- JavaScript for step navigation and form handling -- Matches existing dark theme styling +## Steps: +- [x] 1. Create edit-video-modal.blade.php partial with cute design +- [x] 2. Modify VideoController's edit() method to support AJAX +- [x] 3. Modify VideoController's update() method for AJAX response +- [x] 4. Add Edit button and modal include to video show page +- [x] 5. Test the modal functionality diff --git a/app/Http/Controllers/VideoController.php b/app/Http/Controllers/VideoController.php index fc5caaf..26bd8dc 100644 --- a/app/Http/Controllers/VideoController.php +++ b/app/Http/Controllers/VideoController.php @@ -158,7 +158,7 @@ class VideoController extends Controller // Send email notification try { - Mail::to(Auth::user()->email)->send(new VideoUploaded($video)); + Mail::to(Auth::user()->email)->send(new VideoUploaded($video, Auth::user()->name)); } catch (\Exception $e) { // Log the error but don't fail the upload \Log::error('Email notification failed: ' . $e->getMessage()); @@ -199,13 +199,39 @@ class VideoController extends Controller return view('videos.show', compact('video')); } - public function edit(Video $video) + public function edit(Video $video, Request $request) { - return view('videos.edit', compact('video')); + // Check if user owns the video + if (Auth::id() !== $video->user_id) { + abort(403, 'You do not have permission to edit this video.'); + } + + // If not AJAX request, redirect to show page with edit parameter + if (!$request->expectsJson() && !$request->ajax()) { + return redirect()->route('videos.show', $video->id)->with('openEditModal', true); + } + + // For AJAX request, return JSON + return response()->json([ + 'success' => true, + 'video' => [ + 'id' => $video->id, + 'title' => $video->title, + 'description' => $video->description, + 'thumbnail' => $video->thumbnail, + 'thumbnail_url' => $video->thumbnail ? asset('storage/thumbnails/' . $video->thumbnail) : null, + 'visibility' => $video->visibility ?? 'public', + ] + ]); } public function update(Request $request, Video $video) { + // Check if user owns the video + if (Auth::id() !== $video->user_id) { + abort(403, 'You do not have permission to edit this video.'); + } + $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', @@ -231,6 +257,20 @@ class VideoController extends Controller $video->update($data); + // Return JSON for AJAX requests + if ($request->expectsJson() || $request->ajax()) { + return response()->json([ + 'success' => true, + 'message' => 'Video updated successfully!', + 'video' => [ + 'id' => $video->id, + 'title' => $video->title, + 'description' => $video->description, + 'visibility' => $video->visibility, + ] + ]); + } + return redirect()->route('videos.show', $video)->with('success', 'Video updated!'); } diff --git a/app/Mail/VideoUploaded.php b/app/Mail/VideoUploaded.php index f95dbe7..dd1e772 100644 --- a/app/Mail/VideoUploaded.php +++ b/app/Mail/VideoUploaded.php @@ -13,7 +13,7 @@ class VideoUploaded extends Mailable { use Queueable, SerializesModels; - public function __construct(public Video $video) + public function __construct(public Video $video, public string $userName) { } diff --git a/resources/views/emails/video-uploaded.blade.php b/resources/views/emails/video-uploaded.blade.php index abb6789..2ef248b 100644 --- a/resources/views/emails/video-uploaded.blade.php +++ b/resources/views/emails/video-uploaded.blade.php @@ -16,7 +16,7 @@

Video Uploaded Successfully! 🎉

-

Hi {{ $video->user->name }},

+

Hi {{ $userName }},

Your video "{{ $video->title }}" has been uploaded successfully!

Your video is now being processed and will be available shortly.

Video Details:

diff --git a/resources/views/layouts/partials/edit-video-modal.blade.php b/resources/views/layouts/partials/edit-video-modal.blade.php new file mode 100644 index 0000000..3bb2596 --- /dev/null +++ b/resources/views/layouts/partials/edit-video-modal.blade.php @@ -0,0 +1,1051 @@ + + + + + + + diff --git a/resources/views/layouts/partials/upload-modal.blade.php b/resources/views/layouts/partials/upload-modal.blade.php index 6c68ba7..a1d28b7 100644 --- a/resources/views/layouts/partials/upload-modal.blade.php +++ b/resources/views/layouts/partials/upload-modal.blade.php @@ -208,7 +208,7 @@
- Uploading... + Uploading... 0%
@@ -1065,6 +1065,9 @@ document.getElementById('upload-form-modal').addEventListener('submit', function const formData = new FormData(this); const xhr = new XMLHttpRequest(); + // Set timeout for large file uploads (30 minutes for large videos) + xhr.timeout = 1800000; // 30 minutes + // Show progress document.getElementById('progress-container-modal').classList.add('active'); document.getElementById('submit-btn-modal').disabled = true; @@ -1072,40 +1075,73 @@ document.getElementById('upload-form-modal').addEventListener('submit', function document.getElementById('btn-back-step-4').style.display = 'none'; document.getElementById('status-message-modal').className = 'status-message-modal'; + // Track upload start time for timeout handling + const uploadStartTime = Date.now(); + xhr.upload.addEventListener('progress', function(e) { if (e.lengthComputable) { const percent = Math.round((e.loaded / e.total) * 100); document.getElementById('progress-bar-modal').style.width = percent + '%'; document.getElementById('progress-percent-modal').textContent = percent + '%'; + + // Calculate and display upload speed for large files + if (percent > 0) { + const elapsed = (Date.now() - uploadStartTime) / 1000; // seconds + const speed = (e.loaded / elapsed / 1024 / 1024).toFixed(2); // MB/s + if (elapsed > 5 && speed > 0) { + document.getElementById('progress-label-modal').textContent = `Uploading... ${speed} MB/s`; + } + } } }); xhr.addEventListener('load', function() { document.getElementById('progress-bar-modal').style.width = '100%'; - document.getElementById('progress-label').textContent = 'Processing...'; + document.getElementById('progress-label-modal').textContent = 'Processing...'; - try { - const response = JSON.parse(xhr.responseText); - if (response.success) { - document.getElementById('progress-percent-modal').textContent = 'Done!'; - document.getElementById('status-message-modal').innerHTML = ' Upload successful! Closing...'; - document.getElementById('status-message-modal').className = 'status-message-modal success'; - - // Close modal and redirect after short delay - setTimeout(() => { - closeUploadModal(); - window.location.href = response.redirect; - }, 1000); - } else { - showErrorModal(response.message || 'Upload failed'); + // Check for successful HTTP status + if (xhr.status >= 200 && xhr.status < 300) { + try { + const response = JSON.parse(xhr.responseText); + if (response.success) { + document.getElementById('progress-percent-modal').textContent = 'Done!'; + document.getElementById('status-message-modal').innerHTML = ' Upload successful! Closing...'; + document.getElementById('status-message-modal').className = 'status-message-modal success'; + + // Close modal and redirect after short delay + setTimeout(() => { + closeUploadModal(); + window.location.href = response.redirect; + }, 1000); + } else { + showErrorModal(response.message || 'Upload failed'); + } + } catch(e) { + showErrorModal('Invalid response from server'); + } + } else if (xhr.status === 0) { + showErrorModal('Connection lost. Please check your internet connection and try again.'); + } else { + // Try to parse error message from server response + try { + const response = JSON.parse(xhr.responseText); + showErrorModal(response.message || `Server error (${xhr.status}). Please try again.`); + } catch(e) { + showErrorModal(`Upload failed with status ${xhr.status}. Please try again.`); } - } catch(e) { - showErrorModal('Invalid response from server'); } }); xhr.addEventListener('error', function() { - showErrorModal('Upload failed. Please try again.'); + showErrorModal('Upload failed. Please check your internet connection and try again.'); + }); + + xhr.addEventListener('timeout', function() { + showErrorModal('Upload timed out. The file may be too large or the connection too slow. Please try again.'); + }); + + xhr.addEventListener('abort', function() { + showErrorModal('Upload was cancelled. Please try again.'); }); xhr.open('POST', '{{ route("videos.store") }}'); diff --git a/resources/views/videos/edit.blade.php b/resources/views/videos/edit.blade.php index 29aa11f..8e1cf56 100644 --- a/resources/views/videos/edit.blade.php +++ b/resources/views/videos/edit.blade.php @@ -3,269 +3,545 @@ @section('title', 'Edit ' . $video->title . ' | TAKEONE') @section('extra_styles') - + } + @endsection +@section('body_class', 'edit-page-only') + @section('content') -
-

Edit Video

- -
- @csrf - @method('PUT') - -
- - -
- -
- - -
- -
- -
- - - +
+ +
+
+
+ +
+
+ + Update your video details
- -
- - @if($video->thumbnail) - Thumbnail - @endif - -
- -
- - Cancel -
- +
-
- @csrf - @method('DELETE') - -
+ +
+
+ @csrf + @method('PUT') + + +
+ +
+ @if($video->thumbnail) + Current thumbnail + @endif +
+ + No thumbnail +
+
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ +
+
+ +
+

Click to select new thumbnail

+

JPG, PNG, WebP up to 5MB

+
+
+
+ + +
+ +
+ + + +
+
+ + +
+ + Cancel + + +
+
+ + +
+

Danger Zone

+
+ @csrf + @method('DELETE') + +
+
+
@endsection @section('scripts') + @endauth + @endif @endsection