Skip to content

Phase 22 — History Page (CONV-347–CONV-363)#294

Merged
menvil merged 19 commits into
mainfrom
release/v0.1.22-phase22-history-page
Jun 5, 2026
Merged

Phase 22 — History Page (CONV-347–CONV-363)#294
menvil merged 19 commits into
mainfrom
release/v0.1.22-phase22-history-page

Conversation

@menvil

@menvil menvil commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds /history route (auth-protected) with full conversion history page
  • Implements ConversionHistoryTable Livewire component with user-scoped query, all filter types, row actions, and credit cost column
  • 17 commits (CONV-347 → CONV-363), 670 tests passing, lint clean, build passes

What's included

  • /history page — guest redirects to login, auth user gets 200
  • ConversionHistoryTable Livewire component with eager-loaded sourceFile, resultFile, creditCharge
  • Full table columns: File Name, From, To, Size, Created, Completed, Status, Credits, Actions
  • Status badges via <x-badge> (success/warning/danger/purple/neutral)
  • Search filter — filename + format, debounced 300ms
  • Status filter — All / Queued / Processing / Completed / Failed / Cancelled / Expired
  • Date range filterdateFrom / dateTo on created_at
  • Source/target format filters
  • Pagination (15 per page), resets on any filter change
  • Download action — only for completed + non-expired result file
  • Convert Again action — only when source file is not expired; dispatches conversion-repeat-requested event and redirects to dashboard
  • Credit cost column — shows historical captured_amount only, never recalculates

Test plan

  • composer test — 670 tests passing
  • composer lint — clean
  • npm run build — passes
  • Guest cannot access /history (redirects to login)
  • Authenticated user can access /history
  • User sees only their own conversion jobs
  • All filters work: search, status, date range, source format, target format
  • Download action visible only for completed + non-expired result
  • Convert Again visible only when source file is not expired
  • Credit cost shows historical captured amount, dash when no charge

🤖 Generated with Claude Code


Summary by cubic

Adds an authenticated History page at /history with a filterable conversion table and reuse/download actions for past jobs. Fulfills Linear CONV-347–CONV-363.

  • New Features

    • /history route (auth-only) with ConversionHistoryTable scoped to the current user and eager-loaded relations (sourceFile, resultFile, creditCharge).
    • Filters: search (file name/formats), status, date range, source/target formats; 15-per-page with pagination reset on filter changes.
    • Actions/UI: Download for completed, non-expired results; Convert Again when source file isn’t expired (dispatches conversion-repeat-requested); status badges via <x-badge>; credits column shows captured amount; columns include name, formats, size, created/completed, status, credits, actions.
  • Bug Fixes

    • Credits column now shows 0 captured credits correctly; dash only when amount is null.
    • Eager-loads sourceFile in Convert Again to avoid lazy loading and N+1.

Written for commit 05bf0a1. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

Release Notes

  • New Features
    • Added a conversion history page displaying all past conversion jobs with pagination
    • Introduced filtering options to search by file name, status, source/target formats, and date range
    • Added ability to download completed conversions and retry conversions with original files
    • Displays conversion details including file names, formats, file sizes, timestamps, status, and credit costs

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a conversion history page where authenticated users can view their conversion jobs with filtering, download completed results, and request repeat conversions for jobs with unexpired source files. The feature includes a Livewire component with multi-field filtering, eligibility checks, and comprehensive test coverage.

Changes

Conversion History Page

Layer / File(s) Summary
Livewire Component
app/Livewire/ConversionHistoryTable.php
Component maintains filter state (search, status, source/target formats, date range), provides option lists for form dropdowns, builds a scoped Eloquent query with conditional filtering, checks job eligibility for download and repeat-conversion actions, formats byte counts and maps statuses to badge variants, and renders the history table with paginated results.
Route & Page View
routes/web.php, resources/views/history/index.blade.php
Authenticated route /history serves the history index page, which renders a header and mounts the ConversionHistoryTable component.
Component Template
resources/views/livewire/conversion-history-table.blade.php
Filter bar binds search, status, format, and date-range inputs to component properties, and a conditional table displays jobs with file details, status badges, credit counts, and action buttons (Download and Convert Again) controlled by component eligibility checks.
Feature Tests: Access & Smoke
tests/Feature/History/HistoryPageAccessTest.php, tests/Feature/History/HistoryPageSmokeTest.php
Tests verify guest requests redirect to login, authenticated users can access the page and see the title, and smoke tests validate expected UI elements (headers, filenames, formats, status, credits) render correctly and are isolated per user.
Component Tests: Behavior & Filters
tests/Feature/Livewire/ConversionHistoryTableActionsTest.php, tests/Feature/Livewire/ConversionHistoryTableFiltersTest.php, tests/Feature/Livewire/ConversionHistoryTableTest.php
Tests validate action visibility (Download/Convert Again appear only for eligible jobs based on completion and file expiration), filter behavior (search by filename/format, status selection, date ranges, source/target format filtering), component rendering with user-scoped data, status badge display, and credit count formatting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • menvil/FileConverter#162: Implements the conversions.download route and expired-result validation that the ConversionHistoryTable Download action depends on.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title references a phase number and JIRA issue range but does not clearly convey the specific feature being added—a conversion history page. Consider revising to be more descriptive, such as 'Add conversion history page with filtering and row actions' to clarify the main feature without relying on external issue references.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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.22-phase22-history-page

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 Jun 5, 2026

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="resources/views/livewire/conversion-history-table.blade.php">

