Skip to content

Release v0.1.24 — Phase 24: UX Polish#296

Merged
menvil merged 37 commits into
mainfrom
release/v0.1.24-phase24-ux-polish
Jun 5, 2026
Merged

Release v0.1.24 — Phase 24: UX Polish#296
menvil merged 37 commits into
mainfrom
release/v0.1.24-phase24-ux-polish

Conversation

@menvil

@menvil menvil commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • CONV-381 Audit current dashboard UX states (docs/ux/phase24-ux-audit.md)
  • CONV-382/383 Upload loading state — test + aria-live, aria-label, role="region" on dropzone
  • CONV-384/385 Target selection loading state — test + aria-live/aria-hidden on spinner
  • CONV-386/387 Convert double-submit guard — test confirms existing guard; aria-live on loading button
  • CONV-388 Toast notification infrastructure — toast-region.blade.php, Alpine toastRegion(), wired into app layout
  • CONV-389/390 Toast events — upload success, conversion started, conversion failed, insufficient credits
  • CONV-391 Dashboard empty state — actionable CTA "Start your first conversion"
  • CONV-392 History empty state differentiates "no jobs" vs "filter returned nothing"; billing empty state improved
  • CONV-393 SR-only labels for all filter inputs; aria-label + aria-pressed for table action buttons
  • CONV-394 User dropdown — Billing and Settings replaced with real navigable links
  • CONV-395 Mobile hamburger menu; overflow-x-auto on recent conversions table; desktop nav updated
  • CONV-396 overflow-x-auto + min-w on history and credit transactions tables
  • CONV-397 8 final UX polish smoke tests; audit document marked done

Test plan

  • composer test — 721 passing, 1 skipped
  • npm run build — clean build
  • Upload loading state renders and is accessible
  • Convert button has loading state and double-submit guard
  • Toast infrastructure renders in layout; events dispatched on upload/conversion
  • Empty states have CTA on dashboard, history, and billing pages
  • Accessibility labels on filter inputs and action buttons
  • User dropdown links to real Billing and Settings pages
  • Mobile hamburger menu renders and is keyboard accessible
  • Tables scroll horizontally on small screens without layout breakage
  • Final smoke tests cover all main MVP pages

🤖 Generated with Claude Code


Summary by cubic

Phase 24 UX polish for v0.1.24. Adds robust toast notifications (Livewire 4 compatible and null-safe), mobile navigation, accessible loading/actions, clearer empty states, and responsive tables; completes CONV-381–CONV-397.

  • New Features

    • Toast notifications via toast-region.blade.php and toastRegion(); uses Livewire 4 named-arg events and handles array/missing payloads; wired into the app layout.
    • Mobile navigation: header hamburger + slide-down menu; desktop nav links updated to History/Billing; user dropdown Billing/Settings are real links.
    • Conversion flow toasts on upload, start, unsupported conversion, insufficient credits, and failure.
  • UX Polish

    • Accessibility: SR-only labels for filters; aria-label/aria-pressed on table actions; labeled upload region; moved file input aria-label to the input (WCAG 2.5.3).
    • Empty states: dashboard, history (detects active filters), and billing now have clearer copy and CTAs; history date filters normalize empty strings to null.
    • Responsive tables: overflow-x-auto + min-width on Recent, History, and Credit Transactions; footer help cards no longer use interactive styling without links.
    • Docs/tests: added docs/ux/phase24-ux-audit.md; new smoke/feature tests cover states, toasts, navigation, accessibility, and responsiveness.

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

Review in cubic

Summary by CodeRabbit

  • New Features

    • Toast notifications provide real-time feedback on uploads and conversions
    • Mobile navigation menu for easier mobile browsing
    • Billing and Settings pages now active and accessible
  • Accessibility

    • Added labels and ARIA attributes for form inputs and dropdowns
    • Improved screen reader support throughout the interface
  • UI Improvements

    • Enhanced empty states with call-to-action buttons
    • Better responsive design for data tables

