misc updates
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Modules\Helpdesk\Livewire;
|
namespace Modules\Helpdesk\Livewire;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
@@ -33,6 +35,14 @@ class TicketTimer extends Component
|
|||||||
$this->refreshActiveTimer();
|
$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.
|
* Start a database-backed timer for the current authenticated user.
|
||||||
*
|
*
|
||||||
@@ -61,7 +71,7 @@ class TicketTimer extends Component
|
|||||||
'ended_at' => null,
|
'ended_at' => null,
|
||||||
'logged_at' => $startedAt,
|
'logged_at' => $startedAt,
|
||||||
]);
|
]);
|
||||||
} catch (QueryException $exception) {
|
} catch (QueryException) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'timer' => 'You already have a running timer.',
|
'timer' => 'You already have a running timer.',
|
||||||
]);
|
]);
|
||||||
@@ -94,7 +104,6 @@ class TicketTimer extends Component
|
|||||||
$endedAt = Carbon::now();
|
$endedAt = Carbon::now();
|
||||||
$description = trim($this->description) !== '' ? trim($this->description) : $timer->description;
|
$description = trim($this->description) !== '' ? trim($this->description) : $timer->description;
|
||||||
$seconds = max(1, (int) $timer->started_at->diffInSeconds($endedAt));
|
$seconds = max(1, (int) $timer->started_at->diffInSeconds($endedAt));
|
||||||
|
|
||||||
$minutes = (int) ceil($seconds / 60);
|
$minutes = (int) ceil($seconds / 60);
|
||||||
|
|
||||||
$timer->forceFill([
|
$timer->forceFill([
|
||||||
@@ -122,6 +131,22 @@ class TicketTimer extends Component
|
|||||||
$this->dispatch('timer-ended');
|
$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.
|
* Render the timer view.
|
||||||
*/
|
*/
|
||||||
@@ -145,8 +170,9 @@ class TicketTimer extends Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->activeTimer = $this->activeUserTimerQuery($user)->first();
|
$this->activeTimer = $this->activeUserTimerQuery($user)->first();
|
||||||
|
|
||||||
$this->elapsedSeconds = $this->activeTimer instanceof TicketTimeLog
|
$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,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
|
||||||
Reference in New Issue
Block a user