From 2f41a2036708504a8cc7fd6affb0bec4fef1ef99 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 18:34:58 +0300 Subject: [PATCH 01/13] CONV-071: Create DashboardConverter component --- app/Livewire/Dashboard/DashboardConverter.php | 21 +++++++++++++++++++ .../dashboard/dashboard-converter.blade.php | 4 ++++ .../Livewire/DashboardConverterTest.php | 9 ++++++++ 3 files changed, 34 insertions(+) create mode 100644 app/Livewire/Dashboard/DashboardConverter.php create mode 100644 resources/views/livewire/dashboard/dashboard-converter.blade.php create mode 100644 tests/Feature/Livewire/DashboardConverterTest.php diff --git a/app/Livewire/Dashboard/DashboardConverter.php b/app/Livewire/Dashboard/DashboardConverter.php new file mode 100644 index 0000000..56e324d --- /dev/null +++ b/app/Livewire/Dashboard/DashboardConverter.php @@ -0,0 +1,21 @@ + +

Drop your file here

+

PNG, JPG, WEBP and PDF supported in beta

+ diff --git a/tests/Feature/Livewire/DashboardConverterTest.php b/tests/Feature/Livewire/DashboardConverterTest.php new file mode 100644 index 0000000..ec43f85 --- /dev/null +++ b/tests/Feature/Livewire/DashboardConverterTest.php @@ -0,0 +1,9 @@ +assertSee('Drop your file here'); +}); From 1906f5f0aad01e214dd82d2805b910af532aa014 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 18:36:22 +0300 Subject: [PATCH 02/13] CONV-072: Connect dashboard route to Livewire component --- resources/views/dashboard.blade.php | 31 +--------------------------- tests/Feature/DashboardRouteTest.php | 22 ++++++++------------ 2 files changed, 10 insertions(+), 43 deletions(-) diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 4b88af4..3cca978 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -6,21 +6,7 @@
- - - -
- -
- -
- Try: - - - - -
-
+
-
- -
-
diff --git a/tests/Feature/DashboardRouteTest.php b/tests/Feature/DashboardRouteTest.php index 7701272..4311b2d 100644 --- a/tests/Feature/DashboardRouteTest.php +++ b/tests/Feature/DashboardRouteTest.php @@ -16,6 +16,15 @@ ->assertSee('Convert any file'); }); +it('renders dashboard converter on dashboard page', function () { + $user = User::factory()->create(); + + $this->actingAs($user) + ->get(route('dashboard')) + ->assertOk() + ->assertSee('Drop your file here'); +}); + it('renders dashboard inside the app layout', function () { $this->actingAs(User::factory()->create()) ->get('/dashboard') @@ -41,16 +50,3 @@ ->assertSee('Contact Support') ->assertSee('Refer a Friend'); }); - -it('renders dashboard UI skeleton', function () { - $this->actingAs(User::factory()->create()) - ->get('/dashboard') - ->assertOk() - ->assertSee('Convert any file') - ->assertSee('File') - ->assertSee('Format') - ->assertSee('Settings') - ->assertSee('Convert') - ->assertSee('Recent Conversions') - ->assertSee('Marketing Report.pdf'); -}); From d26cb2f809eaf79e9a3891f0a2d4c4f4e38695f6 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 18:37:15 +0300 Subject: [PATCH 03/13] CONV-073: Render empty upload state --- .../dashboard/dashboard-converter.blade.php | 27 +++++++++++++++++-- .../Livewire/DashboardConverterTest.php | 6 +++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/resources/views/livewire/dashboard/dashboard-converter.blade.php b/resources/views/livewire/dashboard/dashboard-converter.blade.php index 2aa8c45..3ebadc9 100644 --- a/resources/views/livewire/dashboard/dashboard-converter.blade.php +++ b/resources/views/livewire/dashboard/dashboard-converter.blade.php @@ -1,4 +1,27 @@
-

Drop your file here

-

PNG, JPG, WEBP and PDF supported in beta

+ + @if ($step === 'upload') +
+
+ + + +
+ +
+

Drop your file here

+

PNG, JPG, WEBP and PDF supported in beta

+
+ + + +

Your files stay private. We delete them after conversion.

+
+ @endif +
diff --git a/tests/Feature/Livewire/DashboardConverterTest.php b/tests/Feature/Livewire/DashboardConverterTest.php index ec43f85..6f8aad8 100644 --- a/tests/Feature/Livewire/DashboardConverterTest.php +++ b/tests/Feature/Livewire/DashboardConverterTest.php @@ -3,7 +3,9 @@ use App\Livewire\Dashboard\DashboardConverter; use Livewire\Livewire; -it('renders dashboard converter component', function () { +it('renders empty upload state', function () { Livewire::test(DashboardConverter::class) - ->assertSee('Drop your file here'); + ->assertSee('Drop your file here') + ->assertSee('PNG, JPG, WEBP and PDF supported in beta') + ->assertSee('Choose file'); }); From c44007f96a38fe508ce18f8fb35f1ee79c7f9682 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 19:15:52 +0300 Subject: [PATCH 04/13] CONV-074: Test valid file upload flow --- .../Livewire/DashboardConverterUploadTest.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/Feature/Livewire/DashboardConverterUploadTest.php diff --git a/tests/Feature/Livewire/DashboardConverterUploadTest.php b/tests/Feature/Livewire/DashboardConverterUploadTest.php new file mode 100644 index 0000000..e69c1ae --- /dev/null +++ b/tests/Feature/Livewire/DashboardConverterUploadTest.php @@ -0,0 +1,24 @@ +create(); + + Livewire::actingAs($user) + ->test(DashboardConverter::class) + ->set('upload', UploadedFile::fake()->image('sample.png', 600, 400)) + ->call('storeUpload') + ->assertSet('step', 'format') + ->assertSee('sample.png') + ->assertSee('Choose output format'); + + expect(FileRecord::query()->where('original_name', 'sample.png')->exists())->toBeTrue(); +}); From 309be1c850257a3ddc386599695be2d84cdb64c9 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 19:17:09 +0300 Subject: [PATCH 05/13] CONV-075: Implement Livewire upload handling --- app/Livewire/Dashboard/DashboardConverter.php | 31 +++++++++++++++++++ .../dashboard/dashboard-converter.blade.php | 17 ++++++++++ 2 files changed, 48 insertions(+) diff --git a/app/Livewire/Dashboard/DashboardConverter.php b/app/Livewire/Dashboard/DashboardConverter.php index 56e324d..9c17621 100644 --- a/app/Livewire/Dashboard/DashboardConverter.php +++ b/app/Livewire/Dashboard/DashboardConverter.php @@ -2,10 +2,16 @@ namespace App\Livewire\Dashboard; +use App\Actions\Files\StoreUploadedFileAction; +use App\Models\FileRecord; +use App\Support\Files\UploadedFileRules; use Livewire\Component; +use Livewire\WithFileUploads; class DashboardConverter extends Component { + use WithFileUploads; + public string $step = 'upload'; public $upload = null; @@ -14,6 +20,31 @@ class DashboardConverter extends Component public ?string $uploadError = null; + public function storeUpload(StoreUploadedFileAction $storeUploadedFile): void + { + $this->resetErrorBag(); + $this->uploadError = null; + + $this->validate([ + 'upload' => UploadedFileRules::rules(), + ]); + + $fileRecord = $storeUploadedFile->handle( + user: auth()->user(), + file: $this->upload, + ); + + $this->currentFileId = $fileRecord->id; + $this->step = 'format'; + } + + public function getCurrentFileProperty(): ?FileRecord + { + return $this->currentFileId + ? FileRecord::query()->where('user_id', auth()->id())->find($this->currentFileId) + : null; + } + public function render() { return view('livewire.dashboard.dashboard-converter'); diff --git a/resources/views/livewire/dashboard/dashboard-converter.blade.php b/resources/views/livewire/dashboard/dashboard-converter.blade.php index 3ebadc9..c7b16a5 100644 --- a/resources/views/livewire/dashboard/dashboard-converter.blade.php +++ b/resources/views/livewire/dashboard/dashboard-converter.blade.php @@ -21,6 +21,23 @@

Your files stay private. We delete them after conversion.

+ + @error('upload') +

{{ $message }}

+ @enderror + + @endif + + @if ($step === 'format' && $this->currentFile) +
+
+

{{ $this->currentFile->original_name }}

+
+ +
+

Choose output format

+

Target format selection will be added in Phase 7.

+
@endif From 118d68191d2c71a8583b3b92482cf885e7f92a32 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:35:18 +0300 Subject: [PATCH 06/13] CONV-076: Render uploaded file summary --- .../dashboard/dashboard-converter.blade.php | 23 +++++++++++++++++-- .../Livewire/DashboardConverterUploadTest.php | 15 ++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/dashboard/dashboard-converter.blade.php b/resources/views/livewire/dashboard/dashboard-converter.blade.php index c7b16a5..6f4cabd 100644 --- a/resources/views/livewire/dashboard/dashboard-converter.blade.php +++ b/resources/views/livewire/dashboard/dashboard-converter.blade.php @@ -29,9 +29,28 @@ @endif @if ($step === 'format' && $this->currentFile) + @php($file = $this->currentFile)
-
-

{{ $this->currentFile->original_name }}

+
+
+ +
+

{{ $file->original_name }}

+

+ {{ strtoupper($file->extension) }} · + {{ number_format($file->size_bytes / 1024, 1) }} KB + @php($meta = $file->metadata_json ?? []) + @if (isset($meta['width'], $meta['height'])) + · {{ $meta['width'] }}×{{ $meta['height'] }} + @endif +

+
+
+ +
+ Replace + Remove +
diff --git a/tests/Feature/Livewire/DashboardConverterUploadTest.php b/tests/Feature/Livewire/DashboardConverterUploadTest.php index e69c1ae..fe1675f 100644 --- a/tests/Feature/Livewire/DashboardConverterUploadTest.php +++ b/tests/Feature/Livewire/DashboardConverterUploadTest.php @@ -22,3 +22,18 @@ expect(FileRecord::query()->where('original_name', 'sample.png')->exists())->toBeTrue(); }); + +it('shows uploaded file summary after upload', function () { + Storage::fake('local'); + + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(DashboardConverter::class) + ->set('upload', UploadedFile::fake()->image('avatar.jpg', 800, 600)) + ->call('storeUpload') + ->assertSee('avatar.jpg') + ->assertSee('JPG') + ->assertSee('Replace') + ->assertSee('Remove'); +}); From f72bc78e7a25eda1212a2539117cbd15571f3542 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:35:58 +0300 Subject: [PATCH 07/13] CONV-077: Add replace uploaded file action --- app/Livewire/Dashboard/DashboardConverter.php | 12 ++++++++++++ .../Livewire/DashboardConverterUploadTest.php | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/app/Livewire/Dashboard/DashboardConverter.php b/app/Livewire/Dashboard/DashboardConverter.php index 9c17621..9af99a3 100644 --- a/app/Livewire/Dashboard/DashboardConverter.php +++ b/app/Livewire/Dashboard/DashboardConverter.php @@ -38,6 +38,18 @@ public function storeUpload(StoreUploadedFileAction $storeUploadedFile): void $this->step = 'format'; } + public function replaceFile(): void + { + $this->resetCurrentUpload(); + } + + private function resetCurrentUpload(): void + { + $this->reset('upload', 'currentFileId', 'uploadError'); + $this->resetErrorBag(); + $this->step = 'upload'; + } + public function getCurrentFileProperty(): ?FileRecord { return $this->currentFileId diff --git a/tests/Feature/Livewire/DashboardConverterUploadTest.php b/tests/Feature/Livewire/DashboardConverterUploadTest.php index fe1675f..3ee1577 100644 --- a/tests/Feature/Livewire/DashboardConverterUploadTest.php +++ b/tests/Feature/Livewire/DashboardConverterUploadTest.php @@ -23,6 +23,22 @@ expect(FileRecord::query()->where('original_name', 'sample.png')->exists())->toBeTrue(); }); +it('resets current file when replacing uploaded file', function () { + Storage::fake('local'); + + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(DashboardConverter::class) + ->set('upload', UploadedFile::fake()->image('first.png')) + ->call('storeUpload') + ->assertSet('step', 'format') + ->call('replaceFile') + ->assertSet('step', 'upload') + ->assertSet('currentFileId', null) + ->assertSee('Drop your file here'); +}); + it('shows uploaded file summary after upload', function () { Storage::fake('local'); From 502b953b9bc82c121c522b0a6e2c058eb35a645a Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:36:39 +0300 Subject: [PATCH 08/13] CONV-078: Add remove uploaded file action --- app/Livewire/Dashboard/DashboardConverter.php | 5 +++++ .../Livewire/DashboardConverterUploadTest.php | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/Livewire/Dashboard/DashboardConverter.php b/app/Livewire/Dashboard/DashboardConverter.php index 9af99a3..33aec3b 100644 --- a/app/Livewire/Dashboard/DashboardConverter.php +++ b/app/Livewire/Dashboard/DashboardConverter.php @@ -43,6 +43,11 @@ public function replaceFile(): void $this->resetCurrentUpload(); } + public function removeFile(): void + { + $this->resetCurrentUpload(); + } + private function resetCurrentUpload(): void { $this->reset('upload', 'currentFileId', 'uploadError'); diff --git a/tests/Feature/Livewire/DashboardConverterUploadTest.php b/tests/Feature/Livewire/DashboardConverterUploadTest.php index 3ee1577..1ae9ee2 100644 --- a/tests/Feature/Livewire/DashboardConverterUploadTest.php +++ b/tests/Feature/Livewire/DashboardConverterUploadTest.php @@ -39,6 +39,22 @@ ->assertSee('Drop your file here'); }); +it('removes uploaded file from current flow', function () { + Storage::fake('local'); + + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(DashboardConverter::class) + ->set('upload', UploadedFile::fake()->image('remove-me.png')) + ->call('storeUpload') + ->assertSet('step', 'format') + ->call('removeFile') + ->assertSet('step', 'upload') + ->assertSet('currentFileId', null) + ->assertDontSee('remove-me.png'); +}); + it('shows uploaded file summary after upload', function () { Storage::fake('local'); From ca254bfefc07c7431c1ce6dae0659a097913e888 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:37:16 +0300 Subject: [PATCH 09/13] CONV-079: Add drag hover upload UI --- .../livewire/dashboard/dashboard-converter.blade.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/views/livewire/dashboard/dashboard-converter.blade.php b/resources/views/livewire/dashboard/dashboard-converter.blade.php index 6f4cabd..6dca2e6 100644 --- a/resources/views/livewire/dashboard/dashboard-converter.blade.php +++ b/resources/views/livewire/dashboard/dashboard-converter.blade.php @@ -1,7 +1,14 @@
@if ($step === 'upload') -
+
From 8bd72147b9fcfc6a341bc73d248cc09d45627ab1 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:38:17 +0300 Subject: [PATCH 10/13] CONV-080: Add upload error states --- app/Livewire/Dashboard/DashboardConverter.php | 30 +++++++++++++++---- .../dashboard/dashboard-converter.blade.php | 4 +++ .../Livewire/DashboardConverterUploadTest.php | 13 ++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/app/Livewire/Dashboard/DashboardConverter.php b/app/Livewire/Dashboard/DashboardConverter.php index 33aec3b..16782b3 100644 --- a/app/Livewire/Dashboard/DashboardConverter.php +++ b/app/Livewire/Dashboard/DashboardConverter.php @@ -3,6 +3,8 @@ namespace App\Livewire\Dashboard; use App\Actions\Files\StoreUploadedFileAction; +use App\Exceptions\Files\FileStorageException; +use App\Exceptions\Files\UnsupportedFileFormatException; use App\Models\FileRecord; use App\Support\Files\UploadedFileRules; use Livewire\Component; @@ -26,13 +28,31 @@ public function storeUpload(StoreUploadedFileAction $storeUploadedFile): void $this->uploadError = null; $this->validate([ - 'upload' => UploadedFileRules::rules(), + 'upload' => [ + 'required', + 'file', + 'max:'.UploadedFileRules::MAX_FILE_KILOBYTES, + ], + ], [ + 'upload.max' => 'This file is too large. Max upload size is '.(UploadedFileRules::MAX_FILE_KILOBYTES / 1024).' MB.', ]); - $fileRecord = $storeUploadedFile->handle( - user: auth()->user(), - file: $this->upload, - ); + try { + $fileRecord = $storeUploadedFile->handle( + user: auth()->user(), + file: $this->upload, + ); + } catch (UnsupportedFileFormatException) { + $this->uploadError = 'This file type is not supported in beta. Upload PNG, JPG, WEBP or PDF.'; + $this->step = 'upload'; + + return; + } catch (FileStorageException) { + $this->uploadError = 'We could not store your file. Please try again.'; + $this->step = 'upload'; + + return; + } $this->currentFileId = $fileRecord->id; $this->step = 'format'; diff --git a/resources/views/livewire/dashboard/dashboard-converter.blade.php b/resources/views/livewire/dashboard/dashboard-converter.blade.php index 6dca2e6..9b7a367 100644 --- a/resources/views/livewire/dashboard/dashboard-converter.blade.php +++ b/resources/views/livewire/dashboard/dashboard-converter.blade.php @@ -32,6 +32,10 @@ class="flex flex-col items-center justify-center gap-4 rounded-[var(--ca-radius- @error('upload')

{{ $message }}

@enderror + + @if ($uploadError) +

{{ $uploadError }}

+ @endif
@endif diff --git a/tests/Feature/Livewire/DashboardConverterUploadTest.php b/tests/Feature/Livewire/DashboardConverterUploadTest.php index 1ae9ee2..5836286 100644 --- a/tests/Feature/Livewire/DashboardConverterUploadTest.php +++ b/tests/Feature/Livewire/DashboardConverterUploadTest.php @@ -55,6 +55,19 @@ ->assertDontSee('remove-me.png'); }); +it('shows an error for unsupported upload format', function () { + Storage::fake('local'); + + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(DashboardConverter::class) + ->set('upload', UploadedFile::fake()->create('notes.txt', 10, 'text/plain')) + ->call('storeUpload') + ->assertSet('step', 'upload') + ->assertSee('not supported'); +}); + it('shows uploaded file summary after upload', function () { Storage::fake('local'); From ca36bbfc4dd75f1a349216be2c777aad37203e2f Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:38:55 +0300 Subject: [PATCH 11/13] CONV-081: Add upload loading state --- .../views/livewire/dashboard/dashboard-converter.blade.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/dashboard/dashboard-converter.blade.php b/resources/views/livewire/dashboard/dashboard-converter.blade.php index 9b7a367..7a7fc48 100644 --- a/resources/views/livewire/dashboard/dashboard-converter.blade.php +++ b/resources/views/livewire/dashboard/dashboard-converter.blade.php @@ -21,9 +21,10 @@ class="flex flex-col items-center justify-center gap-4 rounded-[var(--ca-radius-
From 6a53bd06ed5f794d1c21e61e59d8b6ecf0fa619d Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:39:38 +0300 Subject: [PATCH 12/13] CONV-082: Add format step placeholder --- .../dashboard/dashboard-converter.blade.php | 1 + .../Livewire/DashboardConverterUploadTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/resources/views/livewire/dashboard/dashboard-converter.blade.php b/resources/views/livewire/dashboard/dashboard-converter.blade.php index 7a7fc48..666f652 100644 --- a/resources/views/livewire/dashboard/dashboard-converter.blade.php +++ b/resources/views/livewire/dashboard/dashboard-converter.blade.php @@ -1,5 +1,6 @@
+ @if ($step === 'upload')
assertSee('not supported'); }); +it('shows format step placeholder after successful upload', function () { + Storage::fake('local'); + + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(DashboardConverter::class) + ->set('upload', UploadedFile::fake()->image('photo.png')) + ->call('storeUpload') + ->assertSet('step', 'format') + ->assertSee('Choose output format') + ->assertSee('Target format selection will be added in Phase 7'); +}); + it('shows uploaded file summary after upload', function () { Storage::fake('local'); From 3ff4da0ffc9379b8337228a6cbec9c9dd27b5472 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Fri, 29 May 2026 20:40:48 +0300 Subject: [PATCH 13/13] CONV-083: Add dashboard upload flow smoke tests --- .../Livewire/DashboardConverterUploadTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Feature/Livewire/DashboardConverterUploadTest.php b/tests/Feature/Livewire/DashboardConverterUploadTest.php index 4bff41a..108dfb8 100644 --- a/tests/Feature/Livewire/DashboardConverterUploadTest.php +++ b/tests/Feature/Livewire/DashboardConverterUploadTest.php @@ -93,6 +93,21 @@ ->call('storeUpload') ->assertSee('avatar.jpg') ->assertSee('JPG') + ->assertSee('KB') ->assertSee('Replace') ->assertSee('Remove'); }); + +it('does not create a file record when no file is provided', function () { + Storage::fake('local'); + + $user = User::factory()->create(); + + Livewire::actingAs($user) + ->test(DashboardConverter::class) + ->call('storeUpload') + ->assertSet('step', 'upload') + ->assertHasErrors(['upload' => 'required']); + + expect(FileRecord::query()->count())->toBe(0); +});