Poles Kalender Pelanggaran di dashboard premium
- Header: icon chip gradient + badge statistik 'X pelanggaran' bulan berjalan - Tombol 'Hari Ini' (kembali ke bulan sekarang) + navigasi prev/next di pill glass, hover efek - Sel tanggal: lebih tinggi, hari ini = gradient biru + ring + label 'Hari Ini', hover lift; tanggal luar bulan lebih redup - Badge jumlah: gradient + glow shadow senada (biru/oranye/merah), hover scale - Legend jadi pill gradient + teks '1/2/3+ pelanggaran', footer interaktif - JS: method currentMonth() + computed monthTotal
This commit is contained in:
@@ -130,54 +130,73 @@
|
|||||||
{{-- Calendar --}}
|
{{-- Calendar --}}
|
||||||
<div class="lg:col-span-2 bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden"
|
<div class="lg:col-span-2 bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden"
|
||||||
x-data="calendarApp()" x-init="init()">
|
x-data="calendarApp()" x-init="init()">
|
||||||
<div class="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
|
<div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between gap-3 flex-wrap">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="w-9 h-9 rounded-xl bg-gradient-to-br from-blue-500 to-blue-600 flex items-center justify-center shadow-sm">
|
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-blue-600 to-indigo-600 flex items-center justify-center text-white shadow-md shadow-blue-500/25">
|
||||||
<i class="fa-solid fa-calendar text-white text-sm"></i>
|
<i class="fa-solid fa-calendar text-sm"></i>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-sm font-semibold text-gray-900">Kalender Pelanggaran</h2>
|
<h2 class="text-sm font-bold text-slate-900">Kalender Pelanggaran</h2>
|
||||||
<p class="text-base font-bold text-gray-700" x-text="monthLabel + ' ' + year"></p>
|
<div class="flex items-center gap-2">
|
||||||
|
<p class="text-base font-extrabold text-slate-700 tracking-tight" x-text="monthLabel + ' ' + year"></p>
|
||||||
|
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-blue-50 border border-blue-100 text-[10px] font-bold text-blue-600">
|
||||||
|
<i class="fa-solid fa-triangle-exclamation text-[8px]"></i>
|
||||||
|
<span x-text="monthTotal + ' pelanggaran'"></span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1">
|
</div>
|
||||||
<button @click="prevMonth()" class="p-2 rounded-lg hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition">
|
<div class="flex items-center gap-1.5">
|
||||||
|
<button @click="currentMonth()"
|
||||||
|
class="px-3 py-2 rounded-xl text-xs font-bold text-blue-600 bg-blue-50 border border-blue-100 hover:bg-blue-100 transition shadow-sm">
|
||||||
|
Hari Ini
|
||||||
|
</button>
|
||||||
|
<div class="flex items-center gap-1 p-1 rounded-xl bg-slate-100">
|
||||||
|
<button @click="prevMonth()" class="p-2 rounded-lg hover:bg-white hover:shadow-sm text-slate-500 hover:text-blue-600 transition" title="Bulan sebelumnya">
|
||||||
<i class="fa-solid fa-chevron-left text-xs"></i>
|
<i class="fa-solid fa-chevron-left text-xs"></i>
|
||||||
</button>
|
</button>
|
||||||
<button @click="nextMonth()" class="p-2 rounded-lg hover:bg-gray-100 text-gray-400 hover:text-gray-600 transition">
|
<button @click="nextMonth()" class="p-2 rounded-lg hover:bg-white hover:shadow-sm text-slate-500 hover:text-blue-600 transition" title="Bulan berikutnya">
|
||||||
<i class="fa-solid fa-chevron-right text-xs"></i>
|
<i class="fa-solid fa-chevron-right text-xs"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="p-5">
|
<div class="p-5">
|
||||||
{{-- Day headers --}}
|
{{-- Day headers --}}
|
||||||
<div class="grid grid-cols-7 mb-2">
|
<div class="grid grid-cols-7 mb-2">
|
||||||
<template x-for="day in ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab']">
|
<template x-for="day in ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab']">
|
||||||
<div class="text-center text-xs font-semibold text-gray-400 uppercase tracking-wider py-2" x-text="day"></div>
|
<div class="text-center text-[10px] font-bold text-slate-400 uppercase tracking-[0.12em] py-1.5" x-text="day"></div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Calendar grid --}}
|
{{-- Calendar grid --}}
|
||||||
<div class="grid grid-cols-7 gap-1">
|
<div class="grid grid-cols-7 gap-1.5">
|
||||||
<template x-for="(day, idx) in days" :key="idx">
|
<template x-for="(day, idx) in days" :key="idx">
|
||||||
<div
|
<div
|
||||||
class="relative min-h-[70px] sm:min-h-[80px] rounded-xl border transition-all duration-150 p-1.5"
|
class="relative min-h-[76px] sm:min-h-[84px] rounded-xl border transition-all duration-150 p-2 group"
|
||||||
:class="day.isToday
|
:class="day.isToday
|
||||||
? 'border-blue-300 bg-blue-50/50 ring-1 ring-blue-200'
|
? 'border-blue-400 bg-gradient-to-b from-blue-50 to-white ring-2 ring-blue-200/70 shadow-sm'
|
||||||
: day.isCurrentMonth
|
: day.isCurrentMonth
|
||||||
? 'border-gray-100 hover:border-slate-200 hover:bg-gray-50'
|
? 'border-slate-100 hover:border-blue-200 hover:shadow-md hover:-translate-y-0.5 hover:bg-slate-50/50'
|
||||||
: 'border-gray-50 bg-gray-50/30 text-gray-300'">
|
: 'border-slate-50 bg-slate-50/40 text-slate-300'">
|
||||||
{{-- Date number --}}
|
{{-- Date number --}}
|
||||||
<div class="text-xs font-semibold"
|
<div class="text-xs font-bold"
|
||||||
:class="day.isToday ? 'text-blue-600' : (day.isCurrentMonth ? 'text-gray-700' : 'text-gray-300')"
|
:class="day.isToday ? 'text-blue-600' : (day.isCurrentMonth ? 'text-slate-700' : 'text-slate-300')"
|
||||||
x-text="day.day">
|
x-text="day.day">
|
||||||
</div>
|
</div>
|
||||||
|
{{-- Label hari ini --}}
|
||||||
|
<div x-show="day.isToday" class="mt-0.5">
|
||||||
|
<span class="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-md bg-blue-600 text-white text-[9px] font-bold shadow-sm shadow-blue-500/30">
|
||||||
|
<i class="fa-solid fa-location-dot text-[7px]"></i> Hari Ini
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{{-- Violation badge --}}
|
{{-- Violation badge --}}
|
||||||
<template x-if="day.count > 0 && day.isCurrentMonth">
|
<template x-if="day.count > 0 && day.isCurrentMonth">
|
||||||
<a :href="'{{ route('violations.index') }}?date_from=' + day.dateStr + '&date_to=' + day.dateStr"
|
<a :href="'{{ route('violations.index') }}?date_from=' + day.dateStr + '&date_to=' + day.dateStr"
|
||||||
class="absolute bottom-1.5 right-1.5 inline-flex items-center justify-center min-w-[26px] h-[26px] text-xs font-bold text-white rounded-full shadow-sm"
|
class="absolute bottom-1.5 right-1.5 inline-flex items-center justify-center min-w-[28px] h-[28px] text-xs font-extrabold text-white rounded-full shadow-lg transition-transform hover:scale-110"
|
||||||
:class="day.count >= 3 ? 'bg-red-500' : (day.count >= 2 ? 'bg-orange-400' : 'bg-blue-400')"
|
:class="day.count >= 3 ? 'bg-gradient-to-br from-red-500 to-rose-600 shadow-red-500/30' : (day.count >= 2 ? 'bg-gradient-to-br from-orange-400 to-orange-500 shadow-orange-400/30' : 'bg-gradient-to-br from-blue-400 to-blue-500 shadow-blue-400/30')"
|
||||||
|
:title="day.count + ' pelanggaran'"
|
||||||
x-text="day.count">
|
x-text="day.count">
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
@@ -187,17 +206,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Legend --}}
|
{{-- Legend --}}
|
||||||
<div class="px-6 py-3 border-t border-gray-100 bg-gray-50/50 flex items-center gap-4 text-[11px] text-gray-500">
|
<div class="px-6 py-3 border-t border-slate-100 bg-gradient-to-r from-slate-50/80 to-blue-50/40 flex items-center gap-4 text-[11px] text-slate-500 flex-wrap">
|
||||||
<span class="inline-flex items-center gap-1.5">
|
<span class="inline-flex items-center gap-1.5 font-semibold">
|
||||||
<span class="w-2.5 h-2.5 rounded bg-blue-400"></span> 1
|
<span class="w-2.5 h-2.5 rounded-full bg-gradient-to-br from-blue-400 to-blue-500 shadow-sm"></span> 1 pelanggaran
|
||||||
</span>
|
</span>
|
||||||
<span class="inline-flex items-center gap-1.5">
|
<span class="inline-flex items-center gap-1.5 font-semibold">
|
||||||
<span class="w-2.5 h-2.5 rounded bg-orange-400"></span> 2
|
<span class="w-2.5 h-2.5 rounded-full bg-gradient-to-br from-orange-400 to-orange-500 shadow-sm"></span> 2 pelanggaran
|
||||||
</span>
|
</span>
|
||||||
<span class="inline-flex items-center gap-1.5">
|
<span class="inline-flex items-center gap-1.5 font-semibold">
|
||||||
<span class="w-2.5 h-2.5 rounded bg-red-500"></span> 3+
|
<span class="w-2.5 h-2.5 rounded-full bg-gradient-to-br from-red-500 to-rose-600 shadow-sm"></span> 3+ pelanggaran
|
||||||
|
</span>
|
||||||
|
<span class="ml-auto inline-flex items-center gap-1 text-slate-400">
|
||||||
|
<i class="fa-solid fa-hand-pointer text-[10px]"></i> Klik badge untuk lihat detail
|
||||||
</span>
|
</span>
|
||||||
<span class="ml-auto text-gray-400">Klik badge untuk lihat detail</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -320,6 +341,10 @@
|
|||||||
return months[this.month - 1] || '';
|
return months[this.month - 1] || '';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get monthTotal() {
|
||||||
|
return Object.values(this.violations || {}).reduce((a, b) => a + b, 0);
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
@@ -405,6 +430,13 @@
|
|||||||
this.fetchAndRender();
|
this.fetchAndRender();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
currentMonth() {
|
||||||
|
const now = new Date();
|
||||||
|
this.year = now.getFullYear();
|
||||||
|
this.month = now.getMonth() + 1;
|
||||||
|
this.fetchAndRender();
|
||||||
|
},
|
||||||
|
|
||||||
fetchAndRender() {
|
fetchAndRender() {
|
||||||
fetch('{{ route('calendar.data') }}?year=' + this.year + '&month=' + this.month)
|
fetch('{{ route('calendar.data') }}?year=' + this.year + '&month=' + this.month)
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
|
|||||||
Reference in New Issue
Block a user