menvil and others added 30 commits June 5, 2026 18:08
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR implements Phase 24 UX polish by adding a global toast notification system, integrating toast dispatches into Livewire user flows, enhancing accessibility across tables with ARIA labels and semantic markup, improving empty-state messaging with calls-to-action, implementing mobile navigation, and activating billing/settings routes.

Changes

Phase 24 UX Polish and Accessibility

Layer / File(s) Summary
Toast notification system and integration
resources/js/app.js, resources/views/components/toast-region.blade.php, resources/views/components/layouts/app.blade.php
window.toastRegion factory creates a toast state container with add(detail) (including shape normalization and auto-removal) and remove(id) methods. <x-toast-region> Blade component renders a fixed bottom-right ARIA-labeled container that listens for toast window events and animates toasts by type. Layout injects the component globally.
DashboardConverter toast dispatch at key user flows
app/Livewire/Dashboard/DashboardConverter.php
After successful file upload, component dispatches success toast prompting format selection. On successful job creation, dispatches info toast before transitioning to converting step. On conversion failures (unsupported format, insufficient credits, generic errors), dispatches targeted error toasts before returning.
Conversion history smart empty states and accessibility
app/Livewire/ConversionHistoryTable.php, resources/views/livewire/conversion-history-table.blade.php
New hasActiveFilters() method detects whether any filter fields (search, formats, dates) differ from defaults. History view branches empty-state UI: when no jobs exist with active filters, shows "No conversions match your filters"; when no jobs exist without filters, shows "No conversion history yet" with first-use call-to-action. All filter inputs gain sr-only labels and associated control ids; date inputs receive aria-label attributes. Download/Convert Again actions enhanced with aria-label and focus-ring styling.
Recent conversions and billing empty state messaging with CTAs
resources/views/livewire/recent-conversions-table.blade.php, resources/views/livewire/billing/billing-page.blade.php
Recent conversions and billing tables display richer empty-state messages with explanatory text and "Start your first conversion" or "Start converting" call-to-action links to dashboard. Recent conversions search/filter inputs gain sr-only labels; action controls (Download, Convert again, Star) receive aria-label and aria-pressed attributes with updated focus-ring classes. Billing table improves responsive layout by adding overflow-x-auto to container and enforcing min-w-[36rem] on table.
Dashboard converter accessibility improvements
resources/views/livewire/dashboard/dashboard-converter.blade.php
Upload drop-zone declares itself as ARIA region with role="region" and aria-label. File input wrapper receives aria-label; uploading status text adds aria-live="polite". Format step loading indicator and convert step status both annotated with aria-live="polite" for screen reader announcements. Convert step loading text clarified from "Starting…" to "Starting conversion…".
Mobile navigation, user dropdown activation, and footer styling
resources/views/components/layouts/app.blade.php, resources/views/components/user-dropdown.blade.php, resources/views/components/footer-help-cards.blade.php
App layout introduces Alpine-powered mobile menu toggle (mobileMenuOpen) and conditional mobile navigation panel rendering dashboard/history/billing/settings links. Desktop header navigation updated to use route-based links for History and Billing. User dropdown converts Billing and Settings from disabled "Coming soon" buttons to active <a> links with icons, hover/focus styling, and role="menuitem". Dashboard link styling updated to match enabled state. Footer help cards switch from variant="interactive" to variant="elevated".
Comprehensive test coverage for UX improvements
tests/Feature/AccessibilitySmokeTest.php, tests/Feature/UxPolishSmokeTest.php, tests/Feature/LayoutSmokeTest.php, tests/Feature/Livewire/...
New accessibility smoke tests assert aria-label on upload area, correct <label> / for associations on history/recent filters, dropdown trigger ARIA attributes, and billing/settings links. UX Polish smoke tests verify core routes respond, DashboardConverter renders expected upload UI text, layout includes toast-region, loading indicators display, mobile nav toggle present, filter labels accessible, user dropdown contains billing/settings, and fresh-user pages show expected empty-state messages. Feature tests validate DashboardConverter toast dispatch on file upload and conversion success/failure, ConversionHistoryTable empty states with/without active filters, billing empty state, recent conversions table headers, and layout smoke tests.

