From 0a673690a3cc42f0da115f3718b7021e0f757815 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Tue, 23 Jun 2026 10:19:51 +0100 Subject: [PATCH] core timer fixes --- .../Providers/HelpdeskServiceProvider.php | 2 + .../views/livewire/ticket-create.blade.php | 63 ++--- .../views/livewire/ticket-timer.blade.php | 240 ++++++++++-------- .../views/partials/ticket-controls.blade.php | 197 +------------- .../partials/ticket-time-timer.blade.php | 2 +- .../Feature/TicketTimerCleanUiFilesTest.php | 35 +++ .../TicketTimerLiveDisplayFilesTest.php | 4 +- .../Feature/TicketTimerSecondsFilesTest.php | 8 +- .../TicketTimerStartDisplayFilesTest.php | 5 +- Modules/Helpdesk/changelog.yaml | 26 ++ Modules/Helpdesk/module.json | 2 +- 11 files changed, 231 insertions(+), 353 deletions(-) create mode 100644 Modules/Helpdesk/Tests/Feature/TicketTimerCleanUiFilesTest.php diff --git a/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php b/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php index b441b10e..62225b12 100644 --- a/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php +++ b/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php @@ -16,6 +16,7 @@ use Modules\Helpdesk\Livewire\SettingsManager; use Modules\Helpdesk\Livewire\TicketCreate; use Modules\Helpdesk\Livewire\TicketIndex; use Modules\Helpdesk\Livewire\TicketShow; +use Modules\Helpdesk\Livewire\TicketTimer; use Modules\Helpdesk\Models\Ticket; use Modules\Helpdesk\Policies\TicketPolicy; use Modules\Helpdesk\Support\ModuleInfo; @@ -69,6 +70,7 @@ final class HelpdeskServiceProvider extends ServiceProvider Livewire::component('helpdesk.ticket-index', TicketIndex::class); Livewire::component('helpdesk.ticket-create', TicketCreate::class); Livewire::component('helpdesk.ticket-show', TicketShow::class); + Livewire::component('helpdesk.ticket-timer', TicketTimer::class); Livewire::component('helpdesk.dashboard-widget', HelpdeskDashboardWidget::class); Livewire::component('helpdesk.kanban-board', KanbanBoard::class); Livewire::component('helpdesk.settings-manager', SettingsManager::class); diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php index b835fa53..248b3b30 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php @@ -7,7 +7,7 @@ 'Raise a new support request with priority, product/service, project, category, team and full details.', ]) -
+

Ticket details

@@ -330,16 +330,13 @@ class="rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700"> Cancel -
@@ -363,50 +360,47 @@ outline: none; } - @script - @endscript diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-timer.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-timer.blade.php index 71a7d8e0..6dd1bb8d 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-timer.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-timer.blade.php @@ -1,133 +1,151 @@
@php $hasRunningTimer = $activeTimer && $activeTimer->ended_at === null; + $startedAtIso = $hasRunningTimer ? $activeTimer->started_at?->toIso8601String() : null; + $startedAtDisplay = $hasRunningTimer ? $activeTimer->started_at?->format('d M Y H:i') : null; @endphp -
- - - +
+
+
+ + + -
-
-

Work timer

-

- Start a user-specific timer. It remains active if the browser is closed. +

+ Ticket timer +

+ +

+ Record focused work against this ticket.

- - @if ($hasRunningTimer) -
-
Running
-
00:00:00
-
- @else -
-
Stopped
-
00:00:00
-
- @endif
- @if ($message) -
- {{ $message }} + @if ($hasRunningTimer) +
+
+ + Running +
+ +
+ {{ gmdate('H:i:s', max(0, (int) $elapsedSeconds)) }} +
+ +

+ Started {{ $startedAtDisplay }} +

@endif +
- @error('timer') -
- {{ $message }} -
- @enderror - -
- - - + @if ($message) +
+ {{ $message }}
+ @endif -
- @if ($hasRunningTimer) - - @else - - @endif + @error('timer') +
+ {{ $message }}
+ @enderror + +
+ + + +
+ +
+

