phases one of time logged now with log display
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
Str::lower((string) ($currentCompany?->slug ?? '')) === 'mpba-limited' ||
|
||||
Str::contains(Str::lower((string) ($currentCompany?->name ?? '')), 'mpba');
|
||||
$visibleCommentCount = $ticket->comments->count();
|
||||
$ticketTimeLogs = collect($ticket->timeLogs ?? []);
|
||||
@endphp
|
||||
|
||||
<div data-dusk="helpdesk-ticket-show" dusk="helpdesk-ticket-show" class="hd-helpdesk-surface space-y-4">
|
||||
@@ -1024,7 +1025,9 @@
|
||||
{{ session('ticket-action-updated') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@include('helpdesk::partials.ticket-time-timer', [
|
||||
'ticket' => $ticket,
|
||||
])
|
||||
<form method="POST" action="{{ route('helpdesk.tickets.time-logs.store', $ticket) }}"
|
||||
class="mb-4 space-y-2">
|
||||
@csrf
|
||||
@@ -1150,7 +1153,7 @@
|
||||
<div class="min-w-0">
|
||||
<h2 class="text-lg font-black tracking-tight">Time log history</h2>
|
||||
<p class="text-xs font-semibold text-emerald-100">
|
||||
{{ $ticket->timeLogs->count() }} entries · {{ $this->formattedTimeSpent() }}
|
||||
{{ $ticketTimeLogs->count() }} entries · {{ $this->formattedTimeSpent() }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1163,7 +1166,7 @@
|
||||
|
||||
<div class="max-h-[70vh] overflow-y-auto p-5">
|
||||
<div class="space-y-3">
|
||||
@forelse ($ticket->timeLogs as $timeLog)
|
||||
@forelse ($ticketTimeLogs as $timeLog)
|
||||
<div
|
||||
class="rounded-xl border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-950">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
<div
|
||||
x-data="{
|
||||
running: @js($activeTimer !== null),
|
||||
startedAt: @js($activeTimer?->started_at?->toIso8601String()),
|
||||
elapsedSeconds: @js((int) $elapsedSeconds),
|
||||
intervalId: null,
|
||||
startClock() {
|
||||
this.stopClock();
|
||||
|
||||
if (!this.running || !this.startedAt) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.tick();
|
||||
this.intervalId = window.setInterval(() => this.tick(), 1000);
|
||||
},
|
||||
stopClock() {
|
||||
if (this.intervalId) {
|
||||
window.clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
}
|
||||
},
|
||||
tick() {
|
||||
if (!this.startedAt) {
|
||||
this.elapsedSeconds = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
this.elapsedSeconds = Math.max(0, Math.floor((Date.now() - Date.parse(this.startedAt)) / 1000));
|
||||
},
|
||||
formattedElapsed() {
|
||||
const hours = String(Math.floor(this.elapsedSeconds / 3600)).padStart(2, '0');
|
||||
const minutes = String(Math.floor((this.elapsedSeconds % 3600) / 60)).padStart(2, '0');
|
||||
const seconds = String(this.elapsedSeconds % 60).padStart(2, '0');
|
||||
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
}"
|
||||
x-init="startClock()"
|
||||
x-on:timer-started.window="running = true; startedAt = $event.detail.startedAt; startClock()"
|
||||
x-on:timer-ended.window="running = false; startedAt = null; stopClock()"
|
||||
class="mb-4 overflow-hidden rounded-xl border border-blue-200 bg-blue-50 shadow-sm dark:border-blue-900 dark:bg-blue-950/40"
|
||||
data-dusk="ticket-timer-panel"
|
||||
>
|
||||
<div class="border-b border-blue-200 bg-blue-700 px-4 py-3 text-white dark:border-blue-900">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<h3 class="text-sm font-black uppercase tracking-wide">User timer</h3>
|
||||
<p class="text-xs font-semibold text-blue-100">Database-backed timer for this ticket</p>
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="inline-flex shrink-0 items-center rounded-lg px-2.5 py-1 text-[0.68rem] font-black"
|
||||
:class="running ? 'bg-emerald-500 text-white' : 'bg-white/15 text-blue-50'"
|
||||
x-text="running ? 'Running' : 'Stopped'"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3 p-4">
|
||||
@if ($message !== null)
|
||||
<div class="rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-xs font-bold text-emerald-800 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-100">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@error('timer')
|
||||
<div class="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-xs font-bold text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-100">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-[1fr_auto] sm:items-end">
|
||||
<div>
|
||||
<label for="ticket-timer-description" class="mb-2 block text-xs font-bold text-zinc-700 dark:text-zinc-200">
|
||||
Work note
|
||||
</label>
|
||||
<textarea
|
||||
id="ticket-timer-description"
|
||||
wire:model.defer="description"
|
||||
rows="2"
|
||||
placeholder="Optional note for this timed work"
|
||||
class="block w-full rounded-lg border border-zinc-300 bg-white p-2 text-xs font-semibold text-zinc-900 shadow-sm focus:border-blue-500 focus:ring-blue-500 dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100"
|
||||
data-dusk="ticket-timer-description"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-blue-200 bg-white px-4 py-3 text-center shadow-sm dark:border-blue-900 dark:bg-zinc-950">
|
||||
<div class="text-[0.65rem] font-black uppercase tracking-[0.16em] text-blue-700 dark:text-blue-200">
|
||||
Elapsed
|
||||
</div>
|
||||
<div class="mt-1 font-mono text-xl font-black text-zinc-900 dark:text-zinc-100" x-text="formattedElapsed()">
|
||||
{{ gmdate('H:i:s', (int) $elapsedSeconds) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@if ($activeTimer === null)
|
||||
<button
|
||||
type="button"
|
||||
wire:click="startTimer"
|
||||
wire:loading.attr="disabled"
|
||||
wire:target="startTimer"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-blue-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
data-dusk="start-ticket-timer"
|
||||
>
|
||||
<i class="fa-solid fa-play mr-2" aria-hidden="true"></i>
|
||||
Start timer
|
||||
</button>
|
||||
@else
|
||||
<button
|
||||
type="button"
|
||||
wire:click="endTimer"
|
||||
wire:loading.attr="disabled"
|
||||
wire:target="endTimer"
|
||||
class="inline-flex items-center justify-center rounded-lg bg-red-600 px-3 py-2 text-xs font-black text-white shadow-sm transition hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
data-dusk="end-ticket-timer"
|
||||
>
|
||||
<i class="fa-solid fa-stop mr-2" aria-hidden="true"></i>
|
||||
End timer
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user