Sequence Diagram(s)

sequenceDiagram
  participant Livewire as DashboardConverter
  participant Factory as window.toastRegion
  participant Component as ToastRegionComponent
  Livewire->>Livewire: storeUpload (file uploaded)
  Livewire->>Factory: dispatch('toast', {type: 'success', title: '...', message: '...'})
  Factory->>Factory: add(detail) - normalize shape
  Factory->>Factory: auto-schedule remove(id) after 4000ms
  Factory->>Component: toasts updated (reactive)
  Component->>Component: render with Alpine transitions
  Component->>Component: animate toast enter
  Component->>Factory: remove(id) on dismiss or timeout
  Factory->>Component: toast filtered out
  Component->>Component: animate toast leave
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • menvil/FileConverter#90: Introduced the DashboardConverter upload flow that toast notifications are now hooked into at file upload and format selection.
  • menvil/FileConverter#162: Modified DashboardConverter step transitions and job creation; this PR adds toast dispatch notifications at those same user-flow checkpoints.
  • menvil/FileConverter#205: Implements InsufficientCreditsException handling in convert(); this PR adds error toast dispatch on top of that exception handling.
🚥 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
Title check ✅ Passed The PR title accurately summarizes the main change: a release covering Phase 24 UX Polish work, which aligns with the changeset covering accessibility improvements, toast notifications, empty states, mobile navigation, and responsive table updates across the application.
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.24-phase24-ux-polish

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.

2 issues found across 19 files

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

Re-trigger cubic

Comment thread app/Livewire/Dashboard/DashboardConverter.php Outdated
Comment thread resources/js/app.js
All dispatch('toast', [...]) calls in DashboardConverter replaced with
named-argument form so Livewire 4 produces a flat event.detail object
instead of wrapping it in an array. Alpine toastRegion.add() now also
guards against the array-of-object shape for defensive compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@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: 7

🧹 Nitpick comments (1)
tests/Feature/LayoutSmokeTest.php (1)

9-9: ⚡ Quick win

Make the toast-container assertion more specific.

assertSee('toast-region', false) is broad; asserting id="toast-region" better validates the actual container markup.

