chore: stop tracking compiled blade cache

This commit is contained in:
2026-09-23 13:56:51 +07:00
parent 507fc6ccdf
commit edd2f626be
154 changed files with 1149 additions and 12046 deletions
+83 -159
View File
@@ -16,10 +16,10 @@
<div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
@foreach([
['Total kucing',$stats['total_cats'] ?? 0,'fa-cat','cats.index'],
['Total kucing aktif',$stats['total_cats'] ?? 0,'fa-cat','cats.index'],
['Dalam perawatan',$stats['sick_cats'] ?? 0,'fa-heart-pulse','cats.index'],
['Pembiakan aktif',$stats['total_mating'] ?? 0,'fa-venus-mars','matting.index'],
['Anakan tercatat',$stats['total_kittens'] ?? 0,'fa-paw','matting.index']
['Masa kehamilan',$stats['total_mating'] ?? 0,'fa-venus-mars','matting.index'],
['Total anakan',$stats['total_kittens'] ?? 0,'fa-paw','matting.index']
] as $i=>$card)
<a href="{{ $i===1 ? route($card[3],['filter_health'=>'treatment']) : route($card[3]) }}" class="border border-slate-200 bg-white p-5 hover:border-slate-300">
<div class="flex items-start justify-between">
@@ -301,6 +301,24 @@
</div>
</div>
</div>
<div class="border-t border-slate-100 px-5 pb-5 pt-5">
<div class="mb-4 flex flex-col gap-1 sm:flex-row sm:items-end sm:justify-between">
<div><h3 class="text-base font-bold text-slate-900">Perubahan berat yang perlu diperhatikan</h3><p class="mt-0.5 text-xs text-slate-500">Kenaikan dan penurunan berdasarkan dua pencatatan terakhir dalam periode {{ $weightPeriodLabel ?? '-' }}.</p></div>
</div>
<div class="grid gap-4 xl:grid-cols-3">
@foreach(['Kitten','Junior','Dewasa'] as $category)
@php $movement = data_get($weightMovements ?? [], $category, ['gainers'=>collect(),'losers'=>collect()]); @endphp
<div class="rounded-2xl border border-slate-200 bg-slate-50/40 p-4">
<h4 class="mb-3 font-bold text-slate-800">{{ $category }}</h4>
<div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-1 2xl:grid-cols-2">
<div class="rounded-xl border border-emerald-100 bg-emerald-50/70 p-3"><p class="mb-2 text-xs font-bold text-emerald-700"><i class="fas fa-arrow-trend-up mr-1"></i>Kenaikan</p><div class="space-y-2">@forelse($movement['gainers'] as $row)<div class="flex items-center justify-between gap-3 text-xs"><a href="{{ route('cats.show',$row['cat_id']) }}" class="truncate font-semibold text-slate-700 hover:text-indigo-600">{{ $row['name'] }}</a><span class="shrink-0 font-bold text-emerald-700">+{{ number_format($row['change'],2,',','.') }} kg</span></div>@empty<p class="text-xs text-slate-400">Tidak ada kenaikan tercatat.</p>@endforelse</div></div>
<div class="rounded-xl border border-rose-100 bg-rose-50/70 p-3"><p class="mb-2 text-xs font-bold text-rose-700"><i class="fas fa-arrow-trend-down mr-1"></i>Penurunan</p><div class="space-y-2">@forelse($movement['losers'] as $row)<div class="flex items-center justify-between gap-3 text-xs"><a href="{{ route('cats.show',$row['cat_id']) }}" class="truncate font-semibold text-slate-700 hover:text-indigo-600">{{ $row['name'] }}</a><span class="shrink-0 font-bold text-rose-700">{{ number_format($row['change'],2,',','.') }} kg</span></div>@empty<p class="text-xs text-slate-400">Tidak ada penurunan tercatat.</p>@endforelse</div></div>
</div>
</div>
@endforeach
</div>
</div>
</section>
<div class="mt-6 grid gap-6 xl:grid-cols-3">
@@ -378,171 +396,77 @@
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const formatKg = (value) => `${Number(value).toFixed(2).replace('.', ',')} kg`;
const formatMonth = (month) => {
const d = new Date(`${month}-01T00:00:00`);
return Number.isNaN(d.getTime()) ? month : d.toLocaleDateString('id-ID', { month: 'short', year: '2-digit' });
};
document.addEventListener('DOMContentLoaded', function () {
const formatKg = (value) => `${Number(value).toFixed(2).replace('.', ',')} kg`;
const formatMonth = (month) => {
const d = new Date(`${month}-01T00:00:00`);
return Number.isNaN(d.getTime()) ? month : d.toLocaleDateString('id-ID', { month: 'short', year: '2-digit' });
};
if (typeof Chart !== 'undefined') {
Chart.defaults.font.family = "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
Chart.defaults.color = '#64748b';
function renderLineChart(canvas, labels, datasets) {
if (!canvas || !labels.length || !datasets.length) return;
const ctx = canvas.getContext('2d');
const parent = canvas.parentElement;
const cssWidth = Math.max(320, parent.clientWidth);
const cssHeight = Math.max(250, parent.clientHeight);
const dpr = window.devicePixelRatio || 1;
canvas.width = cssWidth * dpr; canvas.height = cssHeight * dpr;
canvas.style.width = cssWidth + 'px'; canvas.style.height = cssHeight + 'px';
ctx.setTransform(dpr,0,0,dpr,0,0);
ctx.clearRect(0,0,cssWidth,cssHeight);
const categoryColors = {
Kitten: '#2563eb',
Junior: '#db2777',
Dewasa: '#059669'
};
const catPalette = ['#d97706', '#7c3aed', '#0891b2', '#2563eb', '#059669', '#be123c'];
const pad = {left:48,right:16,top:12,bottom:58};
const w = cssWidth-pad.left-pad.right, h = cssHeight-pad.top-pad.bottom;
const values = datasets.flatMap(d => d.data.filter(v => v !== null && v !== undefined).map(Number));
if (!values.length) return;
const maxY = Math.max(1, Math.ceil(Math.max(...values)*1.15*2)/2);
const minY = 0;
const xAt = i => pad.left + (labels.length<=1 ? w/2 : (i/(labels.length-1))*w);
const yAt = v => pad.top + h - ((Number(v)-minY)/(maxY-minY))*h;
const avgData = @json($categoryAverageTrend ?? ['labels'=>[], 'series'=>[]]);
const avgCanvas = document.getElementById('weightCategoryAverageChart');
if (avgCanvas && avgData.labels && avgData.labels.length) {
new Chart(avgCanvas, {
type: 'line',
data: {
labels: avgData.labels.map(formatMonth),
datasets: (avgData.series || []).map((series) => ({
label: series.name,
data: series.data,
borderColor: categoryColors[series.name] || '#64748b',
backgroundColor: categoryColors[series.name] || '#64748b',
borderWidth: 2.5,
pointRadius: 3,
pointHoverRadius: 5,
pointBorderWidth: 0,
tension: 0.28,
spanGaps: true
}))
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
plugins: {
legend: {
position: 'bottom',
labels: { usePointStyle: true, boxWidth: 8, boxHeight: 8, padding: 18, font: { size: 11, weight: '600' } }
},
tooltip: {
backgroundColor: '#0f172a',
padding: 10,
cornerRadius: 10,
callbacks: { label: (ctx) => `${ctx.dataset.label}: ${formatKg(ctx.parsed.y)}` }
}
},
scales: {
x: { grid: { display: false }, border: { display: false }, ticks: { maxRotation: 0, autoSkip: true, maxTicksLimit: 8 } },
y: {
beginAtZero: true,
grid: { color: '#e2e8f0' },
border: { display: false },
title: { display: true, text: 'Berat (kg)', color: '#64748b', font: { size: 11, weight: '600' } }
}
}
}
});
}
const trendLabels = @json($weightTrendLabels ?? []);
const trendSeries = @json($weightTrendSeries ?? []);
const trendCanvas = document.getElementById('weightSelectedCategoryChart');
if (trendCanvas && trendLabels.length && trendSeries.length) {
new Chart(trendCanvas, {
type: 'line',
data: {
labels: trendLabels.map(formatMonth),
datasets: trendSeries.map((series, index) => ({
label: series.name,
data: series.data,
borderColor: catPalette[index % catPalette.length],
backgroundColor: catPalette[index % catPalette.length],
borderWidth: 2.2,
pointRadius: 3,
pointHoverRadius: 5,
pointBorderWidth: 0,
tension: 0.28,
spanGaps: true
}))
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'nearest', intersect: false },
plugins: {
legend: {
position: 'bottom',
labels: { usePointStyle: true, boxWidth: 8, boxHeight: 8, padding: 15, font: { size: 10, weight: '600' } }
},
tooltip: {
backgroundColor: '#0f172a',
padding: 10,
cornerRadius: 10,
callbacks: { label: (ctx) => `${ctx.dataset.label}: ${formatKg(ctx.parsed.y)}` }
}
},
scales: {
x: { grid: { display: false }, border: { display: false }, ticks: { maxRotation: 0, autoSkip: true, maxTicksLimit: 8 } },
y: {
beginAtZero: true,
grid: { color: '#e2e8f0' },
border: { display: false },
title: { display: true, text: 'Berat (kg)', color: '#64748b', font: { size: 11, weight: '600' } }
}
}
}
});
}
ctx.font='11px system-ui, -apple-system, Segoe UI, sans-serif'; ctx.textBaseline='middle';
for(let i=0;i<=4;i++){
const v=maxY*(i/4), y=yAt(v);
ctx.strokeStyle='#e2e8f0'; ctx.lineWidth=1; ctx.beginPath(); ctx.moveTo(pad.left,y); ctx.lineTo(pad.left+w,y); ctx.stroke();
ctx.fillStyle='#64748b'; ctx.textAlign='right'; ctx.fillText(v.toFixed(v<1?1:1),pad.left-8,y);
}
const step=Math.max(1,Math.ceil(labels.length/6));
labels.forEach((lab,i)=>{ if(i%step!==0 && i!==labels.length-1) return; ctx.fillStyle='#64748b'; ctx.textAlign='center'; ctx.fillText(formatMonth(lab),xAt(i),pad.top+h+20); });
// Pencarian + pagination tabel berat terbaru (client-side, tanpa request tambahan).
const searchInput = document.getElementById('weightTableSearch');
const rowsPerPageSelect = document.getElementById('weightRowsPerPage');
const tableRows = Array.from(document.querySelectorAll('[data-weight-row]'));
const prevButton = document.getElementById('weightPrevPage');
const nextButton = document.getElementById('weightNextPage');
const pageLabel = document.getElementById('weightPageLabel');
const tableInfo = document.getElementById('weightTableInfo');
let currentPage = 1;
datasets.forEach(ds=>{
ctx.strokeStyle=ds.color; ctx.fillStyle=ds.color; ctx.lineWidth=2.4; ctx.lineJoin='round'; ctx.lineCap='round';
ctx.beginPath(); let started=false;
ds.data.forEach((v,i)=>{ if(v===null||v===undefined){started=false;return;} const x=xAt(i),y=yAt(v); if(!started){ctx.moveTo(x,y);started=true;} else ctx.lineTo(x,y); });
ctx.stroke();
ds.data.forEach((v,i)=>{ if(v===null||v===undefined)return; const x=xAt(i),y=yAt(v); ctx.beginPath();ctx.arc(x,y,3,0,Math.PI*2);ctx.fill(); });
});
const renderWeightTable = () => {
if (!tableRows.length) {
if (tableInfo) tableInfo.textContent = '0 data';
if (pageLabel) pageLabel.textContent = '';
if (prevButton) prevButton.disabled = true;
if (nextButton) nextButton.disabled = true;
return;
}
let lx=pad.left, ly=cssHeight-16;
datasets.forEach(ds=>{ const text=ds.label; const tw=ctx.measureText(text).width+28; if(lx+tw>cssWidth-pad.right){ly-=18;lx=pad.left;} ctx.fillStyle=ds.color;ctx.beginPath();ctx.arc(lx+4,ly,4,0,Math.PI*2);ctx.fill();ctx.fillStyle='#475569';ctx.textAlign='left';ctx.fillText(text,lx+14,ly);lx+=tw; });
}
const search = (searchInput?.value || '').trim().toLowerCase();
const pageSize = Number(rowsPerPageSelect?.value || 10);
const filtered = tableRows.filter((row) => (row.dataset.name || '').includes(search));
const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize));
currentPage = Math.min(currentPage, totalPages);
const start = (currentPage - 1) * pageSize;
const visible = new Set(filtered.slice(start, start + pageSize));
const categoryColors={Kitten:'#2563eb',Junior:'#db2777',Dewasa:'#059669'};
const catPalette=['#d97706','#7c3aed','#0891b2','#2563eb','#059669','#be123c'];
const avgData=@json($categoryAverageTrend ?? ['labels'=>[], 'series'=>[]]);
const trendLabels=@json($weightTrendLabels ?? []);
const trendSeries=@json($weightTrendSeries ?? []);
const avgCanvas=document.getElementById('weightCategoryAverageChart');
const trendCanvas=document.getElementById('weightSelectedCategoryChart');
const draw=()=>{
renderLineChart(avgCanvas, avgData.labels||[], (avgData.series||[]).map(s=>({label:s.name,data:s.data,color:categoryColors[s.name]||'#64748b'})));
renderLineChart(trendCanvas, trendLabels||[], (trendSeries||[]).map((s,i)=>({label:s.name,data:s.data,color:catPalette[i%catPalette.length]})));
};
draw(); let resizeTimer; window.addEventListener('resize',()=>{clearTimeout(resizeTimer);resizeTimer=setTimeout(draw,120);});
tableRows.forEach((row) => row.classList.toggle('hidden', !visible.has(row)));
if (tableInfo) {
const from = filtered.length ? start + 1 : 0;
const to = Math.min(start + pageSize, filtered.length);
tableInfo.textContent = `Menampilkan ${from}-${to} dari ${filtered.length} data`;
}
if (pageLabel) pageLabel.textContent = `Hal. ${currentPage} / ${totalPages}`;
if (prevButton) prevButton.disabled = currentPage <= 1;
if (nextButton) nextButton.disabled = currentPage >= totalPages;
};
searchInput?.addEventListener('input', () => { currentPage = 1; renderWeightTable(); });
rowsPerPageSelect?.addEventListener('change', () => { currentPage = 1; renderWeightTable(); });
prevButton?.addEventListener('click', () => { if (currentPage > 1) { currentPage--; renderWeightTable(); } });
nextButton?.addEventListener('click', () => { currentPage++; renderWeightTable(); });
renderWeightTable();
});
const searchInput=document.getElementById('weightTableSearch');
const rowsPerPageSelect=document.getElementById('weightRowsPerPage');
const tableRows=Array.from(document.querySelectorAll('[data-weight-row]'));
const prevButton=document.getElementById('weightPrevPage'); const nextButton=document.getElementById('weightNextPage');
const pageLabel=document.getElementById('weightPageLabel'); const tableInfo=document.getElementById('weightTableInfo'); let currentPage=1;
const renderTable=()=>{ const search=(searchInput?.value||'').trim().toLowerCase(); const size=Number(rowsPerPageSelect?.value||10); const filtered=tableRows.filter(r=>(r.dataset.name||'').includes(search)); const pages=Math.max(1,Math.ceil(filtered.length/size)); currentPage=Math.min(currentPage,pages); const start=(currentPage-1)*size; const visible=new Set(filtered.slice(start,start+size)); tableRows.forEach(r=>r.classList.toggle('hidden',!visible.has(r))); if(tableInfo)tableInfo.textContent=`Menampilkan ${filtered.length?start+1:0}-${Math.min(start+size,filtered.length)} dari ${filtered.length} data`; if(pageLabel)pageLabel.textContent=`Hal. ${currentPage} / ${pages}`; if(prevButton)prevButton.disabled=currentPage<=1; if(nextButton)nextButton.disabled=currentPage>=pages; };
searchInput?.addEventListener('input',()=>{currentPage=1;renderTable();}); rowsPerPageSelect?.addEventListener('change',()=>{currentPage=1;renderTable();}); prevButton?.addEventListener('click',()=>{if(currentPage>1){currentPage--;renderTable();}}); nextButton?.addEventListener('click',()=>{currentPage++;renderTable();}); renderTable();
});
</script>
@endpush
</x-app-layout>
@@ -96,7 +96,6 @@
</div>
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const ctx = document.getElementById('medicalTrendChart');