+ {{ $hasRunningTimer ? 'End the timer to add the elapsed time to the ticket.' : 'Start a timer when work begins. Only one active timer is allowed.' }} +

+ + @if ($hasRunningTimer) + + @else + + @endif
+ +
diff --git a/Modules/Helpdesk/Resources/views/partials/ticket-controls.blade.php b/Modules/Helpdesk/Resources/views/partials/ticket-controls.blade.php index 14d38b7e..b157108f 100644 --- a/Modules/Helpdesk/Resources/views/partials/ticket-controls.blade.php +++ b/Modules/Helpdesk/Resources/views/partials/ticket-controls.blade.php @@ -1,47 +1,3 @@ -@php - use Illuminate\Support\Facades\Schema; - use Modules\Helpdesk\Models\TicketTimeLog; - - $ticketTimeLogTable = (new TicketTimeLog())->getTable(); - - $hasEndedAtColumn = Schema::hasColumn($ticketTimeLogTable, 'ended_at'); - $hasStoppedAtColumn = Schema::hasColumn($ticketTimeLogTable, 'stopped_at'); - $hasFinishedAtColumn = Schema::hasColumn($ticketTimeLogTable, 'finished_at'); - $hasUserIdColumn = Schema::hasColumn($ticketTimeLogTable, 'user_id'); - - $runningTimerQuery = TicketTimeLog::query() - ->where('ticket_id', $ticket->getKey()) - ->whereNotNull('started_at') - ->orderByDesc('started_at'); - - if ($hasUserIdColumn) { - $runningTimerQuery->where('user_id', auth()->id()); - } - - if ($hasEndedAtColumn) { - $runningTimerQuery->whereNull('ended_at'); - } - - if ($hasStoppedAtColumn) { - $runningTimerQuery->whereNull('stopped_at'); - } - - if ($hasFinishedAtColumn) { - $runningTimerQuery->whereNull('finished_at'); - } - - $runningTimer = ($hasEndedAtColumn || $hasStoppedAtColumn || $hasFinishedAtColumn) - ? $runningTimerQuery->first() - : null; - - $runningTimerStartedAt = $runningTimer?->started_at; - $runningTimerElapsedSeconds = $runningTimerStartedAt - ? max(0, now()->diffInSeconds($runningTimerStartedAt)) - : 0; - $runningTimerElapsedDisplay = gmdate('H:i:s', $runningTimerElapsedSeconds); - $hasRunningTimer = $runningTimerStartedAt !== null; -@endphp - @can('update', $ticket)
Log time, assignment, status and priority

- - - - - - - - Running - - - {{ $runningTimerElapsedDisplay }} - -
@@ -93,41 +32,9 @@
@endif - @if ($hasRunningTimer) -
-
-
-

- Timer running -

- -

- Started {{ $runningTimerStartedAt->format('d M Y H:i:s') }} -

-
- -
-

- {{ $runningTimerElapsedDisplay }} -

- -

- Elapsed -