<violation number="1" location="resources/views/livewire/conversion-history-table.blade.php:101">
P2: Credits display incorrectly hides valid `0` captured amounts by using a truthy check.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread resources/views/livewire/conversion-history-table.blade.php Outdated
@menvil

menvil commented Jun 5, 2026

Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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

🧹 Nitpick comments (3)
tests/Feature/Livewire/ConversionHistoryTableActionsTest.php (1)

11-26: ⚡ Quick win

Consider testing download URL functionality, not just text presence.

These tests verify the "Download" text appears/disappears, but don't confirm the download link points to the correct file or uses the proper route. Text-based assertions won't catch broken URLs or incorrect route parameters.

💡 Example enhancement to verify download link

For the completed non-expired result test, you could add:

->assertSeeHtml('href="' . route('files.download', $result) . '"')

This ensures the link targets the correct file download route.

Also applies to: 45-53

🤖 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 `@tests/Feature/Livewire/ConversionHistoryTableActionsTest.php` around lines 11
- 26, Update the tests that currently only assert the presence of the "Download"
text to also assert the actual download link URL is rendered: in the test using
Livewire::actingAs(...)->test(ConversionHistoryTable::class) (and the similar
test for the expired case), after asserting see/not see, add an assertion that
checks for the expected href pointing to route('files.download', $result) so the
rendered anchor targets the correct file download route; locate the test using
FileRecord::factory() and ConversionJob::factory()->completed() to get the
$result and assert the HTML contains the download route URL.
tests/Feature/Livewire/ConversionHistoryTableFiltersTest.php (1)

11-169: ⚡ Quick win

Consider adding a test for pagination reset on filter change.

The PR objectives state that "Pagination...resets on filter changes," but there's no test verifying this behavior. Consider adding a test that:

  1. Navigates to page 2
  2. Changes a filter
  3. Asserts the component is back on page 1
💡 Example test structure
it('resets pagination when filter changes', function () {
    $user = User::factory()->create();
    
    // Create enough jobs to span multiple pages (>15)
    FileRecord::factory()
        ->count(20)
        ->for($user)
        ->create()
        ->each(fn($file) => ConversionJob::factory()
            ->for($user)
            ->for($file, 'sourceFile')
            ->create()
        );
    
    Livewire::actingAs($user)
        ->test(ConversionHistoryTable::class)
        ->call('gotoPage', 2)
        ->set('search', 'test')
        ->assertSet('paginators.page', 1);
});
🤖 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 `@tests/Feature/Livewire/ConversionHistoryTableFiltersTest.php` around lines 11
- 169, Add a test that verifies pagination resets on filter change: in a new
it('resets pagination when filter changes', ...) create more than the per-page
limit (e.g. 20) ConversionJob records for the same user (use
FileRecord::factory()->count(20) and ConversionJob::factory() for each), then
Livewire::actingAs($user)->test(ConversionHistoryTable::class)->call('gotoPage',
2)->set('search', 'something') and assert the paginator was reset with
->assertSet('paginators.page', 1); reference ConversionHistoryTable, gotoPage,
and paginators.page in the test.
tests/Feature/Livewire/ConversionHistoryTableTest.php (1)

21-53: ⚡ Quick win

Add eager loading verification to prevent N+1 queries.

The PR objectives explicitly mention "eager-loaded relations (sourceFile, resultFile, creditCharge)," but there's no test verifying eager loading is actually used. Without this, the component could trigger N+1 queries when rendering the table.

💡 Example eager loading test
use Illuminate\Support\Facades\DB;

