Skip to content

release: v0.1.6 — Phase 06 Dashboard Upload Flow#90

Merged
menvil merged 27 commits into
mainfrom
release/v0.1.6-phase06-dashboard-upload-flow
May 31, 2026
Merged

release: v0.1.6 — Phase 06 Dashboard Upload Flow#90
menvil merged 27 commits into
mainfrom
release/v0.1.6-phase06-dashboard-upload-flow

Conversation

@menvil

@menvil menvil commented May 29, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 6 ships the first real user flow on the dashboard:
empty upload state → upload valid file → store via StoreUploadedFileAction → uploaded file summary → format step placeholder.

Includes CONV-071 … CONV-083:

  • DashboardConverter Livewire component + route binding
  • Empty upload state UI + drag hover
  • Livewire file upload wired to StoreUploadedFileAction
  • Uploaded file summary with Replace / Remove actions
  • Upload error states (unsupported format, storage failure, size)
  • Upload loading state
  • Format step placeholder + stepper
  • Full Livewire + route smoke tests

Test plan

  • composer test (174 passed)
  • composer lint
  • npm run build
  • php artisan migrate:fresh

Summary by cubic

Ships Phase 06: the first end-to-end dashboard upload flow. Users can upload a file, we store it, show a summary, and move to a format step placeholder.

  • New Features
    • New DashboardConverter Livewire component on the dashboard route.
    • Drag-and-drop upload with size validation and loading state.
    • Stores uploads via StoreUploadedFileAction; clear errors for unsupported types and storage failures.
    • Uploaded file summary with metadata plus Replace and Remove actions.
    • Stepper advances from File to Format with a placeholder; added route and Livewire smoke tests for happy path and errors.

Written for commit fcaab6c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Introduced file upload interface with drag-and-drop support
    • Step-based conversion workflow starting with file selection
    • File management capabilities to replace or remove uploaded files
    • Enhanced error handling with user-friendly validation messages

Review Change Stack

menvil added 27 commits May 29, 2026 18:18
…converter-component

CONV-071: Create DashboardConverter component
…-route-to-livewire-component

CONV-072: Connect dashboard route to Livewire component
…ad-state

CONV-073: Render empty upload state
…pload-flow

CONV-074: Test valid file upload flow
…e-upload-handling

CONV-075: Implement Livewire upload handling
…ile-summary

CONV-076: Render uploaded file summary
…ded-file-action

CONV-077: Add replace uploaded file action
…ed-file-action

CONV-078: Add remove uploaded file action
…load-ui

CONV-079: Add drag hover upload UI
…states

CONV-080: Add upload error states
…g-state

CONV-081: Add upload loading state
…laceholder

CONV-082: Add format step placeholder
…oad-flow-smoke-tests

CONV-083: Add dashboard upload flow smoke tests
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces DashboardConverter, a new Livewire component that replaces the dashboard's static file conversion UI. The component validates uploaded files, delegates storage to an existing action, manages step-based UI state (uploadformat), and provides file management actions. The implementation includes comprehensive feature tests covering upload success, state transitions, error handling, and validation.

Changes

Dashboard File Upload Feature

Layer / File(s) Summary
Component Class and Upload Logic
app/Livewire/Dashboard/DashboardConverter.php
DashboardConverter component with state properties, file validation (required, max size), upload storage delegation, exception handling with user-facing errors, step state transitions, and file management actions via replaceFile/removeFile helpers.
Component View Template
resources/views/livewire/dashboard/dashboard-converter.blade.php
Blade template with stepper UI, conditional step rendering, drag-and-drop upload input, loading and error states, file summary display for the format step, and action buttons for replace and remove operations.
Dashboard Page Integration and Route Test
resources/views/dashboard.blade.php, tests/Feature/DashboardRouteTest.php
Dashboard view swaps static conversion card for the new Livewire component. Recent conversions table removed. Route test updated to verify component presence via "Drop your file here" text.
Component Feature Tests
tests/Feature/Livewire/DashboardConverterTest.php, tests/Feature/Livewire/DashboardConverterUploadTest.php
Tests verify empty upload state, successful image upload with step advancement and file record creation, replace and remove actions, unsupported format rejection, format step display, upload summary rendering, and missing upload validation.