Proposed test tweak
-        ->assertSee('toast-region', false);
+        ->assertSee('id="toast-region"', false);
🤖 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/LayoutSmokeTest.php` at line 9, Replace the broad assertion
->assertSee('toast-region', false) in tests/Feature/LayoutSmokeTest.php with a
more specific check for the container's id by asserting
->assertSee('id="toast-region"', false) so the test validates the actual markup
for the toast container rather than any incidental text; update the assertion in
the relevant test method where ->assertSee('toast-region', false) appears.
🤖 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 176-184: hasActiveFilters() currently treats empty-string date
inputs as active because it checks $this->dateFrom !== null / $this->dateTo !==
null, while updatedDateFrom() / updatedDateTo() don't normalize '' to null;
update both updatedDateFrom() and updatedDateTo() to normalize empty values to
null (e.g., if trim($this->dateFrom) === '' set $this->dateFrom = null; same for
dateTo) before calling resetPage(), so hasActiveFilters() and jobs() (which uses
when($this->dateFrom)/when($this->dateTo)) behave consistently.

In `@docs/ux/phase24-ux-audit.md`:
- Around line 213-220: Update the document to remove the contradiction between
the “Current behavior/Problem” and the “final completion checklist” for Phase
24: either relabel the earlier section that lists Billing and Settings as
disabled placeholders and focus/aria issues as “Pre-fix findings” (so it’s clear
those were resolved later), or change the “Current behavior” text to describe
the post-fix state (real Billing/Settings links, focus moved into the menu,
aria-label present, tooltips for disabled items removed or updated). Make the
same change consistently for the section referencing Phase 24 and the checklist
entries so the descriptions of “Billing”, “Settings”, the trigger button
behavior, and focus management are aligned across the document.
- Around line 125-133: Remove the stale accessibility claims about missing
row-specific aria-labels and focus rings from the audit text and update the
related entries to reflect the current implementation: remove or edit the
statements that say the "Star/Starred" button, "Convert again" button, and
Download action lack aria-labels and focus styles, and note that those controls
now include row-specific aria-label attributes and the ca-focus-ring class; also
adjust any mentions of statusBadgeClasses()/raw <span> vs <x-badge> to only call
out remaining issues (color-only status badge, missing caption/labels,
responsive overflow) so the document matches the codebase.

In `@resources/js/app.js`:
- Around line 8-22: In add(detail) guard against missing/empty payloads before
dereferencing: treat Array.isArray(detail) ? detail[0] : detail as possibly
undefined and default it to an empty object (e.g., let raw =
Array.isArray(detail) ? detail[0] : detail; let toast = raw ?? {};), then use
toast.type, toast.title, toast.message, and toast.duration with existing
fallback logic; ensure you still use this.nextId, push to this.toasts, and call
setTimeout(() => this.remove(id), duration) so no property access can throw when
detail is absent or an empty array.

In `@resources/views/components/toast-region.blade.php`:
- Around line 20-25: The component currently uses hard-coded Tailwind color
utilities in the :class mapping for toast.type (e.g., 'border-green-200
bg-green-50 text-green-800'); replace those with the RateGuru design-system
token classes instead (for each branch for toast.type ===
'success'|'error'|'info'|'warning' use the corresponding RateGuru tokenized
classes for border, background and text such as rg-border-success /
rg-bg-success / rg-text-success (or the exact token class names from our design
system) so the toast-region.blade.php :class mapping references tokenized
styling rather than raw Tailwind colors.

In `@resources/views/livewire/dashboard/dashboard-converter.blade.php`:
- Line 56: Remove the aria-label from the <label class="cursor-pointer"> element
in dashboard-converter.blade.php to avoid a Label in Name WCAG 2.5.3 violation;
locate the label with class "cursor-pointer" (the one that wraps the file input
and shows "Choose file"/"Uploading…") and delete the aria-label="Upload file"
attribute, or if you need an explicit accessible name for the input instead,
move that text to the associated <input> element (e.g. as aria-label on the
input) rather than on the label.

In `@tests/Feature/Livewire/DashboardConverterTest.php`:
- Around line 192-226: Update the two tests to assert the specific branch state
on the DashboardConverter component instead of only checking that any 'toast'
was dispatched: after calling convert() in the successful path
(DashboardConverter::class test where a credited user and FileRecord exist)
assert that currentConversionJobId is set (not null/empty) and
hasInsufficientCredits is false and convertError is null; in the
insufficient-credits path assert that hasInsufficientCredits is true,
convertError is set (non-empty) and currentConversionJobId is null/empty. Locate
these checks around the existing
Livewire::actingAs(...)->test(DashboardConverter::class)->set(...)->call('selectTargetFormat',
'jpg')->call('convert') flow and replace or augment the final
assertDispatched('toast') with the component state assertions referencing
currentConversionJobId, hasInsufficientCredits, and convertError.

---

Nitpick comments:
In `@tests/Feature/LayoutSmokeTest.php`:
- Line 9: Replace the broad assertion ->assertSee('toast-region', false) in
tests/Feature/LayoutSmokeTest.php with a more specific check for the container's
id by asserting ->assertSee('id="toast-region"', false) so the test validates
the actual markup for the toast container rather than any incidental text;
update the assertion in the relevant test method where
->assertSee('toast-region', false) appears.
🪄 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: 31b8d424-2d53-4eca-a8ec-973105b5e00b

📥 Commits

Reviewing files that changed from the base of the PR and between 4846735 and a3222b9.

📒 Files selected for processing (19)
  • app/Livewire/ConversionHistoryTable.php
  • app/Livewire/Dashboard/DashboardConverter.php
  • docs/ux/phase24-ux-audit.md
  • resources/js/app.js
  • resources/views/components/footer-help-cards.blade.php
  • resources/views/components/layouts/app.blade.php
  • resources/views/components/toast-region.blade.php
  • resources/views/components/user-dropdown.blade.php
  • resources/views/livewire/billing/billing-page.blade.php
  • resources/views/livewire/conversion-history-table.blade.php
  • resources/views/livewire/dashboard/dashboard-converter.blade.php
  • resources/views/livewire/recent-conversions-table.blade.php
  • tests/Feature/AccessibilitySmokeTest.php
  • tests/Feature/LayoutSmokeTest.php
  • tests/Feature/Livewire/Billing/BillingPageTest.php
  • tests/Feature/Livewire/ConversionHistoryTableTest.php
  • tests/Feature/Livewire/DashboardConverterTest.php
  • tests/Feature/Livewire/RecentConversionsTableTest.php
  • tests/Feature/UxPolishSmokeTest.php

Comment thread app/Livewire/ConversionHistoryTable.php
Comment thread docs/ux/phase24-ux-audit.md Outdated
Comment thread docs/ux/phase24-ux-audit.md Outdated
Comment thread resources/js/app.js
Comment on lines +20 to +25
:class="{
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',
'border-blue-200 bg-blue-50 text-blue-800': toast.type === 'info',
'border-amber-200 bg-amber-50 text-amber-800': toast.type === 'warning',
}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Replace hard-coded status colors with RateGuru tokenized styling.

This reusable component introduces raw Tailwind status colors (green/red/blue/amber) instead of tokenized design-system styling.

As per coding guidelines, "Reusable UI components must use RateGuru tokens and must not introduce random Tailwind color utilities unless the deviation is documented in docs/design/design-contract.md".

🤖 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/components/toast-region.blade.php` around lines 20 - 25, The
component currently uses hard-coded Tailwind color utilities in the :class
mapping for toast.type (e.g., 'border-green-200 bg-green-50 text-green-800');
replace those with the RateGuru design-system token classes instead (for each
branch for toast.type === 'success'|'error'|'info'|'warning' use the
corresponding RateGuru tokenized classes for border, background and text such as
rg-border-success / rg-bg-success / rg-text-success (or the exact token class
names from our design system) so the toast-region.blade.php :class mapping
references tokenized styling rather than raw Tailwind colors.

Comment thread resources/views/livewire/dashboard/dashboard-converter.blade.php Outdated
Comment thread tests/Feature/Livewire/DashboardConverterTest.php Outdated
…, test assertions

- ConversionHistoryTable: normalize empty-string dateFrom/dateTo to null in
  updatedDateFrom/updatedDateTo so hasActiveFilters() and jobs() behave consistently
- app.js: add `?? {}` fallback after array/object normalization in toastRegion.add()
  so missing or empty detail never throws on property access
- dashboard-converter: move aria-label from <label> to <input> to avoid
  WCAG 2.5.3 Label in Name violation
- DashboardConverterTest: augment toast dispatch tests with component state assertions
  (currentConversionJobId, hasInsufficientCredits, convertError)
- LayoutSmokeTest: tighten assertSee to match id="toast-region" markup specifically
- docs/ux/phase24-ux-audit.md: mark pre-fix sections as resolved, align descriptions
  with post-fix state for user dropdown and recent conversions table

Skipped: toast-region rg-* design tokens — no such token classes exist in this project.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@menvil menvil merged commit c34caf5 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