1441 lines
68 KiB
PHP
1441 lines
68 KiB
PHP
@extends('admin.layouts.app')
|
||
|
||
@section('content')
|
||
<style>
|
||
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,400&family=Sora:wght@400;600;700;800&display=swap');
|
||
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
:root {
|
||
--bg: #f0f4fb;
|
||
--bg2: #e4ecf7;
|
||
--surface: #ffffff;
|
||
--surface2: #f7f9ff;
|
||
--surface3: #eef2fb;
|
||
--border: #dde5f4;
|
||
--border2: #c8d5ee;
|
||
|
||
--text-primary: #0f1c3d;
|
||
--text-secondary: #3d5280;
|
||
--text-muted: #7a8fb5;
|
||
--text-faint: #b0bfd8;
|
||
|
||
--blue: #1a4fd6;
|
||
--blue-mid: #2563eb;
|
||
--blue-light: #3b82f6;
|
||
--blue-pale: #dbeafe;
|
||
--blue-ultra: #eff6ff;
|
||
|
||
--indigo: #4338ca;
|
||
--indigo-pale: #e0e7ff;
|
||
|
||
--green: #059669;
|
||
--green-mid: #10b981;
|
||
--green-pale: #d1fae5;
|
||
--green-ultra: #ecfdf5;
|
||
|
||
--amber: #d97706;
|
||
--amber-pale: #fde68a;
|
||
--amber-ultra: #fffbeb;
|
||
|
||
--red: #dc2626;
|
||
--red-pale: #fee2e2;
|
||
--red-ultra: #fef2f2;
|
||
|
||
--purple: #7c3aed;
|
||
--purple-pale: #ede9fe;
|
||
--purple-ultra:#f5f3ff;
|
||
|
||
--shadow-xs: 0 1px 3px rgba(15,28,61,0.06);
|
||
--shadow-sm: 0 2px 8px rgba(15,28,61,0.08);
|
||
--shadow: 0 4px 20px rgba(15,28,61,0.10);
|
||
--shadow-md: 0 8px 32px rgba(15,28,61,0.12);
|
||
--shadow-lg: 0 24px 64px rgba(15,28,61,0.18);
|
||
|
||
--radius: 16px;
|
||
--radius-sm: 10px;
|
||
--radius-xs: 8px;
|
||
}
|
||
|
||
.sa-root {
|
||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||
background: var(--bg);
|
||
min-height: 100vh;
|
||
color: var(--text-primary);
|
||
padding: 2rem;
|
||
}
|
||
|
||
/* ─── PAGE HEADER ─── */
|
||
.sa-page-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
margin-bottom: 1.75rem;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
}
|
||
.sa-page-eyebrow {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 10.5px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.8px;
|
||
color: var(--blue-mid);
|
||
margin-bottom: 7px;
|
||
}
|
||
.sa-page-eyebrow-line {
|
||
width: 24px; height: 2.5px;
|
||
background: linear-gradient(90deg, var(--blue-mid), var(--blue-light));
|
||
border-radius: 2px;
|
||
}
|
||
.sa-page-title {
|
||
font-family: 'Sora', sans-serif;
|
||
font-size: 27px; font-weight: 800;
|
||
color: var(--text-primary);
|
||
letter-spacing: -0.6px; line-height: 1.15;
|
||
}
|
||
.sa-page-sub {
|
||
font-size: 13px; color: var(--text-muted);
|
||
margin-top: 5px; font-weight: 400;
|
||
}
|
||
.sa-header-right { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||
|
||
.sa-date-chip {
|
||
display: flex; align-items: center; gap: 8px;
|
||
background: var(--surface); border: 1.5px solid var(--border);
|
||
border-radius: 50px; padding: 8px 16px;
|
||
font-size: 12px; color: var(--text-secondary);
|
||
font-weight: 600; box-shadow: var(--shadow-xs);
|
||
}
|
||
.sa-superadmin-badge {
|
||
display: flex; align-items: center; gap: 7px;
|
||
background: linear-gradient(135deg, #fef9c3, #fef3c7);
|
||
border: 1.5px solid #f5c842; color: #92400e;
|
||
padding: 8px 16px; border-radius: 50px;
|
||
font-size: 11px; font-weight: 700;
|
||
font-family: 'Sora', sans-serif;
|
||
text-transform: uppercase; letter-spacing: 1px;
|
||
box-shadow: 0 2px 8px rgba(245,200,66,0.18);
|
||
}
|
||
.sa-badge-dot {
|
||
width: 7px; height: 7px; border-radius: 50%;
|
||
background: #f59e0b; box-shadow: 0 0 6px #f59e0b;
|
||
animation: blink 2s infinite;
|
||
}
|
||
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.35} }
|
||
|
||
/* ─── ALERTS ─── */
|
||
.alert-session {
|
||
display: flex; align-items: center; gap: 10px;
|
||
padding: 13px 16px; border-radius: var(--radius-sm);
|
||
margin-bottom: 1.5rem; font-size: 13px; font-weight: 500;
|
||
box-shadow: var(--shadow-xs);
|
||
}
|
||
.alert-session.success { background:var(--green-ultra); border:1.5px solid var(--green-pale); color:#065f46; }
|
||
.alert-session.error { background:var(--red-ultra); border:1.5px solid var(--red-pale); color:#991b1b; }
|
||
|
||
/* ─── STATS GRID ─── */
|
||
.sa-stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 16px; margin-bottom: 1.5rem;
|
||
}
|
||
.stat-card {
|
||
background: var(--surface); border: 1.5px solid var(--border);
|
||
border-radius: var(--radius); padding: 1.4rem 1.5rem;
|
||
position: relative; overflow: hidden;
|
||
transition: all 0.25s ease; box-shadow: var(--shadow-xs);
|
||
cursor: default;
|
||
}
|
||
.stat-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--border2); }
|
||
.stat-card-glow {
|
||
position: absolute; top: -30px; right: -30px;
|
||
width: 90px; height: 90px; border-radius: 50%;
|
||
opacity: 0.07; pointer-events: none;
|
||
}
|
||
.stat-card.gold .stat-card-glow { background: var(--amber); }
|
||
.stat-card.blue .stat-card-glow { background: var(--blue-mid); }
|
||
.stat-card.green .stat-card-glow { background: var(--green); }
|
||
.stat-card.red .stat-card-glow { background: var(--red); }
|
||
.stat-card.purple .stat-card-glow { background: var(--purple); }
|
||
|
||
.stat-card::after {
|
||
content: ''; position: absolute;
|
||
top: 0; left: 0; right: 0; height: 3px; border-radius: 3px 3px 0 0;
|
||
}
|
||
.stat-card.gold::after { background: linear-gradient(90deg,#d97706,#fbbf24); }
|
||
.stat-card.blue::after { background: linear-gradient(90deg,#1a4fd6,#3b82f6); }
|
||
.stat-card.green::after { background: linear-gradient(90deg,#059669,#34d399); }
|
||
.stat-card.red::after { background: linear-gradient(90deg,#dc2626,#f87171); }
|
||
.stat-card.purple::after { background: linear-gradient(90deg,#7c3aed,#a78bfa); }
|
||
|
||
.stat-icon-wrap {
|
||
width: 44px; height: 44px; border-radius: 12px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
margin-bottom: 16px; flex-shrink: 0;
|
||
}
|
||
.stat-card.gold .stat-icon-wrap { background:var(--amber-ultra); color:var(--amber); }
|
||
.stat-card.blue .stat-icon-wrap { background:var(--blue-ultra); color:var(--blue-mid); }
|
||
.stat-card.green .stat-icon-wrap { background:var(--green-ultra); color:var(--green); }
|
||
.stat-card.red .stat-icon-wrap { background:var(--red-ultra); color:var(--red); }
|
||
.stat-card.purple .stat-icon-wrap { background:var(--purple-ultra); color:var(--purple); }
|
||
|
||
.stat-label {
|
||
font-size: 10.5px; font-weight: 700; color: var(--text-muted);
|
||
text-transform: uppercase; letter-spacing: 1.2px; margin-bottom: 6px;
|
||
}
|
||
.stat-num {
|
||
font-family: 'Sora', sans-serif; font-size: 36px;
|
||
font-weight: 800; letter-spacing: -2px; line-height: 1;
|
||
}
|
||
.stat-num.gold { color: var(--amber); }
|
||
.stat-num.blue { color: var(--blue-mid); }
|
||
.stat-num.green { color: var(--green); }
|
||
.stat-num.red { color: var(--red); }
|
||
.stat-num.purple { color: var(--purple); }
|
||
.stat-sub {
|
||
font-size: 11.5px; color: var(--text-faint);
|
||
margin-top: 8px;
|
||
}
|
||
.stat-trend {
|
||
display: inline-flex; align-items: center; gap: 4px;
|
||
font-size: 10.5px; font-weight: 700; margin-top: 8px;
|
||
padding: 3px 8px; border-radius: 20px;
|
||
}
|
||
.stat-trend.up { background: var(--green-ultra); color: var(--green); }
|
||
.stat-trend.down { background: var(--red-ultra); color: var(--red); }
|
||
.stat-trend.flat { background: var(--surface3); color: var(--text-muted); }
|
||
|
||
/* ─── CHART CARD ─── */
|
||
.sa-chart-wrap {
|
||
background: var(--surface); border: 1.5px solid var(--border);
|
||
border-radius: var(--radius); overflow: hidden;
|
||
box-shadow: var(--shadow-xs); margin-bottom: 1.5rem;
|
||
}
|
||
.sa-chart-head {
|
||
padding: 1.25rem 1.5rem;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
border-bottom: 1.5px solid var(--border); background: var(--surface2);
|
||
flex-wrap: wrap; gap: 10px;
|
||
}
|
||
.sa-chart-title {
|
||
font-family: 'Sora', sans-serif; font-size: 14px; font-weight: 700;
|
||
color: var(--text-primary); display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.sa-chart-title-icon {
|
||
width: 30px; height: 30px; border-radius: 8px;
|
||
background: var(--indigo-pale); border: 1.5px solid #c7d2fe;
|
||
display: flex; align-items: center; justify-content: center; color: var(--indigo);
|
||
}
|
||
.chart-filter-tabs {
|
||
display: flex; gap: 4px; background: var(--bg2);
|
||
border-radius: 8px; padding: 3px;
|
||
border: 1.5px solid var(--border);
|
||
}
|
||
.chart-tab {
|
||
padding: 5px 13px; border-radius: 6px;
|
||
font-size: 11px; font-weight: 700;
|
||
color: var(--text-muted); cursor: pointer;
|
||
transition: all 0.2s; border: none; background: none;
|
||
font-family: 'Plus Jakarta Sans', sans-serif;
|
||
}
|
||
.chart-tab.active {
|
||
background: var(--blue-mid); color: #fff;
|
||
box-shadow: 0 2px 8px rgba(37,99,235,0.25);
|
||
}
|
||
.chart-tab:hover:not(.active) { background: var(--surface); color: var(--text-secondary); }
|
||
.chart-body { padding: 1.5rem; position: relative; min-height: 280px; }
|
||
|
||
/* ─── CHART CANVAS ─── */
|
||
.chart-canvas-wrap {
|
||
position: relative; width: 100%; height: 240px;
|
||
display: flex; align-items: flex-end; gap: 10px;
|
||
}
|
||
.chart-y-labels {
|
||
display: flex; flex-direction: column; justify-content: space-between;
|
||
height: 100%; padding-bottom: 24px;
|
||
font-size: 10px; color: var(--text-faint); font-weight: 600;
|
||
text-align: right; width: 30px; flex-shrink: 0;
|
||
}
|
||
.chart-bars-area {
|
||
flex: 1; height: 100%; display: flex; align-items: flex-end;
|
||
position: relative;
|
||
}
|
||
.chart-grid-lines {
|
||
position: absolute; top: 0; left: 0; right: 0;
|
||
bottom: 24px; pointer-events: none;
|
||
display: flex; flex-direction: column; justify-content: space-between;
|
||
}
|
||
.chart-grid-line {
|
||
width: 100%; height: 1px;
|
||
background: var(--border); opacity: 0.7;
|
||
}
|
||
.chart-bars {
|
||
flex: 1; display: flex; align-items: flex-end;
|
||
gap: 8px; position: relative; z-index: 1;
|
||
padding-bottom: 24px;
|
||
height: 100%;
|
||
}
|
||
.chart-bar-col {
|
||
flex: 1; display: flex; flex-direction: column;
|
||
align-items: center; gap: 6px; height: 100%;
|
||
justify-content: flex-end;
|
||
}
|
||
.chart-bar-wrap {
|
||
width: 100%; flex: 1; display: flex; align-items: flex-end;
|
||
min-height: 0;
|
||
}
|
||
.chart-bar {
|
||
width: 100%; border-radius: 7px 7px 0 0;
|
||
cursor: pointer; transition: all 0.35s cubic-bezier(0.34,1.56,0.64,1);
|
||
position: relative;
|
||
background: linear-gradient(180deg, #3b82f6, #1a4fd6);
|
||
transform-origin: bottom;
|
||
min-height: 4px;
|
||
}
|
||
.chart-bar:hover { filter: brightness(1.1); transform: scaleX(1.05); }
|
||
.chart-bar.active-day {
|
||
background: linear-gradient(180deg, #a78bfa, #7c3aed);
|
||
box-shadow: 0 4px 20px rgba(124,58,237,0.4);
|
||
}
|
||
.chart-bar-tooltip {
|
||
position: absolute; bottom: calc(100% + 8px); left: 50%;
|
||
transform: translateX(-50%) scale(0.85);
|
||
background: var(--text-primary); color: #fff;
|
||
padding: 5px 10px; border-radius: 8px;
|
||
font-size: 11px; font-weight: 700;
|
||
white-space: nowrap; pointer-events: none;
|
||
opacity: 0; transition: all 0.2s;
|
||
font-family: 'Sora', sans-serif;
|
||
z-index: 10;
|
||
}
|
||
.chart-bar-tooltip::after {
|
||
content: ''; position: absolute; top: 100%; left: 50%;
|
||
transform: translateX(-50%);
|
||
border: 5px solid transparent;
|
||
border-top-color: var(--text-primary);
|
||
}
|
||
.chart-bar-col:hover .chart-bar-tooltip {
|
||
opacity: 1; transform: translateX(-50%) scale(1);
|
||
}
|
||
.chart-bar-label {
|
||
font-size: 10.5px; font-weight: 700; color: var(--text-muted);
|
||
text-align: center; flex-shrink: 0;
|
||
margin-top: 4px;
|
||
}
|
||
.chart-bar-label.today-label { color: var(--purple); }
|
||
|
||
.chart-summary {
|
||
display: flex; gap: 24px; margin-top: 1.25rem;
|
||
padding-top: 1.25rem; border-top: 1px solid var(--border);
|
||
flex-wrap: wrap;
|
||
}
|
||
.chart-summary-item { display: flex; align-items: center; gap: 8px; }
|
||
.cs-dot {
|
||
width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0;
|
||
}
|
||
.cs-label { font-size: 12px; color: var(--text-muted); font-weight: 500; }
|
||
.cs-val {
|
||
font-family: 'Sora', sans-serif; font-size: 13px;
|
||
font-weight: 700; color: var(--text-primary);
|
||
}
|
||
|
||
/* ─── MAIN LAYOUT ─── */
|
||
.sa-main { display: grid; grid-template-columns: 1fr 300px; gap: 20px; align-items: start; }
|
||
|
||
/* ─── TABLE CARD ─── */
|
||
.sa-table-wrap {
|
||
background: var(--surface); border: 1.5px solid var(--border);
|
||
border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-xs);
|
||
}
|
||
.sa-table-head {
|
||
padding: 1.25rem 1.5rem;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
border-bottom: 1.5px solid var(--border); background: var(--surface2);
|
||
flex-wrap: wrap; gap: 10px;
|
||
}
|
||
.sa-table-title {
|
||
font-family: 'Sora', sans-serif; font-size: 14px; font-weight: 700;
|
||
color: var(--text-primary); display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.sa-table-title-icon {
|
||
width: 30px; height: 30px; border-radius: 8px;
|
||
background: var(--blue-ultra); border: 1.5px solid var(--blue-pale);
|
||
display: flex; align-items: center; justify-content: center; color: var(--blue-mid);
|
||
}
|
||
.sa-table-head-right {
|
||
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
|
||
}
|
||
.sa-search {
|
||
display: flex; align-items: center; gap: 8px;
|
||
background: var(--surface); border: 1.5px solid var(--border);
|
||
border-radius: var(--radius-xs); padding: 7px 12px; transition: 0.2s;
|
||
}
|
||
.sa-search:focus-within { border-color:var(--blue-pale); box-shadow:0 0 0 3px rgba(37,99,235,0.08); }
|
||
.sa-search input {
|
||
background:none; border:none; outline:none;
|
||
font-size:13px; color:var(--text-primary);
|
||
width:180px; font-family:'Plus Jakarta Sans',sans-serif;
|
||
}
|
||
.sa-search input::placeholder { color:var(--text-faint); }
|
||
|
||
.filter-select {
|
||
background: var(--surface); border: 1.5px solid var(--border);
|
||
border-radius: var(--radius-xs); padding: 7px 12px;
|
||
font-size: 12px; color: var(--text-secondary);
|
||
font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 600;
|
||
cursor: pointer; outline: none; transition: 0.2s;
|
||
}
|
||
.filter-select:focus { border-color: var(--blue-pale); }
|
||
|
||
table.sa-tbl { width:100%; border-collapse:collapse; }
|
||
table.sa-tbl thead tr th {
|
||
padding:11px 20px; text-align:left;
|
||
font-size:10.5px; font-weight:700; color:var(--text-muted);
|
||
text-transform:uppercase; letter-spacing:1.1px;
|
||
background:var(--surface2); border-bottom:1.5px solid var(--border);
|
||
}
|
||
table.sa-tbl tbody tr { border-bottom:1px solid var(--border); transition:background 0.15s; }
|
||
table.sa-tbl tbody tr:hover { background:var(--blue-ultra); }
|
||
table.sa-tbl tbody tr:last-child { border-bottom:none; }
|
||
table.sa-tbl td { padding:14px 20px; font-size:13px; vertical-align:middle; }
|
||
|
||
.user-cell { display:flex; align-items:center; gap:10px; }
|
||
.user-avatar {
|
||
width:36px; height:36px; border-radius:10px;
|
||
background:linear-gradient(135deg,#1a3fb5,#3b82f6);
|
||
display:flex; align-items:center; justify-content:center;
|
||
font-size:11px; font-weight:800; color:#fff;
|
||
font-family:'Sora',sans-serif; flex-shrink:0;
|
||
box-shadow:0 2px 8px rgba(26,79,214,0.22);
|
||
}
|
||
.user-name { font-weight:600; color:var(--text-primary); font-size:13px; }
|
||
.user-role { font-size:11px; color:var(--text-muted); margin-top:1px; }
|
||
.activity-name {
|
||
color:var(--text-secondary); font-weight:500;
|
||
max-width:200px; display:block;
|
||
white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
|
||
}
|
||
.date-cell { color:var(--text-muted); font-size:12px; font-weight:500; }
|
||
.instansi-badge {
|
||
display: inline-block; padding: 3px 9px;
|
||
background: var(--indigo-pale); color: var(--indigo);
|
||
border-radius: 20px; font-size: 10.5px; font-weight: 700;
|
||
border: 1.5px solid #c7d2fe;
|
||
}
|
||
|
||
.status-pill {
|
||
display:inline-flex; align-items:center; gap:5px;
|
||
padding:4px 11px; border-radius:50px;
|
||
font-size:10px; font-weight:700;
|
||
text-transform:uppercase; letter-spacing:0.8px;
|
||
}
|
||
.status-pill.selesai { background:var(--green-ultra); color:#065f46; border:1.5px solid var(--green-pale); }
|
||
.status-pill.proses { background:var(--amber-ultra); color:#92400e; border:1.5px solid #fde68a; }
|
||
.status-pill.terlambat { background:var(--red-ultra); color:#b91c1c; border:1.5px solid var(--red-pale); }
|
||
.status-dot { width:5px; height:5px; border-radius:50%; background:currentColor; }
|
||
|
||
.actions { display:flex; gap:6px; justify-content:center; align-items:center; }
|
||
.act-btn {
|
||
width:32px; height:32px; border-radius:var(--radius-xs); border:none;
|
||
display:flex; align-items:center; justify-content:center;
|
||
cursor:pointer; transition:all 0.2s; font-size:13px; text-decoration:none;
|
||
}
|
||
.act-view { background:var(--blue-ultra); color:var(--blue-mid); border:1.5px solid var(--blue-pale); }
|
||
.act-view:hover { background:var(--blue-pale); transform:scale(1.08); }
|
||
.act-edit { background:var(--green-ultra); color:var(--green); border:1.5px solid var(--green-pale); }
|
||
.act-edit:hover { background:var(--green-pale); transform:scale(1.08); }
|
||
.act-delete{ background:var(--red-ultra); color:var(--red); border:1.5px solid var(--red-pale); }
|
||
.act-delete:hover { background:var(--red-pale); transform:scale(1.08); }
|
||
|
||
/* ─── PAGINATION ─── */
|
||
.sa-pagination {
|
||
display:flex; justify-content:space-between; align-items:center;
|
||
padding:14px 20px; border-top:1px solid var(--border); background:var(--surface2);
|
||
flex-wrap: wrap; gap: 8px;
|
||
}
|
||
.page-info { font-size:12px; color:var(--text-muted); font-weight:500; }
|
||
.page-btns { display:flex; gap:5px; }
|
||
.pg-btn {
|
||
min-width:32px; height:32px; border-radius:var(--radius-xs);
|
||
border:1.5px solid var(--border); background:var(--surface);
|
||
color:var(--text-secondary); font-size:12px; font-weight:600;
|
||
cursor:pointer; display:flex; align-items:center; justify-content:center;
|
||
transition:all 0.15s; text-decoration:none; padding:0 8px;
|
||
font-family:'Plus Jakarta Sans',sans-serif;
|
||
}
|
||
.pg-btn:hover { background:var(--blue-ultra); border-color:var(--blue-pale); color:var(--blue-mid); }
|
||
.pg-btn.active { background:var(--blue-mid); border-color:var(--blue-mid); color:#fff; box-shadow:0 2px 8px rgba(37,99,235,0.3); }
|
||
.pg-btn:disabled { opacity:0.35; cursor:not-allowed; }
|
||
|
||
/* ─── EMPTY STATE ─── */
|
||
.empty-state { text-align:center; padding:3.5rem 2rem; }
|
||
.empty-state-icon { margin-bottom:12px; color:var(--text-faint); display:flex; justify-content:center; }
|
||
.empty-state p { font-size:13px; color:var(--text-faint); }
|
||
|
||
/* ─── SIDEBAR ─── */
|
||
.sa-sidebar { display:flex; flex-direction:column; gap:16px; }
|
||
.side-card {
|
||
background:var(--surface); border:1.5px solid var(--border);
|
||
border-radius:var(--radius); padding:1.25rem; box-shadow:var(--shadow-xs);
|
||
}
|
||
.side-title {
|
||
font-family:'Sora',sans-serif; font-size:11.5px; font-weight:700;
|
||
color:var(--text-muted); text-transform:uppercase; letter-spacing:1.4px;
|
||
margin-bottom:1.1rem; display:flex; align-items:center; gap:8px;
|
||
}
|
||
.side-title-bar { width:3px; height:14px; border-radius:2px; background:linear-gradient(180deg,var(--blue-mid),var(--purple)); flex-shrink:0; }
|
||
|
||
/* ─── LOG ─── */
|
||
.activity-log-item { display:flex; gap:10px; margin-bottom:14px; position:relative; }
|
||
.activity-log-item:last-child { margin-bottom:0; }
|
||
.log-line { position:absolute; left:5px; top:16px; bottom:-14px; width:1px; background:var(--border); }
|
||
.activity-log-item:last-child .log-line { display:none; }
|
||
.log-dot { width:11px; height:11px; border-radius:50%; flex-shrink:0; margin-top:3px; border:2px solid; }
|
||
.log-dot.blue { border-color:var(--blue-mid); background:var(--blue-pale); }
|
||
.log-dot.green { border-color:var(--green); background:var(--green-pale); }
|
||
.log-dot.red { border-color:var(--red); background:var(--red-pale); }
|
||
.log-dot.amber { border-color:var(--amber); background:#fde68a; }
|
||
.log-dot.purple{ border-color:var(--purple); background:var(--purple-pale); }
|
||
.log-text { font-size:12px; color:var(--text-secondary); line-height:1.5; }
|
||
.log-text strong { color:var(--text-primary); font-weight:600; }
|
||
.log-time { font-size:10.5px; color:var(--text-faint); margin-top:2px; }
|
||
|
||
/* ─── SYSTEM STATUS ─── */
|
||
.system-bar { display:flex; justify-content:space-between; align-items:center; margin-bottom:5px; }
|
||
.sys-label { font-size:12px; color:var(--text-secondary); font-weight:500; }
|
||
.sys-val { font-size:12px; font-weight:700; color:var(--text-primary); font-family:'Sora',sans-serif; }
|
||
.sys-track { height:7px; background:var(--bg2); border-radius:4px; overflow:hidden; margin-bottom:13px; border:1px solid var(--border); }
|
||
.sys-fill { height:100%; border-radius:4px; transition: width 1s ease; }
|
||
.sys-status-row {
|
||
display: flex; align-items: center; gap: 6px;
|
||
font-size: 11px; font-weight: 700; color: var(--green);
|
||
margin-top: 4px;
|
||
}
|
||
.sys-status-dot {
|
||
width: 7px; height: 7px; border-radius: 50%;
|
||
background: var(--green); box-shadow: 0 0 5px var(--green);
|
||
animation: blink 2s infinite;
|
||
}
|
||
|
||
/* ─── QUICK ACTIONS ─── */
|
||
.quick-action {
|
||
display:flex; align-items:center; gap:10px;
|
||
padding:10px 12px; border-radius:var(--radius-xs);
|
||
background:var(--surface2); border:1.5px solid var(--border);
|
||
cursor:pointer; transition:all 0.2s; margin-bottom:8px;
|
||
font-size:12.5px; font-weight:500; color:var(--text-secondary);
|
||
text-decoration:none;
|
||
}
|
||
.quick-action:last-child { margin-bottom:0; }
|
||
.quick-action:hover {
|
||
background:var(--blue-ultra); border-color:var(--blue-pale);
|
||
color:var(--blue-mid); transform:translateX(4px);
|
||
}
|
||
.qa-icon {
|
||
width:30px; height:30px; border-radius:8px;
|
||
background:var(--blue-pale); color:var(--blue-mid);
|
||
display:flex; align-items:center; justify-content:center; flex-shrink:0;
|
||
transition: all 0.2s;
|
||
}
|
||
.quick-action:hover .qa-icon {
|
||
background: var(--blue-mid); color: #fff;
|
||
}
|
||
|
||
/* ─── TOP USERS SIDEBAR ─── */
|
||
.top-user-item {
|
||
display: flex; align-items: center; gap: 10px;
|
||
padding: 8px 0; border-bottom: 1px solid var(--border);
|
||
}
|
||
.top-user-item:last-child { border-bottom: none; padding-bottom: 0; }
|
||
.top-user-rank {
|
||
width: 22px; height: 22px; border-radius: 6px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 10px; font-weight: 800; flex-shrink: 0;
|
||
font-family: 'Sora', sans-serif;
|
||
}
|
||
.top-user-rank.r1 { background: #fef3c7; color: #d97706; }
|
||
.top-user-rank.r2 { background: #f3f4f6; color: #6b7280; }
|
||
.top-user-rank.r3 { background: #fff7ed; color: #c2410c; }
|
||
.top-user-rank.rn { background: var(--surface3); color: var(--text-muted); }
|
||
.top-user-av {
|
||
width: 30px; height: 30px; border-radius: 8px;
|
||
background: linear-gradient(135deg,#1a3fb5,#3b82f6);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 10px; font-weight: 800; color: #fff;
|
||
font-family: 'Sora', sans-serif; flex-shrink: 0;
|
||
}
|
||
.top-user-info { flex: 1; min-width: 0; }
|
||
.top-user-name { font-size: 12px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.top-user-count { font-size: 10.5px; color: var(--text-muted); }
|
||
.top-user-bar { width: 40px; height: 4px; background: var(--bg2); border-radius: 2px; overflow: hidden; flex-shrink: 0; }
|
||
.top-user-fill { height: 100%; background: linear-gradient(90deg, var(--blue-mid), var(--purple)); border-radius: 2px; }
|
||
|
||
/* ─── MODAL ─── */
|
||
.modal-overlay {
|
||
display:none; position:fixed; inset:0;
|
||
background:rgba(15,28,61,0.5); backdrop-filter:blur(8px);
|
||
z-index:999; align-items:center; justify-content:center;
|
||
}
|
||
.modal-overlay.active { display:flex; }
|
||
.modal-box {
|
||
background:var(--surface); border:1.5px solid var(--border);
|
||
border-radius:20px; padding:2rem; width:460px; max-width:calc(100vw - 2rem);
|
||
box-shadow:var(--shadow-lg); animation:modal-in 0.25s ease;
|
||
}
|
||
@keyframes modal-in { from{opacity:0;transform:scale(0.93) translateY(14px)} to{opacity:1;transform:scale(1) translateY(0)} }
|
||
|
||
.modal-header { display:flex; align-items:center; gap:12px; margin-bottom:1rem; }
|
||
.modal-icon {
|
||
width:44px; height:44px; border-radius:12px;
|
||
background:var(--red-ultra); border:1.5px solid var(--red-pale);
|
||
display:flex; align-items:center; justify-content:center; color:var(--red);
|
||
}
|
||
.modal-header h3 { font-family:'Sora',sans-serif; font-size:19px; font-weight:800; color:var(--text-primary); }
|
||
.modal-desc {
|
||
font-size:13px; color:var(--text-secondary); line-height:1.6;
|
||
margin-bottom:1rem; padding:12px 14px;
|
||
background:var(--red-ultra); border-radius:var(--radius-xs); border:1.5px solid var(--red-pale);
|
||
}
|
||
.modal-desc strong { color:var(--red); font-weight:600; }
|
||
.modal-info-row {
|
||
display: flex; gap: 10px; margin-bottom: 1rem;
|
||
}
|
||
.modal-info-chip {
|
||
display: flex; align-items: center; gap: 6px;
|
||
padding: 7px 12px; border-radius: 8px;
|
||
background: var(--surface2); border: 1.5px solid var(--border);
|
||
font-size: 11.5px; color: var(--text-secondary); font-weight: 600;
|
||
}
|
||
.modal-label { font-size:11px; font-weight:700; color:var(--text-muted); text-transform:uppercase; letter-spacing:1px; margin-bottom:8px; }
|
||
.modal-textarea {
|
||
width:100%; height:110px; border-radius:var(--radius-xs);
|
||
background:var(--surface2); border:1.5px solid var(--border);
|
||
color:var(--text-primary); padding:12px 14px;
|
||
font-family:'Plus Jakarta Sans',sans-serif; font-size:13px;
|
||
outline:none; resize:none; transition:0.2s; margin-bottom:1.25rem;
|
||
}
|
||
.modal-textarea:focus { border-color:var(--red-pale); box-shadow:0 0 0 3px rgba(220,38,38,0.08); background:var(--surface); }
|
||
.modal-textarea::placeholder { color:var(--text-faint); }
|
||
.modal-actions { display:flex; gap:10px; }
|
||
.btn-cancel {
|
||
flex:1; padding:11px; border-radius:var(--radius-xs);
|
||
background:var(--surface2); border:1.5px solid var(--border);
|
||
color:var(--text-secondary); font-size:13px; font-weight:600;
|
||
cursor:pointer; transition:all 0.2s; font-family:'Plus Jakarta Sans',sans-serif;
|
||
}
|
||
.btn-cancel:hover { background:var(--bg2); color:var(--text-primary); }
|
||
.btn-confirm {
|
||
flex:1; padding:11px; border-radius:var(--radius-xs);
|
||
background:linear-gradient(135deg,#dc2626,#b91c1c);
|
||
border:none; color:#fff; font-size:13px; font-weight:700;
|
||
cursor:pointer; transition:all 0.2s; font-family:'Plus Jakarta Sans',sans-serif;
|
||
box-shadow:0 4px 15px rgba(220,38,38,0.28);
|
||
}
|
||
.btn-confirm:hover { opacity:0.92; transform:translateY(-1px); }
|
||
|
||
/* ─── RESPONSIVE ─── */
|
||
@media (max-width:1200px) { .sa-main{grid-template-columns:1fr} .sa-sidebar{display:none} }
|
||
@media (max-width:900px) { .sa-stats{grid-template-columns:repeat(2,1fr)} }
|
||
@media (max-width:768px) {
|
||
.sa-root{padding:1rem}
|
||
.sa-stats{grid-template-columns:repeat(2,1fr)}
|
||
.sa-table-head { flex-direction: column; align-items: flex-start; }
|
||
.sa-table-head-right { width: 100%; flex-direction: column; align-items: stretch; }
|
||
.sa-search { width: 100%; }
|
||
.sa-search input { width: 100%; }
|
||
}
|
||
@media (max-width:480px) { .sa-stats{grid-template-columns:1fr} }
|
||
</style>
|
||
|
||
{{-- ─── SVG SPRITE ─── --}}
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||
<symbol id="ic-list" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/>
|
||
<line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>
|
||
</symbol>
|
||
<symbol id="ic-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 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"/>
|
||
</symbol>
|
||
<symbol id="ic-review" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||
<polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/>
|
||
</symbol>
|
||
<symbol id="ic-deleted" 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"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>
|
||
</symbol>
|
||
<symbol id="ic-calendar" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/>
|
||
<line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
|
||
</symbol>
|
||
<symbol id="ic-star" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
|
||
</symbol>
|
||
<symbol id="ic-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>
|
||
</symbol>
|
||
<symbol id="ic-trash" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>
|
||
<path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
|
||
</symbol>
|
||
<symbol id="ic-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||
</symbol>
|
||
<symbol id="ic-table" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/>
|
||
<line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="9" x2="9" y2="21"/>
|
||
</symbol>
|
||
<symbol id="ic-file" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||
<polyline points="14 2 14 8 20 8"/>
|
||
</symbol>
|
||
<symbol id="ic-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
||
<line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>
|
||
</symbol>
|
||
<symbol id="ic-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="20 6 9 17 4 12"/>
|
||
</symbol>
|
||
<symbol id="ic-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||
<polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>
|
||
</symbol>
|
||
<symbol id="ic-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>
|
||
</symbol>
|
||
<symbol id="ic-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||
</symbol>
|
||
<symbol id="ic-chart" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/>
|
||
<line x1="6" y1="20" x2="6" y2="14"/><line x1="2" y1="20" x2="22" y2="20"/>
|
||
</symbol>
|
||
<symbol id="ic-trend" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/>
|
||
<polyline points="17 6 23 6 23 12"/>
|
||
</symbol>
|
||
<symbol id="ic-edit" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||
</symbol>
|
||
<symbol id="ic-trophy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polyline points="8 20 12 16 16 20"/><line x1="12" y1="16" x2="12" y2="12"/>
|
||
<path d="M6 4H4a2 2 0 0 0-2 2v2a4 4 0 0 0 4 4"/><path d="M18 4h2a2 2 0 0 1 2 2v2a4 4 0 0 1-4 4"/>
|
||
<rect x="6" y="2" width="12" height="8" rx="2"/>
|
||
</symbol>
|
||
<symbol id="ic-zap" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
||
</symbol>
|
||
<symbol id="ic-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="3"/>
|
||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||
</symbol>
|
||
</svg>
|
||
|
||
<div class="sa-root">
|
||
|
||
{{-- PAGE HEADER --}}
|
||
<div class="sa-page-header">
|
||
<div>
|
||
<div class="sa-page-eyebrow">
|
||
<div class="sa-page-eyebrow-line"></div>
|
||
Panel Kendali Sistem
|
||
</div>
|
||
<div class="sa-page-title">Dashboard Super Admin</div>
|
||
<div class="sa-page-sub">Pemantauan terpusat seluruh aktivitas dan data instansi</div>
|
||
</div>
|
||
<div class="sa-header-right">
|
||
<div class="sa-date-chip">
|
||
<svg width="13" height="13" style="color:var(--text-muted)"><use href="#ic-calendar"/></svg>
|
||
{{ \Carbon\Carbon::now()->locale('id')->translatedFormat('l, d F Y') }}
|
||
</div>
|
||
<div class="sa-superadmin-badge">
|
||
<span class="sa-badge-dot"></span>
|
||
<svg width="12" height="12"><use href="#ic-star"/></svg>
|
||
Super Admin
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- FLASH MESSAGES --}}
|
||
@if(session('success'))
|
||
<div class="alert-session success">
|
||
<svg width="16" height="16" style="flex-shrink:0"><use href="#ic-check"/></svg>
|
||
{{ session('success') }}
|
||
</div>
|
||
@endif
|
||
@if(session('error'))
|
||
<div class="alert-session error">
|
||
<svg width="16" height="16" style="flex-shrink:0"><use href="#ic-alert"/></svg>
|
||
{{ session('error') }}
|
||
</div>
|
||
@endif
|
||
|
||
{{-- STATS --}}
|
||
<div class="sa-stats">
|
||
<div class="stat-card gold">
|
||
<div class="stat-card-glow"></div>
|
||
<div class="stat-icon-wrap"><svg width="20" height="20"><use href="#ic-list"/></svg></div>
|
||
<div class="stat-label">Total Aktivitas</div>
|
||
<div class="stat-num gold">{{ number_format($stats['total'] ?? 0) }}</div>
|
||
<div class="stat-sub">Seluruh kegiatan terdaftar</div>
|
||
</div>
|
||
<div class="stat-card blue">
|
||
<div class="stat-card-glow"></div>
|
||
<div class="stat-icon-wrap"><svg width="20" height="20"><use href="#ic-users"/></svg></div>
|
||
<div class="stat-label">User Terdaftar</div>
|
||
<div class="stat-num blue">{{ number_format($stats['users'] ?? 0) }}</div>
|
||
<div class="stat-sub">Akun aktif di sistem</div>
|
||
</div>
|
||
<div class="stat-card green">
|
||
<div class="stat-card-glow"></div>
|
||
<div class="stat-icon-wrap"><svg width="20" height="20"><use href="#ic-review"/></svg></div>
|
||
<div class="stat-label">Perlu Tinjauan</div>
|
||
<div class="stat-num green">{{ number_format($stats['proses'] ?? 0) }}</div>
|
||
<div class="stat-sub">Kegiatan belum selesai</div>
|
||
</div>
|
||
<div class="stat-card red">
|
||
<div class="stat-card-glow"></div>
|
||
<div class="stat-icon-wrap"><svg width="20" height="20"><use href="#ic-deleted"/></svg></div>
|
||
<div class="stat-label">Laporan Dihapus</div>
|
||
<div class="stat-num red">{{ number_format($stats['deleted'] ?? 0) }}</div>
|
||
<div class="stat-sub">Total dihapus bulan ini</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- WEEKLY ACTIVITY CHART --}}
|
||
<div class="sa-chart-wrap">
|
||
<div class="sa-chart-head">
|
||
<div class="sa-chart-title">
|
||
<div class="sa-chart-title-icon">
|
||
<svg width="15" height="15"><use href="#ic-trend"/></svg>
|
||
</div>
|
||
Grafik Aktivitas Mingguan
|
||
</div>
|
||
<div class="chart-filter-tabs">
|
||
<button class="chart-tab active" onclick="switchChart('week', this)">Minggu Ini</button>
|
||
<button class="chart-tab" onclick="switchChart('month', this)">Bulan Ini</button>
|
||
<button class="chart-tab" onclick="switchChart('year', this)">Tahun Ini</button>
|
||
</div>
|
||
</div>
|
||
<div class="chart-body">
|
||
<div class="chart-canvas-wrap">
|
||
<div class="chart-y-labels" id="chartYLabels">
|
||
<span>0</span><span>0</span><span>0</span><span>0</span><span>0</span>
|
||
</div>
|
||
<div class="chart-bars-area">
|
||
<div class="chart-grid-lines" id="chartGridLines">
|
||
<div class="chart-grid-line"></div>
|
||
<div class="chart-grid-line"></div>
|
||
<div class="chart-grid-line"></div>
|
||
<div class="chart-grid-line"></div>
|
||
<div class="chart-grid-line"></div>
|
||
</div>
|
||
<div class="chart-bars" id="chartBars"></div>
|
||
</div>
|
||
</div>
|
||
<div class="chart-summary" id="chartSummary"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sa-main">
|
||
|
||
{{-- TABLE --}}
|
||
<div class="sa-table-wrap">
|
||
<div class="sa-table-head">
|
||
<div class="sa-table-title">
|
||
<div class="sa-table-title-icon">
|
||
<svg width="15" height="15"><use href="#ic-table"/></svg>
|
||
</div>
|
||
Manajemen Kegiatan Terkini
|
||
</div>
|
||
<div class="sa-table-head-right">
|
||
<select class="filter-select" id="statusFilter" onchange="filterTable()">
|
||
<option value="">Semua Status</option>
|
||
<option value="selesai">Selesai</option>
|
||
<option value="on_progres">Proses</option>
|
||
<option value="terlambat">Terlambat</option>
|
||
</select>
|
||
<div class="sa-search">
|
||
<svg width="14" height="14" style="color:var(--text-muted);flex-shrink:0"><use href="#ic-search"/></svg>
|
||
<input type="text" id="searchInput" placeholder="Cari kegiatan atau petugas..." onkeyup="filterTable()">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="overflow-x: auto; width: 100%;">
|
||
<table class="sa-tbl" style="min-width: 600px;">
|
||
<thead>
|
||
<tr>
|
||
<th>Petugas</th>
|
||
<th>Kegiatan</th>
|
||
<th>Tanggal</th>
|
||
<th>Status</th>
|
||
<th style="text-align:center;">Aksi</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="kegiatanTable">
|
||
@forelse($activities as $act)
|
||
@php
|
||
$isOverdueRow = !empty($act->tanggal_selesai) && \Carbon\Carbon::parse($act->tanggal_selesai)->isPast() && $act->status !== 'selesai' && $act->status !== 'telah_ditinjau';
|
||
$rowStatus = $isOverdueRow ? 'terlambat' : $act->status;
|
||
@endphp
|
||
<tr data-status="{{ $rowStatus }}">
|
||
<td>
|
||
<div class="user-cell">
|
||
@php
|
||
$petugasList = collect($act->petugas);
|
||
$firstPetugas = $petugasList->first();
|
||
$petugasName = $firstPetugas['username'] ?? $firstPetugas['name'] ?? 'Belum ada petugas';
|
||
$petugasJabatan = $firstPetugas['jabatan'] ?? 'Petugas';
|
||
$petugasCount = $petugasList->count();
|
||
|
||
$displayName = $petugasName;
|
||
if ($petugasCount > 1) {
|
||
$displayName .= ' (+' . ($petugasCount - 1) . ' lainnya)';
|
||
}
|
||
|
||
$initials = strtoupper(substr($petugasName, 0, 2));
|
||
if ($initials == 'BE') $initials = '??';
|
||
@endphp
|
||
<div class="user-avatar" title="{{ $petugasList->pluck('username')->join(', ') }}">
|
||
{{ $initials }}
|
||
</div>
|
||
<div>
|
||
<div class="user-name" title="{{ $petugasList->pluck('username')->join(', ') }}">{{ $displayName }}</div>
|
||
<div class="user-role">{{ $petugasJabatan }}</div>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td><span class="activity-name" title="{{ $act->nama_kegiatan }}">{{ $act->nama_kegiatan }}</span></td>
|
||
<td>
|
||
<span class="date-cell">
|
||
{{ \Carbon\Carbon::parse($act->tanggal)->format('d/m/y') }}
|
||
@if(!empty($act->tanggal_selesai))
|
||
- {{ \Carbon\Carbon::parse($act->tanggal_selesai)->format('d/m/y') }}
|
||
@php
|
||
$isOverdue = \Carbon\Carbon::parse($act->tanggal_selesai)->isPast() && $act->status !== 'selesai' && $act->status !== 'telah_ditinjau';
|
||
@endphp
|
||
@if($isOverdue)
|
||
<br><span style="color:#ef4444; font-size:10px; font-weight:700;">(TERLAMBAT)</span>
|
||
@endif
|
||
@endif
|
||
</span>
|
||
</td>
|
||
<td>
|
||
@php
|
||
$isOverdue = !empty($act->tanggal_selesai) && \Carbon\Carbon::parse($act->tanggal_selesai)->isPast() && $act->status !== 'selesai' && $act->status !== 'telah_ditinjau';
|
||
@endphp
|
||
<span class="status-pill {{ $act->status == 'selesai' ? 'selesai' : ($isOverdue ? 'terlambat' : 'proses') }}">
|
||
<span class="status-dot"></span>
|
||
{{ $isOverdue ? 'TERLAMBAT' : strtoupper($act->status) }}
|
||
</span>
|
||
</td>
|
||
<td style="text-align:center;">
|
||
{{-- ─── AKSI ─── --}}
|
||
<div class="actions">
|
||
|
||
{{-- ✔️ APPROVE — hanya tampil jika status bukan selesai --}}
|
||
@if($act->status != 'selesai')
|
||
<form action="{{ route('admin.superadmin.kegiatan.approve', $act->id) }}" method="POST" style="display:contents;">
|
||
@csrf
|
||
<button type="submit" class="act-btn act-edit" title="Setujui Kegiatan">
|
||
<svg width="14" height="14"><use href="#ic-check"/></svg>
|
||
</button>
|
||
</form>
|
||
@endif
|
||
|
||
{{-- 📦 ARSIPKAN --}}
|
||
@if($act->status == 'selesai')
|
||
<form action="{{ route('admin.superadmin.kegiatan.archive', $act->id) }}" method="POST" style="display:contents;">
|
||
@csrf
|
||
<button type="submit" class="act-btn act-edit" title="Arsipkan Kegiatan" style="color: #6366f1;">
|
||
<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M21 8v13H3V8M1 3h22v5H1zM10 12h4"/></svg>
|
||
</button>
|
||
</form>
|
||
@endif
|
||
|
||
{{-- 👁️ VIEW --}}
|
||
<a href="{{ route('admin.superadmin.kegiatan.show', $act->id) }}"
|
||
class="act-btn act-view" title="Tinjau Detail">
|
||
<svg width="14" height="14"><use href="#ic-eye"/></svg>
|
||
</a>
|
||
|
||
{{-- 🗑️ DELETE --}}
|
||
<button class="act-btn act-delete" title="Hapus Kegiatan"
|
||
onclick="confirmDelete('{{ $act->id }}', '{{ addslashes($act->nama_kegiatan) }}', '{{ addslashes($displayName) }}')">
|
||
<svg width="14" height="14"><use href="#ic-trash"/></svg>
|
||
</button>
|
||
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@empty
|
||
<tr>
|
||
<td colspan="5">
|
||
<div class="empty-state">
|
||
<div class="empty-state-icon">
|
||
<svg width="40" height="40"><use href="#ic-file"/></svg>
|
||
</div>
|
||
<p>Belum ada kegiatan yang terdaftar dalam sistem</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="sa-pagination">
|
||
<span class="page-info">
|
||
Menampilkan {{ $activities->firstItem() ?? 0 }}–{{ $activities->lastItem() ?? 0 }}
|
||
dari {{ $activities->total() ?? 0 }} kegiatan
|
||
</span>
|
||
<div class="page-btns">
|
||
@if($activities->onFirstPage())
|
||
<button class="pg-btn" disabled>‹</button>
|
||
@else
|
||
<a href="{{ $activities->previousPageUrl() }}" class="pg-btn">‹</a>
|
||
@endif
|
||
|
||
@for($i = max(1, $activities->currentPage()-2); $i <= min($activities->lastPage(), $activities->currentPage()+2); $i++)
|
||
<a href="{{ $activities->url($i) }}"
|
||
class="pg-btn {{ $activities->currentPage() == $i ? 'active' : '' }}">
|
||
{{ $i }}
|
||
</a>
|
||
@endfor
|
||
|
||
@if($activities->hasMorePages())
|
||
<a href="{{ $activities->nextPageUrl() }}" class="pg-btn">›</a>
|
||
@else
|
||
<button class="pg-btn" disabled>›</button>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- SIDEBAR --}}
|
||
<div class="sa-sidebar">
|
||
|
||
{{-- LOG AKTIVITAS --}}
|
||
<div class="side-card">
|
||
<div class="side-title"><span class="side-title-bar"></span>Log Aktivitas</div>
|
||
@forelse($logs ?? [] as $log)
|
||
<div class="activity-log-item">
|
||
<div><div class="log-dot {{ $log->type ?? 'blue' }}"></div><div class="log-line"></div></div>
|
||
<div>
|
||
<div class="log-text">{!! $log->message !!}</div>
|
||
<div class="log-time">{{ \Carbon\Carbon::parse($log->created_at)->diffForHumans() }}</div>
|
||
</div>
|
||
</div>
|
||
@empty
|
||
<div class="activity-log-item">
|
||
<div><div class="log-dot blue"></div><div class="log-line"></div></div>
|
||
<div><div class="log-text">Sistem <strong>berjalan normal</strong></div><div class="log-time">Baru saja</div></div>
|
||
</div>
|
||
<div class="activity-log-item">
|
||
<div><div class="log-dot green"></div><div class="log-line"></div></div>
|
||
<div><div class="log-text"><strong>{{ $stats['users'] ?? 0 }}</strong> pengguna aktif terdaftar</div><div class="log-time">Pembaruan terakhir</div></div>
|
||
</div>
|
||
<div class="activity-log-item">
|
||
<div><div class="log-dot amber"></div><div class="log-line"></div></div>
|
||
<div><div class="log-text"><strong>{{ $stats['proses'] ?? 0 }}</strong> kegiatan perlu ditinjau</div><div class="log-time">Saat ini</div></div>
|
||
</div>
|
||
<div class="activity-log-item">
|
||
<div><div class="log-dot purple"></div></div>
|
||
<div><div class="log-text">Super Admin <strong>masuk sistem</strong></div><div class="log-time">Sesi ini</div></div>
|
||
</div>
|
||
@endforelse
|
||
</div>
|
||
|
||
{{-- TOP PETUGAS --}}
|
||
<div class="side-card">
|
||
<div class="side-title"><span class="side-title-bar"></span>Top Petugas Aktif</div>
|
||
@php
|
||
$topUsers = $topUsers ?? collect([]);
|
||
$maxCount = $topUsers->max('activities_count') ?: 1;
|
||
@endphp
|
||
@forelse($topUsers as $i => $u)
|
||
<div class="top-user-item">
|
||
<div class="top-user-rank {{ ['r1','r2','r3'][$i] ?? 'rn' }}">{{ $i+1 }}</div>
|
||
<div class="top-user-av">{{ strtoupper(substr($u->name, 0, 2)) }}</div>
|
||
<div class="top-user-info">
|
||
<div class="top-user-name">{{ $u->name }}</div>
|
||
<div class="top-user-count">{{ $u->activities_count }} kegiatan</div>
|
||
</div>
|
||
<div class="top-user-bar">
|
||
<div class="top-user-fill" style="width:{{ round(($u->activities_count/$maxCount)*100) }}%"></div>
|
||
</div>
|
||
</div>
|
||
@empty
|
||
<p style="font-size:12px;color:var(--text-faint);text-align:center;padding:1rem 0">Data belum tersedia</p>
|
||
@endforelse
|
||
</div>
|
||
|
||
{{-- STATUS SISTEM --}}
|
||
<div class="side-card">
|
||
<div class="side-title"><span class="side-title-bar"></span>Status Sistem</div>
|
||
<div class="system-bar">
|
||
<span class="sys-label">Server Load</span>
|
||
<span class="sys-val">{{ $system['server'] ?? 34 }}%</span>
|
||
</div>
|
||
<div class="sys-track">
|
||
<div class="sys-fill" style="width:{{ $system['server'] ?? 34 }}%;background:linear-gradient(90deg,#1a4fd6,#3b82f6)"></div>
|
||
</div>
|
||
<div class="system-bar">
|
||
<span class="sys-label">Database</span>
|
||
<span class="sys-val">{{ $system['database'] ?? 61 }}%</span>
|
||
</div>
|
||
<div class="sys-track">
|
||
<div class="sys-fill" style="width:{{ $system['database'] ?? 61 }}%;background:linear-gradient(90deg,#d97706,#fbbf24)"></div>
|
||
</div>
|
||
<div class="system-bar">
|
||
<span class="sys-label">Storage</span>
|
||
<span class="sys-val">{{ $system['storage'] ?? 78 }}%</span>
|
||
</div>
|
||
<div class="sys-track" style="margin-bottom:8px">
|
||
<div class="sys-fill" style="width:{{ $system['storage'] ?? 78 }}%;background:linear-gradient(90deg,#dc2626,#f87171)"></div>
|
||
</div>
|
||
<div class="sys-status-row">
|
||
<div class="sys-status-dot"></div>
|
||
Semua layanan berjalan normal
|
||
</div>
|
||
</div>
|
||
|
||
{{-- AKSI CEPAT --}}
|
||
<div class="side-card">
|
||
<div class="side-title"><span class="side-title-bar"></span>Aksi Cepat</div>
|
||
<button type="button" class="quick-action" style="width:100%; text-align:left; font-family:inherit; margin-bottom:0;" onclick="openExportModal()">
|
||
<span class="qa-icon"><svg width="14" height="14"><use href="#ic-download"/></svg></span>
|
||
<span>Ekspor Laporan Excel</span>
|
||
</button>
|
||
<a href="{{ route('admin.superadmin.user.index') }}" class="quick-action">
|
||
<span class="qa-icon"><svg width="14" height="14"><use href="#ic-user"/></svg></span>
|
||
<span>Manajemen Pengguna</span>
|
||
</a>
|
||
<a href="{{ route('admin.superadmin.user.index') }}?role=all" class="quick-action">
|
||
<span class="qa-icon"><svg width="14" height="14"><use href="#ic-lock"/></svg></span>
|
||
<span>Kelola Hak Akses</span>
|
||
</a>
|
||
<a href="{{ route('admin.superadmin.arsip') }}" class="quick-action">
|
||
<span class="qa-icon"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M21 8v13H3V8M1 3h22v5H1zM10 12h4"/></svg></span>
|
||
<span>Lihat Arsip Laporan</span>
|
||
</a>
|
||
<a href="{{ route('settings') }}" class="quick-action">
|
||
<span class="qa-icon"><svg width="14" height="14"><use href="#ic-settings"/></svg></span>
|
||
<span>Pengaturan Sistem</span>
|
||
</a>
|
||
<a href="#" class="quick-action">
|
||
<span class="qa-icon"><svg width="14" height="14"><use href="#ic-chart"/></svg></span>
|
||
<span>Lihat Analitik Lengkap</span>
|
||
</a>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- MODAL HAPUS KEGIATAN --}}
|
||
<div class="modal-overlay" id="deleteModal">
|
||
<div class="modal-box">
|
||
<div class="modal-header">
|
||
<div class="modal-icon"><svg width="22" height="22"><use href="#ic-alert"/></svg></div>
|
||
<h3>Konfirmasi Penghapusan</h3>
|
||
</div>
|
||
<p class="modal-desc">
|
||
Anda akan menghapus kegiatan <strong id="del-name"></strong> milik petugas <strong id="del-user"></strong>.
|
||
Tindakan ini <strong>tidak dapat dibatalkan</strong> dan akan tercatat dalam audit log sistem.
|
||
</p>
|
||
<div class="modal-info-row">
|
||
<div class="modal-info-chip">
|
||
<svg width="12" height="12" style="color:var(--red)"><use href="#ic-alert"/></svg>
|
||
Aksi Permanen
|
||
</div>
|
||
<div class="modal-info-chip">
|
||
<svg width="12" height="12" style="color:var(--blue-mid)"><use href="#ic-star"/></svg>
|
||
Hak: Super Admin
|
||
</div>
|
||
</div>
|
||
<div class="modal-label">Alasan Penghapusan <span style="color:var(--red)">*</span></div>
|
||
<form id="deleteForm" method="POST">
|
||
@csrf
|
||
@method('DELETE')
|
||
<textarea name="alasan_hapus" class="modal-textarea"
|
||
placeholder="Contoh: Laporan duplikat, data tidak valid, atau entri salah input..." required minlength="10"></textarea>
|
||
<div class="modal-actions">
|
||
<button type="button" class="btn-cancel" onclick="closeDeleteModal()">Batal</button>
|
||
<button type="submit" class="btn-confirm">
|
||
<svg width="14" height="14" style="margin-right:6px;vertical-align:middle"><use href="#ic-trash"/></svg>
|
||
Hapus Permanen
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- MODAL EKSPOR --}}
|
||
<div class="modal-overlay" id="exportModal">
|
||
<div class="modal-box">
|
||
<div class="modal-header">
|
||
<div class="modal-icon" style="background:var(--blue-ultra); border-color:var(--blue-pale); color:var(--blue-mid);"><svg width="22" height="22"><use href="#ic-download"/></svg></div>
|
||
<h3>Ekspor Laporan Kegiatan</h3>
|
||
</div>
|
||
<p class="modal-desc" style="background:var(--blue-ultra); border-color:var(--blue-pale); color:var(--text-secondary);">
|
||
Pilih kolom yang ingin Anda masukkan ke dalam file Excel.
|
||
</p>
|
||
<form id="exportForm" method="POST" action="{{ route('admin.superadmin.export') }}">
|
||
@csrf
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 20px;">
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="nomor_surat" checked> Nomor Surat
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="kegiatan" checked> Nama Kegiatan
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="lokasi" checked> Lokasi
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="keterangan" checked> Deskripsi
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="tanggal_mulai" checked> Tanggal Pelaksanaan
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="status" checked> Status
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="status_note" checked> Catatan Status
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="petugas" checked> Petugas
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="pembuat" checked> Dibuat Oleh
|
||
</label>
|
||
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-primary); cursor:pointer;">
|
||
<input type="checkbox" name="columns[]" value="dokumentasi" checked> Dokumentasi
|
||
</label>
|
||
</div>
|
||
|
||
<div class="modal-actions">
|
||
<button type="button" class="btn-cancel" onclick="closeExportModal()">Batal</button>
|
||
<button type="submit" class="btn-confirm" style="background:linear-gradient(135deg,var(--blue-mid),#1a4fd6); box-shadow:0 4px 15px rgba(37,99,235,0.28);" onclick="closeExportModal()">
|
||
<svg width="14" height="14" style="margin-right:6px;vertical-align:middle"><use href="#ic-download"/></svg>
|
||
Unduh Excel
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// ═══════════════════════════════════════════
|
||
// CHART DATA — dari PHP ke JavaScript
|
||
// ═══════════════════════════════════════════
|
||
@php
|
||
$cdWeekLabels = $chartData['week']['labels'] ?? ['Sen','Sel','Rab','Kam','Jum','Sab','Min'];
|
||
$cdWeekValues = $chartData['week']['values'] ?? [0,0,0,0,0,0,0];
|
||
$cdWeekToday = $chartData['week']['todayIndex'] ?? (date('N') - 1);
|
||
$cdMonthLabels = $chartData['month']['labels'] ?? [];
|
||
$cdMonthValues = $chartData['month']['values'] ?? [];
|
||
$cdMonthToday = $chartData['month']['todayIndex'] ?? -1;
|
||
$cdYearLabels = $chartData['year']['labels'] ?? ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Ags','Sep','Okt','Nov','Des'];
|
||
$cdYearValues = $chartData['year']['values'] ?? [0,0,0,0,0,0,0,0,0,0,0,0];
|
||
$cdYearToday = $chartData['year']['todayIndex'] ?? (date('n') - 1);
|
||
@endphp
|
||
const chartData = {
|
||
week: {
|
||
labels: @json($cdWeekLabels),
|
||
values: @json($cdWeekValues),
|
||
todayIndex: {{ $cdWeekToday }},
|
||
},
|
||
month: {
|
||
labels: @json($cdMonthLabels),
|
||
values: @json($cdMonthValues),
|
||
todayIndex: {{ $cdMonthToday }},
|
||
},
|
||
year: {
|
||
labels: @json($cdYearLabels),
|
||
values: @json($cdYearValues),
|
||
todayIndex: {{ $cdYearToday }},
|
||
}
|
||
};
|
||
|
||
let currentMode = 'week';
|
||
|
||
function renderChart(mode) {
|
||
currentMode = mode;
|
||
const data = chartData[mode];
|
||
const labels = data.labels;
|
||
const values = data.values;
|
||
const todayIdx = data.todayIndex;
|
||
const maxVal = Math.max(...values, 1);
|
||
|
||
// Y-axis labels
|
||
const steps = 4;
|
||
const yLabels = document.getElementById('chartYLabels');
|
||
yLabels.innerHTML = '';
|
||
for (let i = steps; i >= 0; i--) {
|
||
const lbl = document.createElement('span');
|
||
lbl.textContent = Math.round((maxVal / steps) * i);
|
||
yLabels.appendChild(lbl);
|
||
}
|
||
|
||
// Bars
|
||
const barsEl = document.getElementById('chartBars');
|
||
barsEl.innerHTML = '';
|
||
|
||
values.forEach((val, i) => {
|
||
const pct = maxVal > 0 ? (val / maxVal) * 100 : 0;
|
||
const isToday = i === todayIdx;
|
||
|
||
const col = document.createElement('div');
|
||
col.className = 'chart-bar-col';
|
||
|
||
const wrap = document.createElement('div');
|
||
wrap.className = 'chart-bar-wrap';
|
||
|
||
const bar = document.createElement('div');
|
||
bar.className = 'chart-bar' + (isToday ? ' active-day' : '');
|
||
bar.style.height = '0%';
|
||
bar.style.width = '100%';
|
||
|
||
const tooltip = document.createElement('div');
|
||
tooltip.className = 'chart-bar-tooltip';
|
||
tooltip.textContent = val + ' kegiatan';
|
||
bar.appendChild(tooltip);
|
||
|
||
bar.addEventListener('click', () => {
|
||
document.querySelectorAll('.chart-bar').forEach(b => b.classList.remove('active-day'));
|
||
bar.classList.add('active-day');
|
||
});
|
||
|
||
wrap.appendChild(bar);
|
||
col.appendChild(wrap);
|
||
|
||
const lbl = document.createElement('div');
|
||
lbl.className = 'chart-bar-label' + (isToday ? ' today-label' : '');
|
||
lbl.textContent = labels[i] || '';
|
||
col.appendChild(lbl);
|
||
|
||
barsEl.appendChild(col);
|
||
|
||
requestAnimationFrame(() => {
|
||
setTimeout(() => {
|
||
bar.style.height = Math.max(pct, val > 0 ? 2 : 0) + '%';
|
||
}, 60 + i * 60);
|
||
});
|
||
});
|
||
|
||
// Summary
|
||
const total = values.reduce((a, b) => a + b, 0);
|
||
const avg = values.length ? Math.round(total / values.length) : 0;
|
||
const peak = Math.max(...values);
|
||
const peakDay = labels[values.indexOf(peak)] || '-';
|
||
|
||
const summaryEl = document.getElementById('chartSummary');
|
||
summaryEl.innerHTML = `
|
||
<div class="chart-summary-item">
|
||
<div class="cs-dot" style="background:linear-gradient(135deg,#3b82f6,#1a4fd6)"></div>
|
||
<span class="cs-label">Total</span>
|
||
<span class="cs-val">${total}</span>
|
||
</div>
|
||
<div class="chart-summary-item">
|
||
<div class="cs-dot" style="background:linear-gradient(135deg,#34d399,#059669)"></div>
|
||
<span class="cs-label">Rata-rata</span>
|
||
<span class="cs-val">${avg}/hari</span>
|
||
</div>
|
||
<div class="chart-summary-item">
|
||
<div class="cs-dot" style="background:linear-gradient(135deg,#a78bfa,#7c3aed)"></div>
|
||
<span class="cs-label">Tertinggi</span>
|
||
<span class="cs-val">${peak} (${peakDay})</span>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function switchChart(mode, btn) {
|
||
document.querySelectorAll('.chart-tab').forEach(t => t.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
renderChart(mode);
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
renderChart('week');
|
||
});
|
||
|
||
// ═══════════════════════════════════════════
|
||
// TABLE FILTER
|
||
// ═══════════════════════════════════════════
|
||
function filterTable() {
|
||
const q = document.getElementById('searchInput').value.toLowerCase();
|
||
const statusFilter = document.getElementById('statusFilter').value.toLowerCase();
|
||
|
||
document.querySelectorAll('#kegiatanTable tr').forEach(row => {
|
||
const text = row.textContent.toLowerCase();
|
||
const status = (row.dataset.status || '').toLowerCase();
|
||
const matchText = q === '' || text.includes(q);
|
||
|
||
let matchStatus = false;
|
||
if (statusFilter === '') {
|
||
matchStatus = true;
|
||
} else {
|
||
matchStatus = status === statusFilter;
|
||
}
|
||
|
||
row.style.display = (matchText && matchStatus) ? '' : 'none';
|
||
});
|
||
}
|
||
|
||
// ═══════════════════════════════════════════
|
||
// DELETE MODAL
|
||
// ═══════════════════════════════════════════
|
||
function confirmDelete(id, name, userName) {
|
||
document.getElementById('del-name').textContent = name;
|
||
document.getElementById('del-user').textContent = userName || 'Unknown';
|
||
document.getElementById('deleteForm').action = `/superadmin/kegiatan/${id}`;
|
||
document.getElementById('deleteModal').classList.add('active');
|
||
}
|
||
function closeDeleteModal() {
|
||
document.getElementById('deleteModal').classList.remove('active');
|
||
}
|
||
document.getElementById('deleteModal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeDeleteModal();
|
||
});
|
||
document.addEventListener('keydown', e => {
|
||
if (e.key === 'Escape') closeDeleteModal();
|
||
});
|
||
|
||
// ═══════════════════════════════════════════
|
||
// EXPORT MODAL
|
||
// ═══════════════════════════════════════════
|
||
function openExportModal() {
|
||
document.getElementById('exportModal').classList.add('active');
|
||
}
|
||
function closeExportModal() {
|
||
document.getElementById('exportModal').classList.remove('active');
|
||
}
|
||
document.getElementById('exportModal').addEventListener('click', function(e) {
|
||
if (e.target === this) closeExportModal();
|
||
});
|
||
document.addEventListener('keydown', e => {
|
||
if (e.key === 'Escape') closeExportModal();
|
||
});
|
||
|
||
// ═══════════════════════════════════════════
|
||
// AUTO-DISMISS ALERTS
|
||
// ═══════════════════════════════════════════
|
||
setTimeout(() => {
|
||
document.querySelectorAll('.alert-session').forEach(el => {
|
||
el.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
|
||
el.style.opacity = '0';
|
||
el.style.transform = 'translateY(-8px)';
|
||
setTimeout(() => el.remove(), 500);
|
||
});
|
||
}, 4000);
|
||
</script>
|
||
|
||
@endsection |