Sequence Diagram

sequenceDiagram
  participant User
  participant DashboardConverter
  participant StoreUploadedFileAction
  participant Storage
  participant Database

  User->>DashboardConverter: Upload file via drop/input
  DashboardConverter->>DashboardConverter: Validate (required, file, max size)
  alt Validation fails
    DashboardConverter->>User: Display validation error
  else Validation passes
    DashboardConverter->>StoreUploadedFileAction: handle(user, uploadedFile)
    StoreUploadedFileAction->>Storage: Store file
    Storage-->>StoreUploadedFileAction: File stored
    StoreUploadedFileAction->>Database: Create FileRecord
    Database-->>StoreUploadedFileAction: FileRecord created
    alt Exception (unsupported format/storage error)
      StoreUploadedFileAction-->>DashboardConverter: Exception
      DashboardConverter->>User: Display uploadError, reset to upload step
    else Success
      StoreUploadedFileAction-->>DashboardConverter: FileRecord with ID
      DashboardConverter->>DashboardConverter: Set currentFileId, advance to format step
      DashboardConverter->>User: Render format step with file summary
      User->>DashboardConverter: Click replaceFile or removeFile
      DashboardConverter->>DashboardConverter: Clear currentFileId and reset to upload step
      DashboardConverter->>User: Return to upload step
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: implementing Phase 06 of the dashboard upload flow with the version release identifier.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release/v0.1.6-phase06-dashboard-upload-flow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the release Triggers AI code review (CodeRabbit, Cubic) label May 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Livewire/Dashboard/DashboardConverter.php`:
- Around line 61-75: In replaceFile() and removeFile() call a dedicated action
in app/Actions to delete the persisted upload (e.g., DeleteUploadedFileAction)
before calling resetCurrentUpload(); locate the current upload identifier
(currentFileId or upload) inside DashboardConverter and invoke the action (via
constructor injection or app()->make()) to remove the file_record and underlying
blob, handle any errors/exceptions (log or set uploadError) and only then call
resetCurrentUpload() to clear Livewire state; keep the deletion logic inside the
new action so the component stays thin.

In `@resources/views/livewire/dashboard/dashboard-converter.blade.php`:
- Around line 5-25: The dropzone currently only toggles isDragging on x-on:drop
and doesn't prevent default or transfer files to the hidden file input
(wire:model="upload"), so dropped files won't upload and the browser may
navigate away; update the x-on:drop handler on the drop container to
preventDefault/stopPropagation, extract files from event.dataTransfer.files, and
programmatically assign them to the hidden input bound by wire:model="upload"
(or call Livewire.upload/dispatch a custom event to invoke the Livewire upload
handler such as storeUpload) so the dropped files are selected and processed
just like a normal file input; ensure the x-on:dragover and x-on:drop handlers
both call event.preventDefault() to stop default navigation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5bd6bfe7-1c68-41bd-b546-c7191d1b5454

📥 Commits

Reviewing files that changed from the base of the PR and between 90a306c and fcaab6c.

📒 Files selected for processing (6)
  • app/Livewire/Dashboard/DashboardConverter.php
  • resources/views/dashboard.blade.php
  • resources/views/livewire/dashboard/dashboard-converter.blade.php
  • tests/Feature/DashboardRouteTest.php
  • tests/Feature/Livewire/DashboardConverterTest.php
  • tests/Feature/Livewire/DashboardConverterUploadTest.php

Comment on lines +61 to +75
public function replaceFile(): void
{
$this->resetCurrentUpload();
}

public function removeFile(): void
{
$this->resetCurrentUpload();
}

private function resetCurrentUpload(): void
{
$this->reset('upload', 'currentFileId', 'uploadError');
$this->resetErrorBag();
$this->step = 'upload';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Delete the persisted upload before clearing component state.

By this point the file has already been stored via StoreUploadedFileAction, but replaceFile()/removeFile() only null out Livewire state. That leaves orphaned file_records rows and blobs behind, so “Remove” does not actually remove anything and repeated replacements will leak storage.

A small follow-up action here is to delegate cleanup to an app/Actions delete/remove action before resetCurrentUpload(). As per coding guidelines, "Controllers and Livewire components must stay thin - business logic goes into app/Actions".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Livewire/Dashboard/DashboardConverter.php` around lines 61 - 75, In
replaceFile() and removeFile() call a dedicated action in app/Actions to delete
the persisted upload (e.g., DeleteUploadedFileAction) before calling
resetCurrentUpload(); locate the current upload identifier (currentFileId or
upload) inside DashboardConverter and invoke the action (via constructor
injection or app()->make()) to remove the file_record and underlying blob,
handle any errors/exceptions (log or set uploadError) and only then call
resetCurrentUpload() to clear Livewire state; keep the deletion logic inside the
new action so the component stays thin.