-
-
-
- @endif - -
- @include('helpdesk::partials.ticket-time-timer', [ - 'ticket' => $ticket, - ]) -
+ @include('helpdesk::partials.ticket-time-timer', [ + 'ticket' => $ticket, + ]) @@ -247,102 +154,4 @@ - - @endcan diff --git a/Modules/Helpdesk/Resources/views/partials/ticket-time-timer.blade.php b/Modules/Helpdesk/Resources/views/partials/ticket-time-timer.blade.php index 6fa54a66..715c0947 100644 --- a/Modules/Helpdesk/Resources/views/partials/ticket-time-timer.blade.php +++ b/Modules/Helpdesk/Resources/views/partials/ticket-time-timer.blade.php @@ -1,3 +1,3 @@ @can('update', $ticket) - @livewire('Modules\\Helpdesk\\Livewire\\TicketTimer', ['ticket' => $ticket], key('helpdesk-ticket-timer-'.$ticket->getKey())) + @livewire('helpdesk.ticket-timer', ['ticket' => $ticket], key('helpdesk-ticket-timer-'.$ticket->getKey())) @endcan diff --git a/Modules/Helpdesk/Tests/Feature/TicketTimerCleanUiFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketTimerCleanUiFilesTest.php new file mode 100644 index 00000000..a7f2e501 --- /dev/null +++ b/Modules/Helpdesk/Tests/Feature/TicketTimerCleanUiFilesTest.php @@ -0,0 +1,35 @@ +assertStringContainsString("@include('helpdesk::partials.ticket-time-timer'", $controls); + $this->assertStringNotContainsString('data-helpdesk-running-summary-badge', $controls); + $this->assertStringNotContainsString('data-helpdesk-panel-elapsed', $controls); + $this->assertStringNotContainsString('MutationObserver', $controls); + $this->assertStringContainsString('data-helpdesk-live-elapsed', $timer); + $this->assertStringContainsString("Livewire::component('helpdesk.ticket-timer'", $provider); + } + + /** + * The module must not contain accidental duplicate Blade folders or macOS artefacts. + */ + public function test_helpdesk_module_does_not_contain_known_rubbish_files(): void + { + $this->assertDirectoryDoesNotExist(base_path('Modules/Helpdesk/resources/views/views')); + $this->assertFileDoesNotExist(base_path('Modules/Helpdesk/.DS_Store')); + $this->assertFileDoesNotExist(base_path('Modules/Helpdesk/resources/.DS_Store')); + } +} diff --git a/Modules/Helpdesk/Tests/Feature/TicketTimerLiveDisplayFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketTimerLiveDisplayFilesTest.php index 14b656bf..21e5d17c 100644 --- a/Modules/Helpdesk/Tests/Feature/TicketTimerLiveDisplayFilesTest.php +++ b/Modules/Helpdesk/Tests/Feature/TicketTimerLiveDisplayFilesTest.php @@ -13,10 +13,10 @@ class TicketTimerLiveDisplayFilesTest extends TestCase { $view = (string) file_get_contents(base_path('Modules/Helpdesk/resources/views/livewire/ticket-timer.blade.php')); - $this->assertStringContainsString('x-data', $view); + $this->assertStringContainsString('data-helpdesk-live-elapsed', $view); $this->assertStringContainsString('Date.parse', $view); $this->assertStringContainsString('setInterval', $view); - $this->assertStringContainsString('started_at', $view); + $this->assertStringContainsString('data-helpdesk-started-at', $view); $this->assertStringContainsString('data-dusk="ticket-running-time"', $view); $this->assertStringNotContainsString('localStorage', $view); $this->assertStringNotContainsString('sessionStorage', $view); diff --git a/Modules/Helpdesk/Tests/Feature/TicketTimerSecondsFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketTimerSecondsFilesTest.php index 856d382c..dc27665a 100644 --- a/Modules/Helpdesk/Tests/Feature/TicketTimerSecondsFilesTest.php +++ b/Modules/Helpdesk/Tests/Feature/TicketTimerSecondsFilesTest.php @@ -18,9 +18,9 @@ class TicketTimerSecondsFilesTest extends TestCase $this->assertStringContainsString("'seconds'", $component); $this->assertStringContainsString("'seconds'", $model); $this->assertStringContainsString('diffInSeconds', $component); - $this->assertStringContainsString('sum(\'seconds\')', $component); - $this->assertStringContainsString('HH:MM:SS', 'HH:MM:SS'); - $this->assertStringContainsString('formatted()', $view); + $this->assertStringContainsString('sum('seconds')', $component); + $this->assertStringContainsString('formatElapsed', $view); + $this->assertStringContainsString('data-helpdesk-live-elapsed', $view); $this->assertStringNotContainsString('localStorage', $component); $this->assertStringNotContainsString('localStorage', $view); } @@ -33,7 +33,7 @@ class TicketTimerSecondsFilesTest extends TestCase $partial = (string) file_get_contents(base_path('Modules/Helpdesk/resources/views/partials/ticket-time-log-history-query.blade.php')); $this->assertStringContainsString('TicketTimeLog::query()', $partial); - $this->assertStringContainsString('whereNotNull(\'ended_at\')', $partial); + $this->assertStringContainsString('whereNotNull('ended_at')', $partial); $this->assertStringContainsString('seconds', $partial); } } diff --git a/Modules/Helpdesk/Tests/Feature/TicketTimerStartDisplayFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketTimerStartDisplayFilesTest.php index f48ee809..8a16bcfb 100644 --- a/Modules/Helpdesk/Tests/Feature/TicketTimerStartDisplayFilesTest.php +++ b/Modules/Helpdesk/Tests/Feature/TicketTimerStartDisplayFilesTest.php @@ -13,13 +13,14 @@ class TicketTimerStartDisplayFilesTest extends TestCase { $component = (string) file_get_contents(base_path('Modules/Helpdesk/Livewire/TicketTimer.php')); $view = (string) file_get_contents(base_path('Modules/Helpdesk/resources/views/livewire/ticket-timer.blade.php')); + $partial = (string) file_get_contents(base_path('Modules/Helpdesk/resources/views/partials/ticket-time-timer.blade.php')); $this->assertStringContainsString('$this->activeTimer = $timer->fresh();', $component); $this->assertStringContainsString('$this->elapsedSeconds = 0;', $component); $this->assertStringContainsString("dispatch('timer-started')", $component); $this->assertStringContainsString('$hasRunningTimer = $activeTimer && $activeTimer->ended_at === null;', $view); - $this->assertStringContainsString('data-helpdesk-running-timer', $view); - $this->assertStringContainsString('x-on:timer-started.window', $view); + $this->assertStringContainsString('data-helpdesk-live-elapsed', $view); + $this->assertStringContainsString("@livewire('helpdesk.ticket-timer'", $partial); $this->assertStringNotContainsString('wire:ignore', $view); } } diff --git a/Modules/Helpdesk/changelog.yaml b/Modules/Helpdesk/changelog.yaml index 721d4317..c93fe1f4 100644 --- a/Modules/Helpdesk/changelog.yaml +++ b/Modules/Helpdesk/changelog.yaml @@ -7,3 +7,29 @@ - Added persistent started_at, ended_at, and duration_seconds fields to existing helpdesk time logs where available. - Added a reusable Blade include for placing the timer above the existing manual time log form. - Added regression tests for module path, Livewire view root safety, and non-session timer state. +- version: 1.9.30 + date: 2026-06-23 + type: bugfix + summary: Clean up Helpdesk timer UI state and remove duplicate timer display code. + changes: + - Replaced the oversized timer display with a compact professional ticket timer panel. + - Hid the elapsed timer before start and after end so it only displays while a timer is active. + - Added an inline loaded browser timer updater in the Livewire view instead of an unused standalone JavaScript file. + - Removed duplicate running timer summary and MutationObserver logic from ticket controls. + - Registered the timer Livewire component with the helpdesk.ticket-timer alias. + - Removed duplicate Resources/views/views timer files and macOS artefacts from the clean module. + - Added file-level regression coverage for the timer UI cleanup. +- version: 1.9.31 + date: 2026-06-23 + type: bugfix + summary: Reduce Helpdesk start timer button size. + changes: + - Made the inactive-state Start timer button more compact while preserving accessible focus and loading states. + - Kept the timer display hidden before start and after end, with no new standalone JavaScript files. +- version: 1.9.32 + date: 2026-06-23 + type: bugfix + summary: Widen Helpdesk start timer button so the label fits on one line. + changes: + - Updated the inactive Start timer action with a minimum width and no-wrap text so the label does not split. + - Preserved the clean inline timer display behaviour with no unused standalone JavaScript file. diff --git a/Modules/Helpdesk/module.json b/Modules/Helpdesk/module.json index 31300457..eac8e81b 100644 --- a/Modules/Helpdesk/module.json +++ b/Modules/Helpdesk/module.json @@ -22,5 +22,5 @@ "Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider" ], "files": [], - "version": "1.9.29" + "version": "1.9.32" }