it('eager loads relations to avoid N+1 queries', function () {
    $user = User::factory()->create();
    
    // Create multiple jobs with related records
    ConversionJob::factory()
        ->count(5)
        ->for($user)
        ->completed()
        ->create()
        ->each(function ($job) use ($user) {
            ConversionCreditCharge::factory()
                ->for($user)
                ->for($job, 'conversionJob')
                ->create(['status' => 'captured']);
        });
    
    DB::enableQueryLog();
    
    Livewire::actingAs($user)
        ->test(ConversionHistoryTable::class)
        ->assertOk();
    
    $queries = DB::getQueryLog();
    
    // Should have a small, fixed number of queries regardless of job count
    // Adjust threshold based on expected query count
    expect(count($queries))->toBeLessThan(10);
});
🤖 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 `@tests/Feature/Livewire/ConversionHistoryTableTest.php` around lines 21 - 53,
The test lacks verification that ConversionHistoryTable eagerly loads relations
(sourceFile, resultFile, creditCharge) to prevent N+1 queries; add a new test in
ConversionHistoryTableTest that creates multiple ConversionJob records (via
ConversionJob::factory()->count(n)->for($user)...) with associated
ConversionCreditCharge and files, enable query logging (DB::enableQueryLog()),
render the component with
Livewire::actingAs($user)->test(ConversionHistoryTable::class) and then assert
the total number of DB queries is small/under a threshold (e.g. <10) to prove
relations are eager-loaded; ensure the test references the relations sourceFile,
resultFile, and creditCharge so it fails if lazy-loading occurs.
🤖 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/ConversionHistoryTable.php`:
- Around line 129-131: The query that fetches $job
(ConversionJob::query()->where('user_id', auth()->id())->find($jobId)) should
eager-load the sourceFile relation to avoid a lazy load when
canConvertAgain($job) accesses $job->sourceFile; update the query to include the
relation (e.g., use with('sourceFile') on the ConversionJob query) so $job is
returned with sourceFile already loaded before calling canConvertAgain.

In `@tests/Feature/Livewire/ConversionHistoryTableActionsTest.php`:
- Around line 55-67: The test currently only asserts visibility; update it to
call the Livewire action ConversionHistoryTable::convertAgain with the created
ConversionJob's id, then assert the browser event 'conversion-repeat-requested'
was dispatched with payload ['conversionJobId' => $job->id] and that the
component redirected to the dashboard route; specifically, capture the created
ConversionJob in a $job variable, invoke ->call('convertAgain', $job->id) on the
Livewire test instance, then add
->assertDispatchedBrowserEvent('conversion-repeat-requested', ['conversionJobId'
=> $job->id]) and ->assertRedirect(route('dashboard')) (or equivalent
assertRedirect to the dashboard) to the test.

---

Nitpick comments:
In `@tests/Feature/Livewire/ConversionHistoryTableActionsTest.php`:
- Around line 11-26: Update the tests that currently only assert the presence of
the "Download" text to also assert the actual download link URL is rendered: in
the test using Livewire::actingAs(...)->test(ConversionHistoryTable::class) (and
the similar test for the expired case), after asserting see/not see, add an
assertion that checks for the expected href pointing to route('files.download',
$result) so the rendered anchor targets the correct file download route; locate
the test using FileRecord::factory() and ConversionJob::factory()->completed()
to get the $result and assert the HTML contains the download route URL.

In `@tests/Feature/Livewire/ConversionHistoryTableFiltersTest.php`:
- Around line 11-169: Add a test that verifies pagination resets on filter
change: in a new it('resets pagination when filter changes', ...) create more
than the per-page limit (e.g. 20) ConversionJob records for the same user (use
FileRecord::factory()->count(20) and ConversionJob::factory() for each), then
Livewire::actingAs($user)->test(ConversionHistoryTable::class)->call('gotoPage',
2)->set('search', 'something') and assert the paginator was reset with
->assertSet('paginators.page', 1); reference ConversionHistoryTable, gotoPage,
and paginators.page in the test.

In `@tests/Feature/Livewire/ConversionHistoryTableTest.php`:
- Around line 21-53: The test lacks verification that ConversionHistoryTable
eagerly loads relations (sourceFile, resultFile, creditCharge) to prevent N+1
queries; add a new test in ConversionHistoryTableTest that creates multiple
ConversionJob records (via ConversionJob::factory()->count(n)->for($user)...)
with associated ConversionCreditCharge and files, enable query logging
(DB::enableQueryLog()), render the component with
Livewire::actingAs($user)->test(ConversionHistoryTable::class) and then assert
the total number of DB queries is small/under a threshold (e.g. <10) to prove
relations are eager-loaded; ensure the test references the relations sourceFile,
resultFile, and creditCharge so it fails if lazy-loading occurs.
🪄 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: 6987d0d0-e3d7-4357-b253-4a391d39f12c

📥 Commits

Reviewing files that changed from the base of the PR and between 0d17ebe and c3f37da.

📒 Files selected for processing (9)
  • app/Livewire/ConversionHistoryTable.php
  • resources/views/history/index.blade.php
  • resources/views/livewire/conversion-history-table.blade.php
  • routes/web.php
  • tests/Feature/History/HistoryPageAccessTest.php
  • tests/Feature/History/HistoryPageSmokeTest.php
  • tests/Feature/Livewire/ConversionHistoryTableActionsTest.php
  • tests/Feature/Livewire/ConversionHistoryTableFiltersTest.php
  • tests/Feature/Livewire/ConversionHistoryTableTest.php

Comment thread app/Livewire/ConversionHistoryTable.php
Comment thread tests/Feature/Livewire/ConversionHistoryTableActionsTest.php
…, strengthen action tests

- Fix P2: credits column used truthy check hiding valid `captured_amount = 0`; changed to explicit !== null check
- Add with('sourceFile') eager-load in convertAgain query to avoid lazy-load when canConvertAgain accesses $job->sourceFile
- Strengthen convertAgain test: call action, assert event dispatched with payload, assert redirect to dashboard
- Strengthen download test: assert rendered href matches route('conversions.download', $job)
- Add pagination reset test: asserts page resets to 1 when search filter changes
- Add N+1 prevention test: asserts query count < 10 for 5 jobs with all relations loaded
@menvil menvil merged commit fbe6cb5 into main Jun 5, 2026
4 checks passed
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