Comment on lines +5 to +25
<div
x-data="{ isDragging: false }"
x-on:dragover.prevent="isDragging = true"
x-on:dragenter.prevent="isDragging = true"
x-on:dragleave.prevent="isDragging = false"
x-on:drop="isDragging = false"
x-bind:class="isDragging ? 'border-[var(--ca-primary)] bg-[var(--ca-primary)]/5' : 'border-[var(--ca-border)] bg-[var(--ca-surface-muted)]/40'"
class="flex flex-col items-center justify-center gap-4 rounded-[var(--ca-radius-md)] border-2 border-dashed px-6 py-12 text-center transition-colors">
<div class="flex h-14 w-14 items-center justify-center rounded-full bg-[var(--ca-surface-muted)] text-[var(--ca-muted)]">
<svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 7.5m0 0L7.5 12M12 7.5V18" />
</svg>
</div>

<div class="flex flex-col gap-1">
<p class="text-base font-semibold text-[var(--ca-text)]">Drop your file here</p>
<p class="text-sm text-[var(--ca-muted)]">PNG, JPG, WEBP and PDF supported in beta</p>
</div>

<label class="cursor-pointer">
<input type="file" wire:model="upload" wire:loading.attr="disabled" wire:target="upload,storeUpload" class="sr-only">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The dropzone does not handle dropped files.

x-on:drop only flips the hover state, so dropping a file neither uploads it nor selects it for the hidden input. Because the drop event is not prevented, browsers can also try to open the file and navigate away from the dashboard.

Either wire DataTransfer.files into the file input/Livewire upload path or remove the “Drop your file here” affordance until drop is actually supported.

Possible fix
-            <div
+            <div
                 x-data="{ isDragging: false }"
                 x-on:dragover.prevent="isDragging = true"
                 x-on:dragenter.prevent="isDragging = true"
                 x-on:dragleave.prevent="isDragging = false"
-                x-on:drop="isDragging = false"
+                x-on:drop.prevent="
+                    isDragging = false;
+                    const files = $event.dataTransfer?.files;
+                    if (files?.length) {
+                        $refs.upload.files = files;
+                        $refs.upload.dispatchEvent(new Event('change', { bubbles: true }));
+                    }
+                "
                 x-bind:class="isDragging ? 'border-[var(--ca-primary)] bg-[var(--ca-primary)]/5' : 'border-[var(--ca-border)] bg-[var(--ca-surface-muted)]/40'"
                 class="flex flex-col items-center justify-center gap-4 rounded-[var(--ca-radius-md)] border-2 border-dashed px-6 py-12 text-center transition-colors">
...
-                    <input type="file" wire:model="upload" wire:loading.attr="disabled" wire:target="upload,storeUpload" class="sr-only">
+                    <input x-ref="upload" type="file" wire:model="upload" wire:loading.attr="disabled" wire:target="upload,storeUpload" class="sr-only">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/views/livewire/dashboard/dashboard-converter.blade.php` around
lines 5 - 25, The dropzone currently only toggles isDragging on x-on:drop and
doesn't prevent default or transfer files to the hidden file input
(wire:model="upload"), so dropped files won't upload and the browser may
navigate away; update the x-on:drop handler on the drop container to
preventDefault/stopPropagation, extract files from event.dataTransfer.files, and
programmatically assign them to the hidden input bound by wire:model="upload"
(or call Livewire.upload/dispatch a custom event to invoke the Livewire upload
handler such as storeUpload) so the dropped files are selected and processed
just like a normal file input; ensure the x-on:dragover and x-on:drop handlers
both call event.preventDefault() to stop default navigation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Triggers AI code review (CodeRabbit, Cubic)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant