partial update create kpi and progress bar

This commit is contained in:
2025-07-04 18:27:32 +07:00
parent 0ef03fe7cb
commit fa554446ca
19 changed files with 2150 additions and 45 deletions

View File

@@ -0,0 +1,252 @@
@extends('layouts.backapp')
@section('title', 'Tambah Target KPI')
@section('styles')
<style>
.select2-container .select2-selection {
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.select2-container.select2-container--focus .select2-selection {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(197, 214, 233, 0.25);
}
.select2-container .select2-selection--single .select2-selection__rendered {
padding-left: 0;
line-height: 1.5;
}
.select2-container .select2-selection--single .select2-selection__arrow {
height: 100%;
}
.select2-results__option--highlighted[aria-selected] {
background-color: #007bff;
color: white;
}
/* Limit Select2 dropdown height */
.select2-results__options {
max-height: 200px;
overflow-y: auto;
}
/* Style for Select2 results */
.select2-results__option {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
}
.select2-results__option:last-child {
border-bottom: none;
}
/* Improve Select2 search box */
.select2-search--dropdown .select2-search__field {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 100%;
box-sizing: border-box;
}
</style>
@endsection
@section('content')
<div class="kt-content kt-grid__item kt-grid__item--fluid kt-grid kt-grid--hor" id="kt_content">
<div class="kt-container kt-container--fluid kt-grid__item kt-grid__item--fluid">
<div class="kt-portlet kt-portlet--mobile">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<h3 class="kt-portlet__head-title">
Tambah Target KPI
</h3>
</div>
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-actions">
<a href="{{ route('kpi.targets.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</div>
</div>
<div class="kt-portlet__body">
<form id="kpi-form" method="POST" action="{{ route('kpi.targets.store') }}">
@csrf
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="user_id" class="form-control-label">Mekanik <span class="text-danger">*</span></label>
<select name="user_id" id="user_id" class="form-control select2" required>
<option value="">Pilih Mekanik</option>
@foreach($mechanics as $mechanic)
<option value="{{ $mechanic->id }}" {{ old('user_id') == $mechanic->id ? 'selected' : '' }}>
{{ $mechanic->name }} ({{ $mechanic->dealer->name ?? 'N/A' }})
</option>
@endforeach
</select>
@if($mechanics->isEmpty())
<div class="alert alert-warning mt-2">
<i class="fas fa-exclamation-triangle"></i>
Tidak ada mekanik yang ditemukan. Pastikan ada user dengan role "mechanic" di sistem.
</div>
@else
<small class="form-text text-muted">
Ditemukan {{ $mechanics->count() }} mekanik.
@if($mechanics->count() >= 50)
Menampilkan 50 mekanik pertama. Gunakan pencarian untuk menemukan mekanik tertentu.
@endif
</small>
@endif
@error('user_id')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="target_value" class="form-control-label">Target Nilai <span class="text-danger">*</span></label>
<input type="number" name="target_value" id="target_value" class="form-control"
value="{{ old('target_value') }}" min="1" required>
@error('target_value')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="description" class="form-control-label">Deskripsi</label>
<textarea name="description" id="description" class="form-control" rows="3"
placeholder="Deskripsi target (opsional)">{{ old('description') }}</textarea>
@error('description')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" name="is_active" id="is_active" class="custom-control-input"
value="1" {{ old('is_active', true) ? 'checked' : '' }}>
<label class="custom-control-label" for="is_active">
Target Aktif
</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-group">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Simpan Target
</button>
<a href="{{ route('kpi.targets.index') }}" class="btn btn-secondary">Kembali</a>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
@push('javascripts')
<script>
$(document).ready(function() {
// Initialize Select2 with fallback
try {
// Initialize Select2 for mechanics with search limit
$('#user_id').select2({
theme: 'bootstrap4',
width: '100%',
placeholder: 'Pilih Mekanik',
allowClear: true,
minimumInputLength: 1,
maximumInputLength: 50,
maximumResultsForSearch: 10,
language: {
inputTooShort: function() {
return "Masukkan minimal 1 karakter untuk mencari";
},
inputTooLong: function() {
return "Maksimal 50 karakter";
},
noResults: function() {
return "Tidak ada hasil ditemukan";
},
searching: function() {
return "Mencari...";
}
}
});
} catch (error) {
console.log('Select2 not available, using regular select');
// Fallback: ensure regular select works
$('.select2').removeClass('select2').addClass('form-control');
}
// Form validation
$('#kpi-form').on('submit', function(e) {
var isValid = true;
var errors = [];
// Clear previous errors
$('.text-danger').remove();
// Validate required fields
if (!$('#user_id').val()) {
errors.push('Mekanik harus dipilih');
isValid = false;
}
if (!$('#target_value').val() || $('#target_value').val() < 1) {
errors.push('Target nilai harus diisi dan minimal 1');
isValid = false;
}
if (!isValid) {
e.preventDefault();
if (typeof Swal !== 'undefined') {
Swal.fire({
icon: 'error',
title: 'Validasi Gagal',
html: errors.join('<br>'),
confirmButtonText: 'OK'
});
} else {
alert('Validasi Gagal:\n' + errors.join('\n'));
}
}
});
});
</script>
@endpush

View File

@@ -0,0 +1,263 @@
@extends('layouts.backapp')
@section('title', 'Edit Target KPI')
@section('styles')
<style>
.select2-container .select2-selection {
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.select2-container.select2-container--focus .select2-selection {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.select2-container .select2-selection--single .select2-selection__rendered {
padding-left: 0;
line-height: 1.5;
}
.select2-container .select2-selection--single .select2-selection__arrow {
height: 100%;
}
.select2-results__option--highlighted[aria-selected] {
background-color: #007bff;
color: white;
}
/* Ensure Select2 is visible */
.select2-container {
z-index: 9999;
}
.select2-dropdown {
z-index: 9999;
}
/* Fix Select2 width */
.select2-container--default .select2-selection--single {
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
}
/* Limit Select2 dropdown height */
.select2-results__options {
max-height: 200px;
overflow-y: auto;
}
/* Style for Select2 results */
.select2-results__option {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
}
.select2-results__option:last-child {
border-bottom: none;
}
/* Improve Select2 search box */
.select2-search--dropdown .select2-search__field {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 100%;
box-sizing: border-box;
}
</style>
@endsection
@section('content')
<div class="kt-content kt-grid__item kt-grid__item--fluid kt-grid kt-grid--hor" id="kt_content">
<div class="kt-container kt-container--fluid kt-grid__item kt-grid__item--fluid">
<div class="kt-portlet kt-portlet--mobile">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<h3 class="kt-portlet__head-title">
Edit Target KPI
</h3>
</div>
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-actions">
<a href="{{ route('kpi.targets.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</div>
</div>
<div class="kt-portlet__body">
<form id="kpi-form" method="POST" action="{{ route('kpi.targets.update', $target->id) }}">
@csrf
@method('PUT')
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="user_id" class="form-control-label">Mekanik <span class="text-danger">*</span></label>
<select name="user_id" id="user_id" class="form-control select2" required>
<option value="">Pilih Mekanik</option>
@foreach($mechanics as $mechanic)
@php
$isSelected = old('user_id', $target->user_id) == $mechanic->id;
@endphp
<option value="{{ $mechanic->id }}"
{{ $isSelected ? 'selected' : '' }}>
{{ $mechanic->name }}
</option>
@endforeach
</select>
@error('user_id')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="target_value" class="form-control-label">Target Nilai <span class="text-danger">*</span></label>
<input type="number" name="target_value" id="target_value" class="form-control"
value="{{ old('target_value', $target->target_value) }}" min="1" required>
@error('target_value')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="description" class="form-control-label">Deskripsi</label>
<textarea name="description" id="description" class="form-control" rows="3"
placeholder="Deskripsi target (opsional)">{{ old('description', $target->description) }}</textarea>
@error('description')
<span class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" name="is_active" id="is_active" class="custom-control-input"
value="1" {{ old('is_active', $target->is_active) ? 'checked' : '' }}>
<label class="custom-control-label" for="is_active">
Target Aktif
</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="form-group">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Update Target
</button>
<a href="{{ route('kpi.targets.index') }}" class="btn btn-secondary">Kembali</a>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
@push('javascripts')
<script>
$(document).ready(function() {
// Initialize Select2 with fallback and delay
setTimeout(function() {
try {
// Initialize Select2 for mechanics with search limit
$('#user_id').select2({
theme: 'bootstrap4',
width: '100%',
placeholder: 'Pilih Mekanik',
allowClear: true,
minimumInputLength: 1,
maximumInputLength: 50,
maximumResultsForSearch: 10,
language: {
inputTooShort: function() {
return "Masukkan minimal 1 karakter untuk mencari";
},
inputTooLong: function() {
return "Maksimal 50 karakter";
},
noResults: function() {
return "Tidak ada hasil ditemukan";
},
searching: function() {
return "Mencari...";
}
}
});
} catch (error) {
console.log('Select2 not available, using regular select');
// Fallback: ensure regular select works
$('.select2').removeClass('select2').addClass('form-control');
}
}, 100);
// Form validation
$('#kpi-form').on('submit', function(e) {
var isValid = true;
var errors = [];
// Clear previous errors
$('.text-danger').remove();
// Validate required fields
if (!$('#user_id').val()) {
errors.push('Mekanik harus dipilih');
isValid = false;
}
if (!$('#target_value').val() || $('#target_value').val() < 1) {
errors.push('Target nilai harus diisi dan minimal 1');
isValid = false;
}
if (!isValid) {
e.preventDefault();
if (typeof Swal !== 'undefined') {
Swal.fire({
icon: 'error',
title: 'Validasi Gagal',
html: errors.join('<br>'),
confirmButtonText: 'OK'
});
} else {
alert('Validasi Gagal:\n' + errors.join('\n'));
}
}
});
});
</script>
@endpush

View File

@@ -0,0 +1,212 @@
@extends('layouts.backapp')
@section('content')
<div class="kt-content kt-grid__item kt-grid__item--fluid kt-grid kt-grid--hor" id="kt_content">
<div class="kt-container kt-container--fluid kt-grid__item kt-grid__item--fluid">
<div class="kt-portlet kt-portlet--mobile">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<h3 class="kt-portlet__head-title">
Manajemen Target KPI
</h3>
</div>
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-actions">
<a href="{{ route('kpi.targets.create') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> Tambah Target
</a>
</div>
</div>
</div>
<div class="kt-portlet__body">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
@if(session('error'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
{{ session('error') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
<div class="table-responsive">
<table class="table table-striped table-bordered" id="kpiTargetsTable">
<thead>
<tr>
<th>No</th>
<th>Mekanik</th>
<th>Target</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($targets as $target)
<tr>
<td>{{ $loop->iteration }}</td>
<td>{{ $target->user->name }}</td>
<td>{{ number_format($target->target_value) }}</td>
<td>
@if($target->is_active)
<span class="badge badge-success">Aktif</span>
@else
<span class="badge badge-secondary">Nonaktif</span>
@endif
</td>
<td>
<div class="btn-group" role="group">
<a href="{{ route('kpi.targets.show', $target->id) }}"
class="btn btn-sm btn-info" title="Detail">
<i class="fas fa-eye"></i>
</a>
<a href="{{ route('kpi.targets.edit', $target->id) }}"
class="btn btn-sm btn-warning" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button"
class="btn btn-sm btn-{{ $target->is_active ? 'warning' : 'success' }}"
onclick="toggleStatus({{ $target->id }})"
title="{{ $target->is_active ? 'Nonaktifkan' : 'Aktifkan' }}">
<i class="fas fa-{{ $target->is_active ? 'pause' : 'play' }}"></i>
</button>
<form action="{{ route('kpi.targets.destroy', $target->id) }}"
method="POST"
style="display: inline;"
onsubmit="return confirm('Yakin ingin menghapus target ini?')">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-sm btn-danger" title="Hapus">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center">Tidak ada data target KPI</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($targets->hasPages())
<div class="d-flex justify-content-center">
{{ $targets->links() }}
</div>
@endif
</div>
</div>
</div>
</div>
<!-- Filter Modal -->
<div class="modal fade" id="filterModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Filter Target KPI</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="{{ route('kpi.targets.index') }}" method="GET">
<div class="modal-body">
<div class="form-group">
<label>Mekanik</label>
<select name="user_id" class="form-control">
<option value="">Semua Mekanik</option>
@foreach($mechanics as $mechanic)
<option value="{{ $mechanic->id }}"
{{ request('user_id') == $mechanic->id ? 'selected' : '' }}>
{{ $mechanic->name }}
</option>
@endforeach
</select>
</div>
<div class="form-group">
<label>Status</label>
<select name="is_active" class="form-control">
<option value="">Semua Status</option>
<option value="1" {{ request('is_active') == '1' ? 'selected' : '' }}>Aktif</option>
<option value="0" {{ request('is_active') == '0' ? 'selected' : '' }}>Nonaktif</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary">Filter</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('javascripts')
<script>
$(document).ready(function() {
// Initialize DataTable
$('#kpiTargetsTable').DataTable({
"pageLength": 25,
"order": [[0, "asc"]],
"language": {
"url": "//cdn.datatables.net/plug-ins/1.10.24/i18n/Indonesian.json"
}
});
// Auto hide alerts after 5 seconds
setTimeout(function() {
$('.alert').fadeOut('slow');
}, 5000);
});
function toggleStatus(targetId) {
if (confirm('Yakin ingin mengubah status target ini?')) {
$.ajax({
url: '{{ route("kpi.targets.toggle-status", ":id") }}'.replace(':id', targetId),
type: 'POST',
data: {
_token: '{{ csrf_token() }}'
},
success: function(response) {
if (response.success) {
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: response.message,
timer: 2000,
showConfirmButton: false
}).then(function() {
location.reload();
});
} else {
Swal.fire({
icon: 'error',
title: 'Error',
text: response.message
});
}
},
error: function() {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Terjadi kesalahan saat mengubah status'
});
}
});
}
}
</script>
@endpush

View File

@@ -0,0 +1,193 @@
@extends('layouts.backapp')
@section('content')
<div class="kt-content kt-grid__item kt-grid__item--fluid kt-grid kt-grid--hor" id="kt_content">
<div class="kt-container kt-container--fluid kt-grid__item kt-grid__item--fluid">
<div class="kt-portlet kt-portlet--mobile">
<div class="kt-portlet__head kt-portlet__head--lg">
<div class="kt-portlet__head-label">
<h3 class="kt-portlet__head-title">
Detail Target KPI
</h3>
</div>
<div class="kt-portlet__head-toolbar">
<div class="kt-portlet__head-actions">
<a href="{{ route('kpi.targets.edit', $target->id) }}" class="btn btn-warning">
<i class="fas fa-edit"></i> Edit
</a>
<a href="{{ route('kpi.targets.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Kembali
</a>
</div>
</div>
</div>
<div class="kt-portlet__body">
<div class="row">
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td width="150"><strong>Mekanik</strong></td>
<td>: {{ $target->user->name }}</td>
</tr>
<tr>
<td><strong>Email</strong></td>
<td>: {{ $target->user->email }}</td>
</tr>
<tr>
<td><strong>Dealer</strong></td>
<td>: {{ $target->user->dealer->name ?? 'N/A' }}</td>
</tr>
<tr>
<td><strong>Target Nilai</strong></td>
<td>: {{ number_format($target->target_value) }} Pekerjaan</td>
</tr>
<tr>
<td><strong>Status</strong></td>
<td>:
@if($target->is_active)
<span class="badge badge-success">Aktif</span>
@else
<span class="badge badge-secondary">Nonaktif</span>
@endif
</td>
</tr>
</table>
</div>
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td width="150"><strong>Jenis Target</strong></td>
<td>: <span class="badge badge-info">Target Permanen</span></td>
</tr>
<tr>
<td><strong>Berlaku Sejak</strong></td>
<td>: {{ $target->created_at->format('d/m/Y') }}</td>
</tr>
<tr>
<td><strong>Dibuat</strong></td>
<td>: {{ $target->created_at->format('d/m/Y H:i') }}</td>
</tr>
<tr>
<td><strong>Terakhir Update</strong></td>
<td>: {{ $target->updated_at->format('d/m/Y H:i') }}</td>
</tr>
<tr>
<td><strong>Total Pencapaian</strong></td>
<td>: {{ $target->achievements->count() }} Bulan</td>
</tr>
</table>
</div>
</div>
@if($target->description)
<div class="row mt-3">
<div class="col-12">
<h6><strong>Deskripsi:</strong></h6>
<p class="text-muted">{{ $target->description }}</p>
</div>
</div>
@endif
<!-- Achievement History -->
<div class="row mt-4">
<div class="col-12">
<h5><i class="fas fa-chart-line"></i> Riwayat Pencapaian Bulanan</h5>
@if($target->achievements->count() > 0)
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>Periode</th>
<th>Target</th>
<th>Aktual</th>
<th>Pencapaian</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach($target->achievements->sortByDesc('year')->sortByDesc('month') as $achievement)
<tr>
<td>{{ $achievement->getPeriodDisplayName() }}</td>
<td>{{ number_format($achievement->target_value) }}</td>
<td>{{ number_format($achievement->actual_value) }}</td>
<td>{{ number_format($achievement->achievement_percentage, 1) }}%</td>
<td>
<span class="badge badge-{{ $achievement->status_color }}">
@switch($achievement->status)
@case('exceeded')
Melebihi Target
@break
@case('good')
Baik
@break
@case('fair')
Cukup
@break
@case('poor')
Kurang
@break
@default
Tidak Diketahui
@endswitch
</span>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="alert alert-info">
<i class="fas fa-info-circle"></i> Belum ada data pencapaian untuk target ini.
</div>
@endif
</div>
</div>
<!-- Summary Statistics -->
@if($target->achievements->count() > 0)
<div class="row mt-4">
<div class="col-12">
<h5><i class="fas fa-chart-bar"></i> Statistik Pencapaian</h5>
<div class="row">
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body text-center">
<h4>{{ $target->achievements->count() }}</h4>
<small>Total Pencapaian</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body text-center">
<h4>{{ $target->achievements->where('achievement_percentage', '>=', 100)->count() }}</h4>
<small>Target Tercapai</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body text-center">
<h4>{{ number_format($target->achievements->avg('achievement_percentage'), 1) }}%</h4>
<small>Rata-rata Pencapaian</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body text-center">
<h4>{{ number_format($target->achievements->max('achievement_percentage'), 1) }}%</h4>
<small>Pencapaian Tertinggi</small>
</div>
</div>
</div>
</div>
</div>
</div>
@endif
</div>
</div>
</div>
</div>
@endsection

View File

@@ -222,6 +222,22 @@
</a>
</li>
@endcan
<div class="kt-menu__section" style="padding: 15px 20px; margin-top: 10px; margin-bottom: 5px;">
<div style="display: flex; align-items: center; color: #a7abc3; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;">
<i class="fa fa-user-cog" style="margin-right: 8px; font-size: 14px;"></i>
<span>KPI</span>
</div>
</div>
@can('view', $menus['kpi.targets.index'])
<li class="kt-menu__item" aria-haspopup="true">
<a href="{{ route('kpi.targets.index') }}" class="kt-menu__link">
<i class="fa fa-user-cog" style="display: flex; align-items: center; margin-right: 10px;"></i>
<span class="kt-menu__link-text">Target</span>
</a>
</li>
@endcan
</ul>
</div>
</div>

View File

@@ -320,6 +320,8 @@ use Illuminate\Support\Facades\Auth;
border-color: #28a745;
box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
}
</style>
@endsection
@@ -359,6 +361,58 @@ use Illuminate\Support\Facades\Auth;
<b>Dealer {{ $mechanic->dealer_name }}</b><br><br>
<a href="#">Total {{ $count_transaction_dealers }} Pekerjaan terkirim pada dealer</a><br>
<a href="#">Anda telah posting {{ $count_transaction_users }} pekerjaan</a>
<!-- KPI Information -->
<div class="mt-3">
@if($kpiData['has_target'])
<div class="row">
<div class="col-6">
<small class="text-muted">Target {{ $kpiData['period'] }}</small><br>
<strong class="text-primary">{{ number_format($kpiData['target']) }} Pekerjaan</strong>
</div>
<div class="col-6">
<small class="text-muted">Pencapaian</small><br>
<strong class="text-{{ $kpiData['status_color'] }}">{{ number_format($kpiData['actual']) }} Pekerjaan</strong>
</div>
</div>
<div class="mt-2">
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Progress</small>
<small class="text-{{ $kpiData['status_color'] }} font-weight-bold">{{ $kpiData['percentage'] }}%</small>
</div>
<div class="progress" style="height: 8px;">
@php
$progressWidth = min(100, $kpiData['percentage']);
@endphp
<div class="progress-bar bg-{{ $kpiData['status_color'] }}"
role="progressbar"
style="width: {{ $progressWidth }}%"
aria-valuenow="{{ $kpiData['percentage'] }}"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<div class="mt-1">
@if($kpiData['status'] == 'exceeded')
<small class="text-success"><i class="fa fa-check-circle"></i> Target tercapai!</small>
@elseif($kpiData['status'] == 'good')
<small class="text-info"><i class="fa fa-arrow-up"></i> Performa baik</small>
@elseif($kpiData['status'] == 'fair')
<small class="text-warning"><i class="fa fa-exclamation-triangle"></i> Perlu peningkatan</small>
@elseif($kpiData['status'] == 'poor')
<small class="text-danger"><i class="fa fa-times-circle"></i> Perlu perbaikan</small>
@else
<small class="text-secondary"><i class="fa fa-clock"></i> Belum ada data</small>
@endif
</div>
</div>
@else
<div class="text-center py-2">
<i class="fa fa-info-circle text-muted"></i>
<small class="text-muted">Belum ada target KPI untuk {{ $kpiData['period'] }}</small>
</div>
@endif
</div>
</div>
<div class="col-4">
<div class="text-center mt-2">
@@ -1665,6 +1719,8 @@ use Illuminate\Support\Facades\Auth;
$(this).val('0.00');
}
});
});
// Handle when input loses focus - set default if empty