misc updates

This commit is contained in:
Mark Gregory
2026-06-23 08:14:35 +01:00
parent 39ba020951
commit 90ba0bfafa
3 changed files with 120 additions and 3 deletions
+29 -3
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace Modules\Helpdesk\Livewire;
use Carbon\Carbon;
@@ -33,6 +35,14 @@ class TicketTimer extends Component
$this->refreshActiveTimer();
}
/**
* Refresh the timer every poll cycle while the timer is running.
*/
public function refreshTimer(): void
{
$this->refreshActiveTimer();
}
/**
* Start a database-backed timer for the current authenticated user.
*
@@ -61,7 +71,7 @@ class TicketTimer extends Component
'ended_at' => null,
'logged_at' => $startedAt,
]);
} catch (QueryException $exception) {
} catch (QueryException) {
throw ValidationException::withMessages([
'timer' => 'You already have a running timer.',
]);
@@ -94,7 +104,6 @@ class TicketTimer extends Component
$endedAt = Carbon::now();
$description = trim($this->description) !== '' ? trim($this->description) : $timer->description;
$seconds = max(1, (int) $timer->started_at->diffInSeconds($endedAt));
$minutes = (int) ceil($seconds / 60);
$timer->forceFill([
@@ -122,6 +131,22 @@ class TicketTimer extends Component
$this->dispatch('timer-ended');
}
/**
* Get the currently elapsed timer duration.
*/
public function getElapsedTimeProperty(): string
{
return gmdate('H:i:s', $this->elapsedSeconds);
}
/**
* Determine whether the current user has a running timer.
*/
public function getIsRunningProperty(): bool
{
return $this->activeTimer instanceof TicketTimeLog;
}
/**
* Render the timer view.
*/
@@ -145,8 +170,9 @@ class TicketTimer extends Component
}
$this->activeTimer = $this->activeUserTimerQuery($user)->first();
$this->elapsedSeconds = $this->activeTimer instanceof TicketTimeLog
? $this->activeTimer->started_at->diffInSeconds(Carbon::now())
? max(0, (int) $this->activeTimer->started_at->diffInSeconds(Carbon::now()))
: 0;
}
@@ -0,0 +1,88 @@
<section
@if ($this->isRunning) wire:poll.1s="refreshTimer" @endif
class="rounded-xl border border-emerald-700/70 bg-emerald-950/40 p-4 text-emerald-50 shadow-sm"
data-helpdesk-ticket-timer
>
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div class="flex min-w-0 gap-3">
<span class="inline-flex h-14 w-14 shrink-0 items-center justify-center rounded-xl bg-emerald-600 text-white">
<i class="fa-solid fa-stopwatch text-xl" aria-hidden="true"></i>
</span>
<div class="min-w-0">
<h3 class="text-lg font-black text-emerald-50">Work timer</h3>
<p class="mt-1 max-w-sm text-sm font-semibold leading-6 text-emerald-100">
Start a user-specific timer. It remains active if the browser is closed.
</p>
</div>
</div>
@if ($this->isRunning)
<div
class="shrink-0 rounded-xl bg-emerald-600 px-5 py-3 text-center text-white shadow-sm"
data-helpdesk-running-timer
data-dusk="ticket-running-timer-summary"
>
<div class="text-xs font-black uppercase tracking-[0.25em]">
Running
</div>
<div
class="mt-2 font-mono text-2xl font-black"
data-dusk="ticket-running-elapsed-time"
>
{{ $this->elapsedTime }}
</div>
</div>
@endif
</div>
@if ($message)
<div class="mt-4 rounded-xl border border-emerald-700 bg-zinc-950 px-4 py-3 text-sm font-bold text-emerald-50">
{{ $message }}
</div>
@endif
<div class="mt-4 space-y-2">
<label for="ticket-timer-description" class="block text-sm font-bold text-emerald-50">
Timer note
</label>
<textarea
id="ticket-timer-description"
wire:model="description"
rows="3"
placeholder="Optional work note"
class="block w-full rounded-xl border border-emerald-700 bg-zinc-950 p-3 text-sm font-semibold text-emerald-50 shadow-sm placeholder:text-zinc-500 focus:border-emerald-400 focus:ring-emerald-400"
></textarea>
</div>
@error('timer')
<p class="mt-3 text-sm font-bold text-red-300">{{ $message }}</p>
@enderror
<div class="mt-4 flex flex-wrap gap-2">
@if ($this->isRunning)
<button
type="button"
wire:click="endTimer"
wire:loading.attr="disabled"
class="inline-flex items-center justify-center rounded-xl bg-red-600 px-4 py-3 text-sm font-black text-white shadow-sm transition hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-60"
>
<i class="fa-solid fa-stop mr-2" aria-hidden="true"></i>
End timer
</button>
@else
<button
type="button"
wire:click="startTimer"
wire:loading.attr="disabled"
class="inline-flex items-center justify-center rounded-xl bg-emerald-600 px-4 py-3 text-sm font-black text-white shadow-sm transition hover:bg-emerald-700 disabled:cursor-not-allowed disabled:opacity-60"
>
<i class="fa-solid fa-play mr-2" aria-hidden="true"></i>
Start timer
</button>
@endif
</div>
</section>
@@ -0,0 +1,3 @@
@can('update', $ticket)
@livewire('Modules\\Helpdesk\\Livewire\\TicketTimer', ['ticket' => $ticket], key('helpdesk-ticket-timer-'.$ticket->getKey()))
@endcan