913 lines
34 KiB
PHP
913 lines
34 KiB
PHP
@extends('admin.layouts.app')
|
||
@section('content')
|
||
|
||
<style>
|
||
/* ══════════════════════════════════════════════
|
||
DISNAKER KALBAR – ADMIN DASHBOARD
|
||
Professional Government Dashboard UI
|
||
══════════════════════════════════════════════ */
|
||
|
||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
||
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
|
||
.d-wrap {
|
||
padding: 2rem 2.5rem 3rem;
|
||
max-width: 1440px;
|
||
margin: 0 auto;
|
||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||
}
|
||
|
||
/* ── Page Header ─────────────────────────────── */
|
||
.page-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-end;
|
||
margin-bottom: 1.75rem;
|
||
}
|
||
.page-header-left h2 {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: #0D215C;
|
||
letter-spacing: -0.3px;
|
||
line-height: 1;
|
||
}
|
||
.page-header-left .subtitle {
|
||
font-size: 12.5px;
|
||
color: #94a3b8;
|
||
font-weight: 500;
|
||
margin-top: 5px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.status-dot {
|
||
width: 7px; height: 7px;
|
||
border-radius: 50%;
|
||
background: #22c55e;
|
||
display: inline-block;
|
||
box-shadow: 0 0 0 3px rgba(34,197,94,0.2);
|
||
animation: pulse-dot 2s infinite;
|
||
}
|
||
@keyframes pulse-dot {
|
||
0%,100% { box-shadow: 0 0 0 3px rgba(34,197,94,0.2); }
|
||
50% { box-shadow: 0 0 0 6px rgba(34,197,94,0.08); }
|
||
}
|
||
.page-header-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.hdr-chip {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 7px 18px;
|
||
border-radius: 50px;
|
||
letter-spacing: 0.4px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.hdr-chip.navy { background: #0D215C; color: #fff; box-shadow: 0 4px 14px rgba(13,33,92,0.22); }
|
||
.hdr-chip.light { background: #fff; color: #64748b; border: 1px solid #e2e8f0; }
|
||
.hdr-chip svg { flex-shrink: 0; }
|
||
|
||
/* ── Stat Cards ──────────────────────────────── */
|
||
.stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 18px;
|
||
margin-bottom: 1.75rem;
|
||
}
|
||
.s-card {
|
||
background: #fff;
|
||
border-radius: 20px;
|
||
padding: 1.5rem 1.6rem 1.4rem;
|
||
border: 1px solid #edf1f7;
|
||
position: relative;
|
||
overflow: hidden;
|
||
cursor: default;
|
||
transition: transform 0.22s cubic-bezier(.4,0,.2,1),
|
||
box-shadow 0.22s cubic-bezier(.4,0,.2,1);
|
||
}
|
||
.s-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 16px 40px rgba(13,33,92,0.12);
|
||
}
|
||
.s-card::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0; left: 0; right: 0;
|
||
height: 4px;
|
||
border-radius: 0 0 20px 20px;
|
||
opacity: 0.9;
|
||
}
|
||
.s-card.c-blue::after { background: linear-gradient(90deg,#3b82f6,#6366f1); }
|
||
.s-card.c-green::after { background: linear-gradient(90deg,#22c55e,#10b981); }
|
||
.s-card.c-yellow::after { background: linear-gradient(90deg,#f59e0b,#f97316); }
|
||
.s-card.c-red::after { background: linear-gradient(90deg,#64748b,#475569); }
|
||
.s-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -20px; right: -20px;
|
||
width: 100px; height: 100px;
|
||
border-radius: 50%;
|
||
opacity: 0.04;
|
||
}
|
||
.s-card.c-blue::before { background: #3b82f6; }
|
||
.s-card.c-green::before { background: #22c55e; }
|
||
.s-card.c-yellow::before { background: #f59e0b; }
|
||
.s-card.c-red::before { background: #64748b; }
|
||
|
||
.s-icon {
|
||
width: 44px; height: 44px;
|
||
border-radius: 12px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
.s-icon.c-blue { background: #eff6ff; color: #3b82f6; }
|
||
.s-icon.c-green { background: #f0fdf4; color: #16a34a; }
|
||
.s-icon.c-yellow { background: #fffbeb; color: #d97706; }
|
||
.s-icon.c-red { background: #f8fafc; color: #475569; }
|
||
.s-icon svg { width: 20px; height: 20px; }
|
||
|
||
.s-num {
|
||
font-size: 36px;
|
||
font-weight: 700;
|
||
color: #0f172a;
|
||
letter-spacing: -2px;
|
||
line-height: 1;
|
||
}
|
||
.s-label {
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
color: #94a3b8;
|
||
margin-top: 5px;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
.s-trend {
|
||
font-size: 11.5px;
|
||
font-weight: 500;
|
||
margin-top: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
color: #94a3b8;
|
||
}
|
||
.s-trend.up { color: #16a34a; }
|
||
.s-trend.down { color: #94a3b8; }
|
||
.s-trend.flat { color: #94a3b8; }
|
||
.s-trend svg { width: 13px; height: 13px; }
|
||
|
||
/* ── Main Content Grid ───────────────────────── */
|
||
.content-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 360px;
|
||
gap: 20px;
|
||
align-items: start;
|
||
}
|
||
|
||
/* ── Generic Card ────────────────────────────── */
|
||
.g-card {
|
||
background: #fff;
|
||
border-radius: 20px;
|
||
padding: 1.5rem 1.6rem;
|
||
border: 1px solid #edf1f7;
|
||
box-shadow: 0 2px 12px rgba(13,33,92,0.04);
|
||
}
|
||
.g-card-title {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 1.2rem;
|
||
}
|
||
.g-card-title h3 {
|
||
font-size: 14px;
|
||
font-weight: 700;
|
||
color: #0D215C;
|
||
letter-spacing: -0.1px;
|
||
}
|
||
.g-card-title a {
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
color: #3b82f6;
|
||
text-decoration: none;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
transition: gap 0.18s;
|
||
}
|
||
.g-card-title a:hover { gap: 8px; }
|
||
|
||
/* ── Divider ─────────────────────────────────── */
|
||
.act-divider {
|
||
height: 1px;
|
||
background: #f1f5f9;
|
||
margin: 0 0 12px 0;
|
||
}
|
||
|
||
/* ── Filter Tabs ─────────────────────────────── */
|
||
.ftabs {
|
||
display: flex;
|
||
gap: 6px;
|
||
margin-bottom: 1rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
.ftab {
|
||
padding: 5px 15px;
|
||
border-radius: 50px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
border: 1.5px solid transparent;
|
||
transition: all 0.15s;
|
||
user-select: none;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
.ftab.on { background: #0D215C; color: #fff; border-color: #0D215C; }
|
||
.ftab.off { background: #f8fafc; color: #94a3b8; border-color: #e2e8f0; }
|
||
.ftab.off:hover { background: #eff6ff; color: #3b82f6; border-color: #bfdbfe; }
|
||
|
||
/* ── Activity Rows ───────────────────────────── */
|
||
.act-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 13px;
|
||
padding: 10px 10px;
|
||
border-radius: 12px;
|
||
cursor: pointer;
|
||
transition: background 0.15s, box-shadow 0.15s, transform 0.15s;
|
||
}
|
||
.act-item:hover {
|
||
background: #f8fafc;
|
||
box-shadow: 0 4px 16px rgba(13,33,92,0.06);
|
||
transform: translateX(2px);
|
||
}
|
||
.act-item:active { transform: scale(0.99); }
|
||
|
||
.act-ava {
|
||
width: 38px; height: 38px;
|
||
border-radius: 10px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
flex-shrink: 0;
|
||
}
|
||
.av-1 { background: linear-gradient(135deg,#3b82f6,#6366f1); }
|
||
.av-2 { background: linear-gradient(135deg,#14b8a6,#06b6d4); }
|
||
.av-3 { background: linear-gradient(135deg,#a855f7,#ec4899); }
|
||
.av-4 { background: linear-gradient(135deg,#f97316,#f59e0b); }
|
||
.av-5 { background: linear-gradient(135deg,#22c55e,#10b981); }
|
||
|
||
.act-body { flex: 1; min-width: 0; }
|
||
.act-name {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #1e293b;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.act-meta {
|
||
font-size: 11px;
|
||
color: #94a3b8;
|
||
font-weight: 400;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.pill {
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
padding: 4px 12px;
|
||
border-radius: 50px;
|
||
letter-spacing: 0.3px;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
}
|
||
.pill-ok { background: #dcfce7; color: #15803d; }
|
||
.pill-run { background: #dbeafe; color: #1d4ed8; }
|
||
.pill-del { background: #fee2e2; color: #b91c1c; }
|
||
.pill-pnd { background: #fef9c3; color: #92400e; }
|
||
|
||
.empty-box {
|
||
padding: 3.5rem 1rem;
|
||
text-align: center;
|
||
color: #cbd5e1;
|
||
}
|
||
.empty-box .ei {
|
||
width: 48px; height: 48px;
|
||
margin: 0 auto 12px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: #f1f5f9;
|
||
border-radius: 14px;
|
||
color: #94a3b8;
|
||
}
|
||
.empty-box .ei svg { width: 22px; height: 22px; }
|
||
.empty-box p { font-size: 13px; font-weight: 500; color: #94a3b8; }
|
||
|
||
/* ── Modal ────────────────────────────────────── */
|
||
.modal-overlay {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(13,33,92,0.28);
|
||
backdrop-filter: blur(4px);
|
||
z-index: 999;
|
||
align-items: center;
|
||
justify-content: center;
|
||
animation: fadeIn 0.2s ease;
|
||
}
|
||
.modal-overlay.show { display: flex; }
|
||
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||
.modal-box {
|
||
background: #fff;
|
||
border-radius: 22px;
|
||
padding: 2rem;
|
||
width: 460px;
|
||
max-width: 95vw;
|
||
box-shadow: 0 30px 80px rgba(13,33,92,0.18);
|
||
animation: slideUp 0.25s cubic-bezier(.4,0,.2,1);
|
||
}
|
||
@keyframes slideUp {
|
||
from { opacity:0; transform:translateY(20px); }
|
||
to { opacity:1; transform:translateY(0); }
|
||
}
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 1.2rem;
|
||
}
|
||
.modal-header h4 {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #0D215C;
|
||
line-height: 1.4;
|
||
max-width: 85%;
|
||
}
|
||
.modal-close {
|
||
width: 30px; height: 30px;
|
||
border-radius: 50%;
|
||
background: #f1f5f9;
|
||
border: none;
|
||
cursor: pointer;
|
||
display: flex; align-items: center; justify-content: center;
|
||
color: #64748b;
|
||
transition: all 0.15s;
|
||
}
|
||
.modal-close:hover { background: #fee2e2; color: #ef4444; }
|
||
.modal-close svg { width: 14px; height: 14px; }
|
||
.modal-field {
|
||
display: flex;
|
||
gap: 10px;
|
||
padding: 10px 0;
|
||
border-bottom: 1px solid #f1f5f9;
|
||
font-size: 13px;
|
||
}
|
||
.modal-field:last-child { border-bottom: none; }
|
||
.modal-field .mf-label {
|
||
width: 120px;
|
||
flex-shrink: 0;
|
||
color: #94a3b8;
|
||
font-weight: 500;
|
||
font-size: 11.5px;
|
||
letter-spacing: 0.3px;
|
||
padding-top: 1px;
|
||
}
|
||
.modal-field .mf-value {
|
||
color: #1e293b;
|
||
font-weight: 500;
|
||
font-size: 13px;
|
||
}
|
||
|
||
/* ── Sidebar ─────────────────────────────────── */
|
||
.sidebar-col { display: flex; flex-direction: column; gap: 18px; }
|
||
|
||
/* Bar Chart */
|
||
.chart-bars {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
gap: 8px;
|
||
height: 88px;
|
||
margin: 4px 0 8px;
|
||
}
|
||
.bar-wrap {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
height: 100%;
|
||
gap: 6px;
|
||
cursor: pointer;
|
||
}
|
||
.bar-body {
|
||
width: 100%;
|
||
border-radius: 6px 6px 0 0;
|
||
background: #e0e7ff;
|
||
margin-top: auto;
|
||
transition: all 0.3s cubic-bezier(.4,0,.2,1);
|
||
position: relative;
|
||
}
|
||
.bar-wrap:hover .bar-body {
|
||
filter: brightness(0.9);
|
||
transform: scaleY(1.04);
|
||
transform-origin: bottom;
|
||
}
|
||
.bar-body::after {
|
||
content: attr(data-val);
|
||
position: absolute;
|
||
top: -28px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: #0D215C;
|
||
color: #fff;
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
padding: 3px 8px;
|
||
border-radius: 6px;
|
||
white-space: nowrap;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.bar-wrap:hover .bar-body::after { opacity: 1; }
|
||
.bar-body.today { background: linear-gradient(180deg,#3b82f6 0%,#0D215C 100%); }
|
||
.bar-body.active { background: #93c5fd; }
|
||
.bar-lbl {
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
color: #94a3b8;
|
||
text-align: center;
|
||
}
|
||
.bar-lbl.today-lbl { color: #3b82f6; font-weight: 600; }
|
||
.chart-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-top: 10px;
|
||
border-top: 1px solid #f1f5f9;
|
||
font-size: 12px;
|
||
color: #64748b;
|
||
font-weight: 400;
|
||
}
|
||
.chart-footer span { color: #94a3b8; }
|
||
.chart-footer strong { color: #0D215C; font-weight: 700; }
|
||
|
||
/* Progress */
|
||
.prog-item { margin-bottom: 14px; }
|
||
.prog-item:last-child { margin-bottom: 0; }
|
||
.prog-head { display: flex; justify-content: space-between; margin-bottom: 7px; }
|
||
.prog-head span { font-size: 12.5px; font-weight: 500; color: #334155; }
|
||
.prog-head strong { font-size: 12.5px; font-weight: 700; color: #0D215C; }
|
||
.prog-track { height: 8px; background: #f1f5f9; border-radius: 50px; overflow: hidden; }
|
||
.prog-fill { height: 100%; border-radius: 50px; transition: width 1.2s cubic-bezier(.4,0,.2,1); }
|
||
|
||
/* Top Users */
|
||
.usr-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 11px;
|
||
padding: 8px 10px;
|
||
border-radius: 12px;
|
||
transition: background 0.15s, box-shadow 0.15s;
|
||
cursor: default;
|
||
}
|
||
.usr-row:hover {
|
||
background: #f8fafc;
|
||
box-shadow: 0 2px 10px rgba(13,33,92,0.05);
|
||
}
|
||
.usr-rank {
|
||
width: 24px; height: 24px;
|
||
border-radius: 7px;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
display: flex; align-items: center; justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.rank-1 { background: #fef9c3; color: #b45309; }
|
||
.rank-2 { background: #f1f5f9; color: #64748b; }
|
||
.rank-3 { background: #fff7ed; color: #c2410c; }
|
||
.rank-n { background: #f8fafc; color: #94a3b8; }
|
||
.usr-ava {
|
||
width: 32px; height: 32px;
|
||
border-radius: 9px;
|
||
background: linear-gradient(135deg,#0D215C,#2563eb);
|
||
color: #fff;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
display: flex; align-items: center; justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
.usr-name { flex: 1; font-size: 12.5px; font-weight: 500; color: #334155; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.usr-count {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: #3b82f6;
|
||
background: #eff6ff;
|
||
padding: 3px 11px;
|
||
border-radius: 50px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ── Animations ──────────────────────────────── */
|
||
@keyframes fadeUp {
|
||
from { opacity:0; transform:translateY(16px); }
|
||
to { opacity:1; transform:translateY(0); }
|
||
}
|
||
.s-card { animation: fadeUp 0.4s ease both; }
|
||
.s-card:nth-child(1) { animation-delay:.06s; }
|
||
.s-card:nth-child(2) { animation-delay:.12s; }
|
||
.s-card:nth-child(3) { animation-delay:.18s; }
|
||
.s-card:nth-child(4) { animation-delay:.24s; }
|
||
.content-grid { animation: fadeUp 0.4s .28s ease both; }
|
||
|
||
/* ── Responsive ──────────────────────────────── */
|
||
@media (max-width:1100px) { .content-grid { grid-template-columns:1fr; } }
|
||
@media (max-width:768px) {
|
||
.stats-grid { grid-template-columns:repeat(2,1fr); gap: 12px; }
|
||
.d-wrap { padding: 1rem 0.5rem; }
|
||
.page-header { flex-direction:column; align-items:flex-start; gap:12px; }
|
||
.page-header-right { flex-wrap: wrap; gap: 8px; }
|
||
.g-card { padding: 1.2rem; }
|
||
.act-item { gap: 10px; padding: 8px; }
|
||
.hdr-chip { padding: 6px 12px; }
|
||
}
|
||
@media (max-width:480px) {
|
||
.stats-grid { grid-template-columns:1fr; }
|
||
.d-wrap { padding: 0.8rem 0; }
|
||
.g-card { padding: 1rem; border-radius: 16px; }
|
||
.act-meta { font-size: 10px; }
|
||
.s-card { padding: 1.2rem; }
|
||
.act-item { flex-direction: column; align-items: flex-start; gap: 8px; }
|
||
.act-item .pill { align-self: flex-start; }
|
||
}
|
||
</style>
|
||
|
||
{{-- ═══════ MODAL (Detail Kegiatan) ═══════ --}}
|
||
<div class="modal-overlay" id="detailModal">
|
||
<div class="modal-box">
|
||
<div class="modal-header">
|
||
<h4 id="mdl-title">—</h4>
|
||
<button class="modal-close" onclick="closeModal()">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||
</button>
|
||
</div>
|
||
<div id="mdl-body"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="d-wrap">
|
||
|
||
{{-- ── PAGE HEADER ── --}}
|
||
<div class="page-header">
|
||
<div class="page-header-left">
|
||
<h2>Dashboard Admin</h2>
|
||
<div class="subtitle">
|
||
<span class="status-dot"></span>
|
||
{{ \Carbon\Carbon::now()->timezone('Asia/Jakarta')->locale('id')->translatedFormat('l, d F Y') }}
|
||
· Sistem Online
|
||
</div>
|
||
</div>
|
||
<div class="page-header-right">
|
||
<span class="hdr-chip light">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||
{{ \Carbon\Carbon::now()->timezone('Asia/Jakarta')->format('H:i') }} WIB
|
||
</span>
|
||
<span class="hdr-chip navy">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
|
||
Disnaker Kalbar
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- ── STAT CARDS ── --}}
|
||
<div class="stats-grid">
|
||
|
||
<div class="s-card c-blue">
|
||
<div class="s-icon c-blue">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/>
|
||
<rect x="9" y="3" width="6" height="4" rx="1"/>
|
||
<line x1="9" y1="12" x2="15" y2="12"/>
|
||
<line x1="9" y1="16" x2="13" y2="16"/>
|
||
</svg>
|
||
</div>
|
||
<div class="s-num">{{ $stats['total'] ?? 0 }}</div>
|
||
<div class="s-label">Total Kegiatan</div>
|
||
<div class="s-trend up">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg>
|
||
12% bulan ini
|
||
</div>
|
||
</div>
|
||
|
||
<div class="s-card c-green">
|
||
<div class="s-icon c-green">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
|
||
<polyline points="22 4 12 14.01 9 11.01"/>
|
||
</svg>
|
||
</div>
|
||
<div class="s-num">{{ $stats['selesai'] ?? 0 }}</div>
|
||
<div class="s-label">Kegiatan Selesai</div>
|
||
<div class="s-trend up">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"/></svg>
|
||
8% dari kemarin
|
||
</div>
|
||
</div>
|
||
|
||
<div class="s-card c-yellow">
|
||
<div class="s-icon c-yellow">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||
<line x1="12" y1="2" x2="12" y2="6"/>
|
||
<line x1="12" y1="18" x2="12" y2="22"/>
|
||
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/>
|
||
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/>
|
||
<line x1="2" y1="12" x2="6" y2="12"/>
|
||
<line x1="18" y1="12" x2="22" y2="12"/>
|
||
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/>
|
||
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/>
|
||
</svg>
|
||
</div>
|
||
<div class="s-num">{{ $stats['proses'] ?? 0 }}</div>
|
||
<div class="s-label">Sedang Berjalan</div>
|
||
<div class="s-trend flat">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||
Sama seperti kemarin
|
||
</div>
|
||
</div>
|
||
|
||
<div class="s-card c-red">
|
||
<div class="s-icon c-red">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
||
<circle cx="9" cy="7" r="4"/>
|
||
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
|
||
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||
</svg>
|
||
</div>
|
||
<div class="s-num">{{ $stats['users'] ?? 0 }}</div>
|
||
<div class="s-label">Pengguna Aktif</div>
|
||
<div class="s-trend down">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
||
3% minggu ini
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
{{-- ── MAIN GRID ── --}}
|
||
<div class="content-grid">
|
||
|
||
{{-- LEFT: Kegiatan Terbaru --}}
|
||
<div class="g-card">
|
||
<div class="g-card-title">
|
||
<h3>5 Kegiatan Terbaru</h3>
|
||
<a href="{{ route('admin.kegiatan.index') }}">Lihat Semua →</a>
|
||
</div>
|
||
|
||
<div class="ftabs">
|
||
<div class="ftab on" onclick="filterAct(this,'semua')">Semua</div>
|
||
<div class="ftab off" onclick="filterAct(this,'selesai')">Selesai</div>
|
||
<div class="ftab off" onclick="filterAct(this,'on_progres')">Berjalan</div>
|
||
<div class="ftab off" onclick="filterAct(this,'terlambat')">Terlambat</div>
|
||
<div class="ftab off" onclick="filterAct(this,'dibatalkan')">Dibatalkan</div>
|
||
</div>
|
||
|
||
<div class="act-divider"></div>
|
||
|
||
<div id="act-list">
|
||
@php
|
||
$avClasses = ['av-1','av-2','av-3','av-4','av-5'];
|
||
$pillMap = [
|
||
'selesai' => ['pill-ok', 'Selesai'],
|
||
'telah_ditinjau' => ['pill-ok', 'Ditinjau'],
|
||
'on_progres' => ['pill-run', 'Berjalan'],
|
||
'terlambat' => ['pill-del', 'Terlambat'],
|
||
'dibatalkan' => ['pill-del', 'Dibatalkan'],
|
||
'review' => ['pill-pnd', 'Review'],
|
||
'pending' => ['pill-pnd', 'Pending'],
|
||
];
|
||
@endphp
|
||
|
||
@forelse($activities ?? [] as $idx => $item)
|
||
@php
|
||
$st = strtolower($item->status ?? 'pending');
|
||
$pc = $pillMap[$st] ?? ['pill-pnd', ucfirst($st)];
|
||
$ac = $avClasses[$idx % 5];
|
||
$tgl = \Carbon\Carbon::parse($item->tanggal)->locale('id')->translatedFormat('d M Y');
|
||
$usr = e($item->user->name ?? 'Sistem');
|
||
$nm = e($item->nama_kegiatan);
|
||
@endphp
|
||
<div class="act-item"
|
||
data-status="{{ $st }}"
|
||
onclick="openModal(
|
||
'{{ addslashes($nm) }}',
|
||
'{{ addslashes($usr) }}',
|
||
'{{ $tgl }}',
|
||
'{{ $st }}',
|
||
'{{ addslashes(str_replace(["\r", "\n"], ["", "\\n"], $item->keterangan ?? '-')) }}',
|
||
'{{ addslashes(str_replace(["\r", "\n"], ["", "\\n"], $item->status_note ?? '')) }}',
|
||
'{{ addslashes($item->lokasi ?? '-') }}',
|
||
'{{ $item->lampiran ? rtrim(env('BACKEND_API_URL', 'http://127.0.0.1:8000'), '/api') . '/storage/' . $item->lampiran : '' }}'
|
||
)">
|
||
<div class="act-ava {{ $ac }}">{{ strtoupper(substr($item->user->name ?? 'U', 0, 2)) }}</div>
|
||
<div class="act-body">
|
||
<div class="act-name">{{ $nm }}</div>
|
||
<div class="act-meta">{{ $usr }} • {{ $tgl }}</div>
|
||
</div>
|
||
<span class="pill {{ $pc[0] }}">{{ $pc[1] }}</span>
|
||
</div>
|
||
@empty
|
||
<div class="empty-box">
|
||
<div class="ei">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>
|
||
</div>
|
||
<p>Belum ada aktivitas kegiatan yang tercatat.</p>
|
||
</div>
|
||
@endforelse
|
||
</div>
|
||
</div>
|
||
|
||
{{-- RIGHT: Sidebar --}}
|
||
<div class="sidebar-col">
|
||
|
||
{{-- Weekly Chart --}}
|
||
<div class="g-card">
|
||
<div class="g-card-title" style="margin-bottom:.8rem">
|
||
<h3>Aktivitas Minggu Ini</h3>
|
||
@php $wTotal = array_sum($weeklyData ?? [0,0,0,0,0,0,0]); @endphp
|
||
<span class="pill pill-run">{{ $wTotal }} kegiatan</span>
|
||
</div>
|
||
|
||
@php
|
||
$days = ['Sen','Sel','Rab','Kam','Jum','Sab','Min'];
|
||
$wData = $weeklyData ?? [0,0,0,0,0,0,0];
|
||
$maxBar = max($wData) ?: 1;
|
||
$todayDI = (int)\Carbon\Carbon::now()->dayOfWeekIso - 1;
|
||
@endphp
|
||
|
||
<div class="chart-bars">
|
||
@foreach($days as $i => $d)
|
||
@php $pct = round(($wData[$i] / $maxBar) * 100); $isToday = $i === $todayDI; @endphp
|
||
<div class="bar-wrap">
|
||
<div class="bar-body {{ $isToday ? 'today' : ($wData[$i] > 0 ? 'active' : '') }}"
|
||
style="height:{{ max($pct, 8) }}%"
|
||
data-val="{{ $wData[$i] }} kegiatan">
|
||
</div>
|
||
<span class="bar-lbl {{ $isToday ? 'today-lbl' : '' }}">{{ $d }}</span>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
|
||
<div class="chart-footer">
|
||
<span>Total minggu ini</span>
|
||
<strong>{{ $wTotal }} kegiatan</strong>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Division Progress --}}
|
||
<div class="g-card">
|
||
<div class="g-card-title" style="margin-bottom:.8rem">
|
||
<h3>Progres Divisi</h3>
|
||
</div>
|
||
{{-- FIX: Gunakan $divisionProgress dari controller, fallback jika tidak ada --}}
|
||
@php
|
||
$divs = $divisionProgress ?? [
|
||
['name' => 'Operasional', 'pct' => 78, 'clr' => 'linear-gradient(90deg,#3b82f6,#6366f1)'],
|
||
['name' => 'Keuangan', 'pct' => 64, 'clr' => 'linear-gradient(90deg,#22c55e,#10b981)'],
|
||
['name' => 'SDM', 'pct' => 91, 'clr' => 'linear-gradient(90deg,#f97316,#f59e0b)'],
|
||
['name' => 'IT', 'pct' => 55, 'clr' => 'linear-gradient(90deg,#a855f7,#ec4899)'],
|
||
];
|
||
@endphp
|
||
@foreach($divs as $dv)
|
||
<div class="prog-item">
|
||
<div class="prog-head">
|
||
<span>{{ $dv['name'] }}</span>
|
||
<strong>{{ $dv['pct'] ?? 0 }}%</strong>
|
||
</div>
|
||
<div class="prog-track">
|
||
<div class="prog-fill" style="width:{{ $dv['pct'] ?? 0 }}%;background:{{ $dv['clr'] ?? '#3b82f6' }}"></div>
|
||
</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
|
||
{{-- Top Kontributor --}}
|
||
@if(!empty($topUsers) && count($topUsers))
|
||
<div class="g-card">
|
||
<div class="g-card-title" style="margin-bottom:.8rem">
|
||
<h3>Top Kontributor</h3>
|
||
</div>
|
||
@foreach($topUsers as $ri => $u)
|
||
@php
|
||
$rankCls = match($ri) { 0 => 'rank-1', 1 => 'rank-2', 2 => 'rank-3', default => 'rank-n' };
|
||
$medal = match($ri) {
|
||
0 => '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="#b45309" stroke="none"><circle cx="12" cy="8" r="6"/><path d="M15.477 12.89L17 22l-5-3-5 3 1.523-9.11"/></svg>',
|
||
1 => '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="#64748b" stroke="none"><circle cx="12" cy="8" r="6"/><path d="M15.477 12.89L17 22l-5-3-5 3 1.523-9.11"/></svg>',
|
||
2 => '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="#c2410c" stroke="none"><circle cx="12" cy="8" r="6"/><path d="M15.477 12.89L17 22l-5-3-5 3 1.523-9.11"/></svg>',
|
||
default => $ri + 1
|
||
};
|
||
@endphp
|
||
<div class="usr-row">
|
||
<div class="usr-rank {{ $rankCls }}">{!! $medal !!}</div>
|
||
<div class="usr-ava">{{ strtoupper(substr($u->name, 0, 2)) }}</div>
|
||
<span class="usr-name">{{ $u->name }}</span>
|
||
{{-- FIX: Ganti kegiatan_count → activities_count (sesuai withCount('activities')) --}}
|
||
<span class="usr-count">{{ $u->activities_count ?? 0 }} Lap.</span>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
@endif
|
||
|
||
</div>{{-- /sidebar --}}
|
||
</div>{{-- /content-grid --}}
|
||
|
||
</div>{{-- /d-wrap --}}
|
||
|
||
<script>
|
||
/* ── Filter Tabs ─────────────────── */
|
||
function filterAct(el, status) {
|
||
document.querySelectorAll('.ftab').forEach(t => {
|
||
t.classList.remove('on');
|
||
t.classList.add('off');
|
||
});
|
||
el.classList.add('on');
|
||
el.classList.remove('off');
|
||
document.querySelectorAll('#act-list .act-item').forEach(row => {
|
||
row.style.display = (status === 'semua' || row.dataset.status === status) ? 'flex' : 'none';
|
||
});
|
||
}
|
||
|
||
/* ── Modal ───────────────────────── */
|
||
const pillLabels = {
|
||
selesai: '<span class="pill pill-ok">Selesai</span>',
|
||
on_progres: '<span class="pill pill-run">Berjalan</span>',
|
||
terlambat: '<span class="pill pill-del">Terlambat</span>',
|
||
dibatalkan: '<span class="pill pill-del">Dibatalkan</span>',
|
||
review: '<span class="pill pill-pnd">Review</span>',
|
||
pending: '<span class="pill pill-pnd">Pending</span>'
|
||
};
|
||
|
||
function openModal(nama, user, tgl, status, ket, statusNote, lokasi, lampiranUrl) {
|
||
document.getElementById('mdl-title').textContent = nama;
|
||
|
||
let lampiranHtml = '';
|
||
if (lampiranUrl) {
|
||
lampiranHtml = `
|
||
<div class="modal-field" style="margin-top: 15px; grid-column: span 2;">
|
||
<span class="mf-label">Lampiran Foto/Dokumen</span>
|
||
<div style="margin-top: 8px;">
|
||
<img src="${lampiranUrl}" alt="Lampiran" style="max-width: 100%; max-height: 400px; border-radius: 6px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
document.getElementById('mdl-body').innerHTML = `
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
|
||
<div class="modal-field">
|
||
<span class="mf-label">Pegawai Ditugaskan</span>
|
||
<span class="mf-value">${user}</span>
|
||
</div>
|
||
<div class="modal-field">
|
||
<span class="mf-label">Tanggal</span>
|
||
<span class="mf-value">${tgl}</span>
|
||
</div>
|
||
<div class="modal-field">
|
||
<span class="mf-label">Lokasi Tugas</span>
|
||
<span class="mf-value">${lokasi}</span>
|
||
</div>
|
||
<div class="modal-field">
|
||
<span class="mf-label">Status</span>
|
||
<span class="mf-value">${pillLabels[status] || status}</span>
|
||
</div>
|
||
<div class="modal-field" style="grid-column: span 2;">
|
||
<span class="mf-label">Deskripsi Laporan (Keterangan)</span>
|
||
<span class="mf-value" style="white-space: pre-wrap; line-height: 1.5;">${ket || '—'}</span>
|
||
</div>
|
||
${statusNote ? `
|
||
<div class="modal-field" style="grid-column: span 2; background: #eff6ff; padding: 12px; border-radius: 8px;">
|
||
<span class="mf-label" style="color: #2563eb; margin-bottom: 4px; display: block;">Catatan Status (Node Petugas)</span>
|
||
<span class="mf-value" style="white-space: pre-wrap; line-height: 1.5; color: #1e3a8a;">${statusNote}</span>
|
||
</div>
|
||
` : ''}
|
||
${lampiranHtml}
|
||
</div>
|
||
`;
|
||
document.getElementById('detailModal').classList.add('show');
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById('detailModal').classList.remove('show');
|
||
}
|
||
|
||
document.getElementById('detailModal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeModal();
|
||
});
|
||
|
||
document.addEventListener('keydown', e => {
|
||
if (e.key === 'Escape') closeModal();
|
||
});
|
||
</script>
|
||
|
||
@endsection |