277 lines
16 KiB
PHP
277 lines
16 KiB
PHP
<div>
|
|
<!-- Performance Tracking Modal -->
|
|
@if($showModal)
|
|
<div class="fixed inset-0 z-50 overflow-y-auto" x-data="{ show: @entangle('showModal') }" x-show="show" x-cloak>
|
|
<div class="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
|
<div class="fixed inset-0 transition-opacity bg-zinc-500 bg-opacity-75" x-show="show" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"></div>
|
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen">​</span>
|
|
|
|
<div class="inline-block align-bottom bg-white dark:bg-zinc-800 rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-6xl sm:w-full sm:p-6" x-show="show" x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100" x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100" x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between mb-6">
|
|
<flux:heading size="lg">
|
|
Performance Tracking - {{ $technician?->full_name }}
|
|
</flux:heading>
|
|
<flux:button variant="ghost" size="sm" icon="x-mark" wire:click="closeModal"></flux:button>
|
|
</div>
|
|
|
|
<!-- Body -->
|
|
<div class="space-y-6">
|
|
@if($technician)
|
|
<!-- Date Range and Filters -->
|
|
<div class="flex flex-col lg:flex-row gap-4 items-end">
|
|
<div class="flex-1">
|
|
<flux:field>
|
|
<flux:label>Time Period</flux:label>
|
|
<flux:select wire:model.live="periodFilter">
|
|
<option value="current_week">Current Week</option>
|
|
<option value="current_month">Current Month</option>
|
|
<option value="current_quarter">Current Quarter</option>
|
|
<option value="current_year">Current Year</option>
|
|
<option value="last_30_days">Last 30 Days</option>
|
|
<option value="custom">Custom Range</option>
|
|
</flux:select>
|
|
</flux:field>
|
|
</div>
|
|
|
|
@if($periodFilter === 'custom')
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Start Date</flux:label>
|
|
<flux:input type="date" wire:model.live="startDate" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>End Date</flux:label>
|
|
<flux:input type="date" wire:model.live="endDate" />
|
|
</flux:field>
|
|
</div>
|
|
@endif
|
|
|
|
<flux:button variant="primary" icon="plus" wire:click="addPerformanceRecord">
|
|
Add Record
|
|
</flux:button>
|
|
</div>
|
|
|
|
<!-- Performance Stats Summary -->
|
|
@if(count($performanceStats) > 0)
|
|
<div>
|
|
<flux:heading size="lg" class="mb-4">Performance Summary</flux:heading>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
@foreach($performanceStats as $type => $stats)
|
|
<div class="bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-4 text-center">
|
|
<div class="text-2xl font-bold text-blue-600 mb-1">
|
|
{{ $stats['current'] }}
|
|
</div>
|
|
<div class="text-sm font-medium text-zinc-700 mb-2">
|
|
{{ $stats['label'] }}
|
|
</div>
|
|
<div class="text-xs text-zinc-500 space-y-1">
|
|
<div>Avg: {{ $stats['average'] }}</div>
|
|
@if($type !== 'customer_rating')
|
|
<div>Total: {{ $stats['total'] }}</div>
|
|
@endif
|
|
<div>Records: {{ $stats['count'] }}</div>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Performance Chart -->
|
|
@if(count($chartData) > 0)
|
|
<div>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<flux:heading size="lg">Performance Trend</flux:heading>
|
|
<flux:select wire:model.live="selectedMetric" class="w-48">
|
|
@foreach($metricTypes as $type => $label)
|
|
<option value="{{ $type }}">{{ $label }}</option>
|
|
@endforeach
|
|
</flux:select>
|
|
</div>
|
|
|
|
<div class="bg-white border rounded-lg p-4">
|
|
<div class="h-64 flex items-end justify-center space-x-2">
|
|
@foreach($chartData as $index => $data)
|
|
<div class="flex flex-col items-center space-y-2">
|
|
<div class="text-xs font-medium">{{ $data['formatted_value'] }}</div>
|
|
<div class="bg-blue-500 rounded-t"
|
|
style="height: {{ max(($data['value'] / collect($chartData)->max('value')) * 200, 10) }}px; width: 30px;">
|
|
</div>
|
|
<div class="text-xs text-zinc-500 transform -rotate-45">
|
|
{{ \Carbon\Carbon::parse($data['date'])->format('M d') }}
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Performance Records -->
|
|
<div>
|
|
<flux:heading size="lg" class="mb-4">Performance Records</flux:heading>
|
|
|
|
@if($filteredPerformances->count() > 0)
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full">
|
|
<thead>
|
|
<tr class="border-b border-zinc-200 dark:border-zinc-700">
|
|
<th class="text-left py-3 px-4">Date</th>
|
|
<th class="text-left py-3 px-4">Metric</th>
|
|
<th class="text-left py-3 px-4">Value</th>
|
|
<th class="text-left py-3 px-4">Period</th>
|
|
<th class="text-left py-3 px-4">Notes</th>
|
|
<th class="text-left py-3 px-4">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
@foreach($filteredPerformances as $performance)
|
|
<tr class="border-b border-zinc-200 dark:border-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-800">
|
|
<td class="py-3 px-4">{{ $performance->performance_date->format('M d, Y') }}</td>
|
|
<td class="py-3 px-4">
|
|
<flux:badge color="blue">
|
|
{{ $metricTypes[$performance->metric_type] ?? $performance->metric_type }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<span class="font-medium">{{ $performance->formatted_value }}</span>
|
|
</td>
|
|
<td class="py-3 px-4">{{ ucfirst($performance->period_type) }}</td>
|
|
<td class="py-3 px-4">
|
|
@if($performance->notes)
|
|
<div class="truncate max-w-32" title="{{ $performance->notes }}">
|
|
{{ $performance->notes }}
|
|
</div>
|
|
@else
|
|
<span class="text-zinc-400">-</span>
|
|
@endif
|
|
</td>
|
|
<td class="py-3 px-4">
|
|
<div class="flex space-x-1">
|
|
<flux:button size="sm" variant="ghost" icon="pencil"
|
|
wire:click="editPerformance({{ $performance->id }})">
|
|
</flux:button>
|
|
<flux:button size="sm" variant="ghost" icon="trash"
|
|
wire:click="deletePerformance({{ $performance->id }})"
|
|
wire:confirm="Are you sure you want to delete this performance record?">
|
|
</flux:button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@else
|
|
<div class="text-center py-8 border-2 border-dashed border-zinc-300 rounded-lg">
|
|
<flux:icon.chart-bar class="w-12 h-12 text-zinc-400 mx-auto mb-4" />
|
|
<div class="text-zinc-600 mb-2">No performance records found</div>
|
|
<div class="text-zinc-500 mb-4">Start tracking performance by adding records.</div>
|
|
<flux:button variant="primary" size="sm" icon="plus" wire:click="addPerformanceRecord">
|
|
Add Performance Record
|
|
</flux:button>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Performance Form -->
|
|
@if($editing !== false || $metric_type)
|
|
<div class="border-t pt-6">
|
|
<flux:heading size="lg" class="mb-4">
|
|
{{ $editing ? 'Edit Performance Record' : 'Add Performance Record' }}
|
|
</flux:heading>
|
|
|
|
<form wire:submit="savePerformance" class="space-y-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Metric Type</flux:label>
|
|
<flux:select wire:model="metric_type">
|
|
<option value="">Select metric type</option>
|
|
@foreach($metricTypes as $type => $label)
|
|
<option value="{{ $type }}">{{ $label }}</option>
|
|
@endforeach
|
|
</flux:select>
|
|
<flux:error name="metric_type" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Value</flux:label>
|
|
<flux:input type="number" step="0.01" wire:model="metric_value" placeholder="Enter value" />
|
|
<flux:error name="metric_value" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Date</flux:label>
|
|
<flux:input type="date" wire:model="performance_date" />
|
|
<flux:error name="performance_date" />
|
|
</flux:field>
|
|
</div>
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Period Type</flux:label>
|
|
<flux:select wire:model="period_type">
|
|
@foreach($periodTypes as $type => $label)
|
|
<option value="{{ $type }}">{{ $label }}</option>
|
|
@endforeach
|
|
</flux:select>
|
|
<flux:error name="period_type" />
|
|
</flux:field>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<flux:field>
|
|
<flux:label>Notes</flux:label>
|
|
<flux:textarea wire:model="notes" placeholder="Additional notes about this performance record..." rows="3" />
|
|
<flux:error name="notes" />
|
|
</flux:field>
|
|
</div>
|
|
|
|
<div class="flex space-x-2">
|
|
<flux:button type="submit" variant="primary">
|
|
{{ $editing ? 'Update Record' : 'Add Record' }}
|
|
</flux:button>
|
|
<flux:button type="button" variant="ghost" wire:click="resetForm">
|
|
Cancel
|
|
</flux:button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
@endif
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div class="mt-6 flex justify-end space-x-3">
|
|
<flux:button variant="ghost" wire:click="closeModal">Close</flux:button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Success Message -->
|
|
@if (session()->has('message'))
|
|
<div x-data="{ show: true }"
|
|
x-show="show"
|
|
x-transition
|
|
x-init="setTimeout(() => show = false, 3000)"
|
|
class="fixed top-4 right-4 z-50">
|
|
<div class="bg-green-50 border-green-200 border rounded-lg p-4">
|
|
<div class="flex items-center space-x-2 text-green-800">
|
|
<flux:icon.check-circle class="w-5 h-5" />
|
|
<span>{{ session('message') }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|