Initial commit for combined ProjectKP

This commit is contained in:
raditarahman5-cloud
2026-06-28 15:14:51 +07:00
commit ac6c3473d4
203 changed files with 41848 additions and 0 deletions
@@ -0,0 +1,60 @@
@extends('admin.layouts.app')
@section('content')
{{-- Flex container untuk menarik konten ke tengah horizontal --}}
<div class="flex justify-center items-center w-full pb-20">
<div class="w-full max-w-lg"> {{-- max-w-lg membatasi lebar form supaya simetris --}}
<div class="mb-8 text-center">
<h1 class="text-3xl font-extrabold text-[#0D215C] tracking-tight">Tambah Petugas Baru</h1>
<p class="text-slate-500 mt-2 font-medium">Buat akun untuk pengawas lapangan</p>
</div>
<div class="bg-white rounded-[2.5rem] shadow-2xl shadow-slate-200/60 border border-slate-100 overflow-hidden">
<div class="p-8 md:p-12">
<form action="{{ route('admin.users.store') }}" method="POST" class="space-y-6">
@csrf
<div class="space-y-2">
<label class="text-[11px] font-black text-slate-400 uppercase tracking-[2px] ml-1">Nama Lengkap</label>
<input type="text" name="name"
class="w-full px-6 py-4 bg-[#F2F5FB] border-none rounded-2xl focus:ring-4 focus:ring-[#0D215C]/10 transition-all outline-none text-slate-700 placeholder:text-slate-300"
placeholder="Contoh: Budi Santoso" required>
</div>
<div class="space-y-2">
<label class="text-[11px] font-black text-slate-400 uppercase tracking-[2px] ml-1">Username</label>
<input type="text" name="username"
class="w-full px-6 py-4 bg-[#F2F5FB] border-none rounded-2xl focus:ring-4 focus:ring-[#0D215C]/10 transition-all outline-none text-slate-700 placeholder:text-slate-300"
placeholder="budi_petugas" required>
</div>
<div class="space-y-2">
<label class="text-[11px] font-black text-slate-400 uppercase tracking-[2px] ml-1">Password</label>
<input type="password" name="password"
class="w-full px-6 py-4 bg-[#F2F5FB] border-none rounded-2xl focus:ring-4 focus:ring-[#0D215C]/10 transition-all outline-none text-slate-700 placeholder:text-slate-300"
placeholder="Masukkan password" required>
</div>
<div class="pt-6">
<button type="submit"
class="w-full bg-[#0D215C] text-white font-bold py-5 rounded-2xl shadow-xl shadow-blue-900/20 hover:bg-[#081a45] transition-all transform active:scale-95">
Simpan Akun Petugas
</button>
</div>
</form>
</div>
</div>
<div class="mt-8 text-center">
<a href="{{ route('admin.users') }}" class="text-sm font-bold text-slate-400 hover:text-[#0D215C] transition-colors flex items-center justify-center">
<i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> KEMBALI KE DAFTAR
</a>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,913 @@
@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') }}
&nbsp;·&nbsp; 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 &rarr;</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 }} &bull; {{ $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
@@ -0,0 +1,212 @@
@extends('admin.layouts.app')
@section('content')
<style>
@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; }
body, * { font-family: 'Plus Jakarta Sans', sans-serif; }
.eu-wrap {
padding: 2rem 2.5rem 3rem;
max-width: 640px;
margin: 0 auto;
}
.eu-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 2rem;
}
.eu-title { font-size: 22px; font-weight: 700; color: #0D215C; }
.eu-sub { font-size: 13px; color: #94a3b8; margin-top: 4px; }
.btn-back {
padding: 8px 18px;
border-radius: 10px;
background: #eef2ff;
color: #3730a3;
font-size: 13px;
font-weight: 600;
text-decoration: none;
transition: .2s;
}
.btn-back:hover { background: #dbeafe; color: #1d4ed8; }
.eu-card {
background: #fff;
border: 1px solid #edf1f7;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(13,33,92,.05);
}
.eu-card-header {
padding: 1.2rem 1.5rem;
border-bottom: 1px solid #f1f5f9;
background: #f8fafc;
display: flex;
align-items: center;
gap: 10px;
}
.eu-card-header h3 { font-size: 14px; font-weight: 700; color: #0D215C; margin: 0; }
.eu-body { padding: 1.5rem; }
.form-group { margin-bottom: 1.2rem; }
.form-label {
display: block;
font-size: 13px;
font-weight: 700;
color: #374151;
margin-bottom: 7px;
}
.form-input, .form-select-field {
width: 100%;
padding: 11px 14px;
border: 1.5px solid #e2e8f0;
border-radius: 12px;
font-size: 14px;
outline: none;
color: #0f172a;
background: #fff;
transition: border .2s, box-shadow .2s;
}
.form-input:focus, .form-select-field:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59,130,246,.1);
}
.form-hint {
font-size: 11px;
color: #94a3b8;
margin-top: 5px;
}
.pw-wrap { position: relative; }
.pw-wrap .form-input { padding-right: 44px; }
.pw-toggle {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
color: #94a3b8;
padding: 4px;
display: flex;
align-items: center;
}
.pw-toggle:hover { color: #3b82f6; }
.btn-submit {
width: 100%;
padding: 13px;
margin-top: .5rem;
background: #0D215C;
color: #fff;
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 700;
cursor: pointer;
transition: .2s;
box-shadow: 0 4px 14px rgba(13,33,92,.2);
}
.btn-submit:hover { background: #1e3a8a; transform: translateY(-1px); }
.alert-error {
background: #fee2e2;
color: #b91c1c;
padding: 12px 16px;
border-radius: 10px;
margin-bottom: 1rem;
font-size: 13px;
}
</style>
<div class="eu-wrap">
<div class="eu-header">
<div>
<div class="eu-title">Edit Pengguna</div>
<div class="eu-sub">Perbarui informasi akun pengguna</div>
</div>
<a href="{{ route('admin.users') }}" class="btn-back"> Kembali</a>
</div>
<div class="eu-card">
<div class="eu-card-header">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0D215C" 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"/></svg>
<h3>Form Edit Pengguna</h3>
</div>
<div class="eu-body">
@if($errors->any())
<div class="alert-error">
@foreach($errors->all() as $e) <div> {{ $e }}</div> @endforeach
</div>
@endif
@if(session('error'))
<div class="alert-error"> {{ session('error') }}</div>
@endif
<form action="{{ route('admin.users.update', $user->id) }}" method="POST">
@csrf
<div class="form-group">
<label class="form-label" for="name">Nama Lengkap</label>
<input type="text" id="name" name="name" class="form-input"
value="{{ old('name', $user->name) }}" placeholder="Nama lengkap pengguna" required>
</div>
<div class="form-group">
<label class="form-label" for="username">Username</label>
<input type="text" id="username" name="username" class="form-input"
value="{{ old('username', $user->username) }}" placeholder="Username login" required>
</div>
<div class="form-group">
<label class="form-label" for="jabatan">Jabatan</label>
<input type="text" id="jabatan" name="jabatan" class="form-input"
value="{{ old('jabatan', $user->jabatan ?? '') }}" placeholder="Jabatan pengguna (opsional)">
</div>
<div class="form-group">
<label class="form-label" for="role">Role / Hak Akses</label>
<select id="role" name="role" class="form-select-field">
<option value="user" {{ old('role', $user->role) == 'user' ? 'selected' : '' }}>Petugas Lapangan</option>
<option value="admin" {{ old('role', $user->role) == 'admin' ? 'selected' : '' }}>Administrator (Kasi)</option>
<option value="superadmin" {{ old('role', $user->role) == 'superadmin' ? 'selected' : '' }}>Super Admin (Kabid)</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="password">Password Baru</label>
<div class="pw-wrap">
<input type="password" id="password" name="password" class="form-input"
placeholder="Kosongkan jika tidak ingin mengubah">
<button type="button" class="pw-toggle" onclick="togglePassword('password','eyeIconPw')">
<svg id="eyeIconPw" xmlns="http://www.w3.org/2000/svg" width="18" height="18" 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"/></svg>
</button>
</div>
<div class="form-hint">Minimal 6 karakter. Kosongkan jika tidak ingin mengubah password.</div>
</div>
<button type="submit" class="btn-submit">Simpan Perubahan</button>
</form>
</div>
</div>
</div>
<script>
function togglePassword(fieldId, iconId) {
const field = document.getElementById(fieldId);
const icon = document.getElementById(iconId);
if (field.type === 'password') {
field.type = 'text';
icon.innerHTML = '<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/>';
} else {
field.type = 'password';
icon.innerHTML = '<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"/>';
}
}
</script>
@endsection
@@ -0,0 +1,242 @@
@extends('admin.layouts.app')
@section('content')
<style>
@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; }
body, * { font-family: 'Plus Jakarta Sans', sans-serif; }
.detail-wrap {
padding: 2rem 2.5rem 3rem;
max-width: 900px;
margin: 0 auto;
}
.detail-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 2rem;
}
.detail-title { font-size: 22px; font-weight: 700; color: #0D215C; }
.detail-sub { font-size: 13px; color: #94a3b8; margin-top: 4px; }
.btn-back {
padding: 8px 18px;
border-radius: 10px;
background: #eef2ff;
color: #3730a3;
font-size: 13px;
font-weight: 600;
text-decoration: none;
transition: all .2s;
}
.btn-back:hover { background: #dbeafe; color: #1d4ed8; }
.detail-card {
background: #fff;
border: 1px solid #edf1f7;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(13,33,92,.05);
margin-bottom: 1.5rem;
}
.detail-card-header {
padding: 1.2rem 1.5rem;
border-bottom: 1px solid #f1f5f9;
background: #f8fafc;
display: flex;
align-items: center;
gap: 10px;
}
.detail-card-header h3 {
font-size: 14px;
font-weight: 700;
color: #0D215C;
margin: 0;
}
.detail-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
}
.detail-field {
padding: 1rem 1.5rem;
border-bottom: 1px solid #f1f5f9;
display: flex;
flex-direction: column;
gap: 4px;
}
.detail-field:nth-child(odd) { border-right: 1px solid #f1f5f9; }
.detail-field.full {
grid-column: 1 / -1;
border-right: none;
}
.df-label {
font-size: 11px;
font-weight: 700;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: .5px;
}
.df-value {
font-size: 14px;
font-weight: 600;
color: #0f172a;
}
.pill {
display: inline-flex;
font-size: 11px;
font-weight: 700;
padding: 5px 14px;
border-radius: 50px;
letter-spacing: .3px;
}
.pill-ok { background: #dcfce7; color: #15803d; }
.pill-run { background: #dbeafe; color: #1d4ed8; }
.pill-rev { background: #fef9c3; color: #92400e; }
.pill-del { background: #fee2e2; color: #b91c1c; }
.petugas-list {
padding: 1rem 1.5rem;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.petugas-chip {
display: flex;
align-items: center;
gap: 8px;
background: #f1f5f9;
border-radius: 10px;
padding: 8px 14px;
font-size: 13px;
font-weight: 500;
color: #334155;
}
.petugas-ava {
width: 28px; height: 28px;
border-radius: 8px;
background: linear-gradient(135deg, #0D215C, #2563eb);
color: #fff;
font-size: 10px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.action-bar {
display: flex;
gap: 12px;
margin-top: 1.5rem;
}
.action-btn {
padding: 10px 22px;
border-radius: 12px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
border: none;
cursor: pointer;
transition: .2s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-edit { background: #0D215C; color: #fff; box-shadow: 0 4px 12px rgba(13,33,92,.2); }
.btn-edit:hover { background: #1e3a8a; }
.btn-batal { background: #fee2e2; color: #b91c1c; }
.btn-batal:hover { background: #fecaca; }
</style>
<div class="detail-wrap">
{{-- HEADER --}}
<div class="detail-header">
<div>
<div class="detail-title">Detail Kegiatan</div>
<div class="detail-sub">Informasi lengkap penugasan lapangan</div>
</div>
<a href="{{ route('admin.kegiatan.index') }}" class="btn-back"> Kembali</a>
</div>
{{-- INFO UTAMA --}}
<div class="detail-card">
<div class="detail-card-header">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0D215C" stroke-width="2" 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"/></svg>
<h3>Informasi Penugasan</h3>
</div>
<div class="detail-grid">
<div class="detail-field">
<div class="df-label">Nomor Surat</div>
<div class="df-value">{{ $activity->nomor_surat ?? '-' }}</div>
</div>
<div class="detail-field">
<div class="df-label">Status</div>
<div class="df-value">
@php
$pillMap = [
'selesai' => ['pill-ok', 'Selesai'],
'on_progres' => ['pill-run', 'Sedang Berjalan'],
'telah_ditinjau' => ['pill-rev', 'Telah Ditinjau'],
'dibatalkan' => ['pill-del', 'Dibatalkan'],
];
$pc = $pillMap[$activity->status] ?? ['pill-run', ucfirst($activity->status)];
@endphp
<span class="pill {{ $pc[0] }}">{{ $pc[1] }}</span>
</div>
</div>
<div class="detail-field full">
<div class="df-label">Nama Kegiatan</div>
<div class="df-value">{{ $activity->nama_kegiatan }}</div>
</div>
<div class="detail-field">
<div class="df-label">Tanggal Mulai</div>
<div class="df-value">{{ \Carbon\Carbon::parse($activity->tanggal)->locale('id')->translatedFormat('d F Y') }}</div>
</div>
<div class="detail-field">
<div class="df-label">Tanggal Selesai</div>
<div class="df-value">
{{ $activity->tanggal_selesai ? \Carbon\Carbon::parse($activity->tanggal_selesai)->locale('id')->translatedFormat('d F Y') : '-' }}
</div>
</div>
<div class="detail-field">
<div class="df-label">Penanggung Jawab</div>
<div class="df-value">{{ optional($activity->user)->name ?? '-' }}</div>
</div>
<div class="detail-field">
<div class="df-label">Terakhir Diperbarui</div>
<div class="df-value">{{ \Carbon\Carbon::parse($activity->updated_at)->locale('id')->translatedFormat('d F Y, H:i') }}</div>
</div>
</div>
</div>
{{-- DAFTAR PETUGAS --}}
<div class="detail-card">
<div class="detail-card-header">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0D215C" 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"/></svg>
<h3>Petugas yang Ditugaskan</h3>
</div>
<div class="petugas-list">
@forelse($activity->petugas ?? [] as $p)
@php $nm = is_array($p) ? ($p['name'] ?? '-') : ($p->name ?? '-'); @endphp
<div class="petugas-chip">
<div class="petugas-ava">{{ strtoupper(substr($nm, 0, 2)) }}</div>
{{ $nm }}
</div>
@empty
<span style="color:#94a3b8;font-size:13px;">Belum ada petugas yang ditetapkan.</span>
@endforelse
</div>
</div>
</div>
@endsection
@@ -0,0 +1,220 @@
@extends('admin.layouts.app')
@section('content')
<style>
@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; }
body, * { font-family: 'Plus Jakarta Sans', sans-serif; }
.edit-wrap {
padding: 2rem 2.5rem 3rem;
max-width: 680px;
margin: 0 auto;
}
.edit-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 2rem;
}
.edit-title { font-size: 22px; font-weight: 700; color: #0D215C; }
.edit-sub { font-size: 13px; color: #94a3b8; margin-top: 4px; }
.btn-back {
padding: 8px 18px;
border-radius: 10px;
background: #eef2ff;
color: #3730a3;
font-size: 13px;
font-weight: 600;
text-decoration: none;
transition: all .2s;
}
.btn-back:hover { background: #dbeafe; color: #1d4ed8; }
.edit-card {
background: #fff;
border: 1px solid #edf1f7;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(13,33,92,.05);
}
.edit-card-header {
padding: 1.2rem 1.5rem;
border-bottom: 1px solid #f1f5f9;
background: #f8fafc;
display: flex;
align-items: center;
gap: 10px;
}
.edit-card-header h3 {
font-size: 14px;
font-weight: 700;
color: #0D215C;
margin: 0;
}
.edit-body { padding: 1.5rem; }
.info-row {
display: flex;
flex-direction: column;
gap: 4px;
padding: 12px 0;
border-bottom: 1px solid #f1f5f9;
margin-bottom: 4px;
}
.info-row:last-of-type { border-bottom: none; }
.info-label {
font-size: 11px;
font-weight: 700;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: .5px;
}
.info-value {
font-size: 15px;
font-weight: 600;
color: #0f172a;
}
.form-group { margin-top: 1.5rem; }
.form-label {
display: block;
font-size: 13px;
font-weight: 700;
color: #374151;
margin-bottom: 8px;
}
.form-select {
width: 100%;
padding: 12px 16px;
border: 1.5px solid #e2e8f0;
border-radius: 12px;
font-size: 14px;
outline: none;
background: #fff;
cursor: pointer;
color: #0f172a;
transition: border .2s;
appearance: auto;
}
.form-select:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59,130,246,.1); }
.status-info {
margin-top: 10px;
padding: 10px 14px;
background: #fffbeb;
border-radius: 10px;
font-size: 12px;
color: #92400e;
border: 1px solid #fde68a;
}
.btn-submit {
width: 100%;
padding: 14px;
margin-top: 1.5rem;
background: #0D215C;
color: #fff;
border: none;
border-radius: 12px;
font-size: 14px;
font-weight: 700;
cursor: pointer;
transition: .2s;
box-shadow: 0 4px 14px rgba(13,33,92,.2);
}
.btn-submit:hover { background: #1e3a8a; transform: translateY(-1px); }
.pill {
display: inline-flex;
font-size: 11px;
font-weight: 700;
padding: 4px 12px;
border-radius: 50px;
}
.pill-ok { background: #dcfce7; color: #15803d; }
.pill-run { background: #dbeafe; color: #1d4ed8; }
.pill-rev { background: #fef9c3; color: #92400e; }
.pill-del { background: #fee2e2; color: #b91c1c; }
</style>
<div class="edit-wrap">
{{-- HEADER --}}
<div class="edit-header">
<div>
<div class="edit-title">Ubah Status Kegiatan</div>
<div class="edit-sub">Perbarui status kemajuan kegiatan lapangan</div>
</div>
<a href="{{ route('admin.kegiatan.show', $activity->id) }}" class="btn-back"> Kembali</a>
</div>
{{-- FORM CARD --}}
<div class="edit-card">
<div class="edit-card-header">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0D215C" 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"/></svg>
<h3>Form Edit Status</h3>
</div>
<div class="edit-body">
{{-- ALERT --}}
@if($errors->any())
<div style="background:#fee2e2;color:#b91c1c;padding:12px 16px;border-radius:10px;margin-bottom:1rem;font-size:13px;">
@foreach($errors->all() as $e) <div> {{ $e }}</div> @endforeach
</div>
@endif
{{-- INFO KEGIATAN --}}
<div class="info-row">
<div class="info-label">Nomor Surat</div>
<div class="info-value">{{ $activity->nomor_surat ?? '-' }}</div>
</div>
<div class="info-row">
<div class="info-label">Nama Kegiatan</div>
<div class="info-value">{{ $activity->nama_kegiatan }}</div>
</div>
<div class="info-row">
<div class="info-label">Status Saat Ini</div>
<div class="info-value">
@php
$pillMap = [
'selesai' => ['pill-ok', 'Selesai'],
'on_progres' => ['pill-run', 'Sedang Berjalan'],
'telah_ditinjau' => ['pill-rev', 'Telah Ditinjau'],
'dibatalkan' => ['pill-del', 'Dibatalkan'],
];
$pc = $pillMap[$activity->status] ?? ['pill-run', ucfirst($activity->status)];
@endphp
<span class="pill {{ $pc[0] }}">{{ $pc[1] }}</span>
</div>
</div>
{{-- FORM --}}
<form action="{{ route('admin.kegiatan.update', $activity->id) }}" method="POST">
@csrf
@method('POST')
<div class="form-group">
<label class="form-label" for="status">Status Baru</label>
<select name="status" id="status" class="form-select">
<option value="on_progres" {{ $activity->status == 'on_progres' ? 'selected' : '' }}>Sedang Berjalan</option>
<option value="selesai" {{ $activity->status == 'selesai' ? 'selected' : '' }}>Selesai</option>
<option value="telah_ditinjau" {{ $activity->status == 'telah_ditinjau' ? 'selected' : '' }}>Telah Ditinjau</option>
</select>
<div class="status-info">
⚠️ Perubahan status bersifat permanen dan akan tercatat dalam riwayat sistem.
</div>
</div>
<button type="submit" class="btn-submit">Simpan Perubahan Status</button>
</form>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,329 @@
@extends('admin.layouts.app')
@section('content')
<style>
@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; }
.keg-wrap {
padding: 2rem 2.5rem 3rem;
max-width: 1440px;
margin: 0 auto;
font-family: 'Plus Jakarta Sans', sans-serif;
}
/* ── Header ─── */
.keg-topbar {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 1.75rem;
}
.keg-title { font-size: 22px; font-weight: 700; color: #0D215C; }
.keg-sub { font-size: 12.5px; color: #94a3b8; margin-top: 4px; }
.keg-btn-back {
padding: 8px 18px;
border-radius: 10px;
background: #eef2ff;
color: #3730a3;
font-size: 13px;
font-weight: 600;
text-decoration: none;
transition: all .2s;
}
.keg-btn-back:hover { background: #dbeafe; color: #1d4ed8; }
/* ── Stats Strip ─── */
.keg-stats {
display: grid;
grid-template-columns: repeat(3,1fr);
gap: 16px;
margin-bottom: 1.5rem;
}
.kstat {
background: #fff;
border: 1px solid #edf1f7;
border-radius: 16px;
padding: 1.2rem 1.4rem;
display: flex;
align-items: center;
gap: 14px;
}
.kstat-icon {
width: 42px; height: 42px;
border-radius: 12px;
display: flex; align-items: center; justify-content: center;
}
.kstat-icon.blue { background: #eff6ff; color: #3b82f6; }
.kstat-icon.green { background: #f0fdf4; color: #16a34a; }
.kstat-icon.yellow { background: #fffbeb; color: #d97706; }
.kstat-num { font-size: 28px; font-weight: 700; color: #0f172a; line-height: 1; }
.kstat-label { font-size: 12px; color: #94a3b8; margin-top: 3px; font-weight: 500; }
/* ── Search + Filter ─── */
.keg-filter-bar {
display: flex;
gap: 12px;
align-items: center;
margin-bottom: 1.2rem;
flex-wrap: wrap;
}
.keg-search {
flex: 1;
min-width: 200px;
padding: 9px 14px;
border: 1.5px solid #e2e8f0;
border-radius: 10px;
font-size: 13px;
outline: none;
transition: border .2s;
}
.keg-search:focus { border-color: #3b82f6; }
.keg-select {
padding: 9px 14px;
border: 1.5px solid #e2e8f0;
border-radius: 10px;
font-size: 13px;
outline: none;
background: #fff;
cursor: pointer;
}
.keg-filter-btn {
padding: 9px 20px;
background: #0D215C;
color: #fff;
border: none;
border-radius: 10px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: .2s;
}
.keg-filter-btn:hover { background: #1e3a8a; }
/* ── Table Card ─── */
.keg-card {
background: #fff;
border: 1px solid #edf1f7;
border-radius: 20px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(13,33,92,.04);
}
.keg-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.keg-table thead { background: #f8fafc; }
.keg-table thead th {
padding: 14px 18px;
text-align: left;
font-size: 11px;
font-weight: 700;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: .5px;
}
.keg-table tbody td {
padding: 14px 18px;
border-top: 1px solid #f1f5f9;
vertical-align: middle;
color: #334155;
}
.keg-table tbody tr:hover { background: #f8fafc; }
.pill {
font-size: 10px;
font-weight: 700;
padding: 4px 12px;
border-radius: 50px;
letter-spacing: .3px;
white-space: nowrap;
}
.pill-ok { background: #dcfce7; color: #15803d; }
.pill-run { background: #dbeafe; color: #1d4ed8; }
.pill-rev { background: #fef9c3; color: #92400e; }
.pill-del { background: #fee2e2; color: #b91c1c; }
.act-btn {
padding: 6px 14px;
border-radius: 8px;
font-size: 11px;
font-weight: 700;
text-decoration: none;
border: none;
cursor: pointer;
transition: .15s;
display: inline-flex;
align-items: center;
gap: 4px;
}
.btn-view { background: #eff6ff; color: #2563eb; }
.btn-view:hover { background: #dbeafe; }
.btn-edit-k { background: #f0fdf4; color: #15803d; }
.btn-edit-k:hover { background: #dcfce7; }
.btn-del-k { background: #fff1f2; color: #be123c; }
.btn-del-k:hover { background: #fee2e2; }
.pg-wrap { padding: 16px 18px; border-top: 1px solid #f1f5f9; }
.empty-row td {
text-align: center;
padding: 3rem;
color: #94a3b8;
font-size: 13px;
}
/* ── Responsive ─── */
@media (max-width: 768px) {
.keg-wrap { padding: 1rem; }
.keg-topbar { flex-direction: column; align-items: flex-start; gap: 12px; }
.keg-stats { grid-template-columns: 1fr; gap: 12px; }
.keg-filter-bar { flex-direction: column; align-items: stretch; }
.keg-search, .keg-select, .keg-filter-btn { width: 100%; }
}
</style>
<div class="keg-wrap">
{{-- ALERTS --}}
@if(session('success'))
<div style="background:#dcfce7;color:#166534;padding:12px 18px;border-radius:12px;margin-bottom:1.2rem;font-size:13px;font-weight:600;">
{{ session('success') }}
</div>
@endif
@if(session('error'))
<div style="background:#fee2e2;color:#b91c1c;padding:12px 18px;border-radius:12px;margin-bottom:1.2rem;font-size:13px;font-weight:600;">
{{ session('error') }}
</div>
@endif
{{-- TOPBAR --}}
<div class="keg-topbar">
<div>
<div class="keg-title">Daftar Kegiatan</div>
<div class="keg-sub">Monitor dan kelola seluruh kegiatan lapangan</div>
</div>
<a href="{{ route('admin.dashboard') }}" class="keg-btn-back"> Kembali ke Dashboard</a>
</div>
{{-- STATS --}}
@php
$totalAll = $allItems->count();
$totalSelesai = $allItems->where('status','selesai')->count();
$totalProses = $allItems->where('status','on_progres')->count();
@endphp
<div class="keg-stats">
<div class="kstat">
<div class="kstat-icon blue">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" 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"/></svg>
</div>
<div>
<div class="kstat-num">{{ $totalAll }}</div>
<div class="kstat-label">Total Kegiatan</div>
</div>
</div>
<div class="kstat">
<div class="kstat-icon green">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" 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>
<div class="kstat-num">{{ $totalSelesai }}</div>
<div class="kstat-label">Selesai</div>
</div>
</div>
<div class="kstat">
<div class="kstat-icon yellow">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
</div>
<div>
<div class="kstat-num">{{ $totalProses }}</div>
<div class="kstat-label">Sedang Berjalan</div>
</div>
</div>
</div>
{{-- FILTER BAR --}}
<form method="GET" action="{{ route('admin.kegiatan.index') }}" class="keg-filter-bar">
<input type="text" name="search" class="keg-search" placeholder="Cari nama kegiatan / penanggung jawab..." value="{{ request('search') }}">
<select name="status" class="keg-select">
<option value="semua" {{ request('status','semua')=='semua' ? 'selected':'' }}>Semua Status</option>
<option value="on_progres" {{ request('status')=='on_progres' ? 'selected':'' }}>Sedang Berjalan</option>
<option value="selesai" {{ request('status')=='selesai' ? 'selected':'' }}>Selesai</option>
<option value="telah_ditinjau" {{ request('status')=='telah_ditinjau' ? 'selected':'' }}>Telah Ditinjau</option>
</select>
<button type="submit" class="keg-filter-btn">Cari</button>
@if(request('search') || request('status'))
<a href="{{ route('admin.kegiatan.index') }}" class="keg-btn-back" style="padding:9px 16px;font-size:12px;">Reset</a>
@endif
</form>
{{-- TABLE --}}
<div class="keg-card">
<div style="overflow-x: auto; width: 100%;">
<table class="keg-table" style="min-width: 700px;">
<thead>
<tr>
<th>#</th>
<th>Nama Kegiatan</th>
<th>Penanggung Jawab</th>
<th>Tanggal</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($activities as $i => $item)
@php
$namaUser = optional($item->user)->name ?? 'User Tidak Ada';
$inisial = collect(explode(' ', $namaUser))
->take(2)
->map(fn($w) => strtoupper(substr($w,0,1)))
->join('');
$pillMap = [
'selesai' => ['pill-ok', 'Selesai'],
'on_progres' => ['pill-run', 'Berjalan'],
'telah_ditinjau' => ['pill-rev', 'Ditinjau'],
'dibatalkan' => ['pill-del', 'Dibatalkan'],
];
$pc = $pillMap[$item->status] ?? ['pill-run', ucfirst($item->status)];
@endphp
<tr>
<td>{{ $activities->firstItem() + $i }}</td>
<td><strong>{{ $item->nama_kegiatan }}</strong></td>
<td>
<div style="display:flex;align-items:center;gap:8px;">
<span style="background:#e0e7ff;color:#3730a3;width:30px;height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;">{{ $inisial }}</span>
{{ $namaUser }}
</div>
</td>
<td>{{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }}</td>
<td><span class="pill {{ $pc[0] }}">{{ $pc[1] }}</span></td>
<td>
<div style="display:flex;gap:6px;">
<a href="{{ route('admin.kegiatan.show', $item->id) }}" class="act-btn btn-view">Lihat</a>
</div>
</td>
</tr>
@empty
<tr class="empty-row">
<td colspan="6">
<div style="display:flex;flex-direction:column;align-items:center;gap:8px;color:#94a3b8;">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" 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>
Tidak ada data kegiatan yang ditemukan.
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="pg-wrap">{{ $activities->links() }}</div>
</div>
</div>
@endsection
@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Admin Disnaker Prov. Kalimantan Barat</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; }
body {
font-family: 'Outfit', sans-serif;
background: #F2F5FB;
color: #334155;
margin: 0;
padding: 0;
}
main {
min-height: 100vh;
}
::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: #f1f5f9; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
</style>
</head>
<body>
@include('admin.layouts.navbar')
<main class="pt-10 px-4">
{{-- Yield inilah panggung untuk konten kamu --}}
@yield('content')
</main>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
lucide.createIcons();
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('[onclick^="return confirm"]').forEach(function(btn) {
const match = btn.getAttribute('onclick').match(/confirm\('([^']+)'\)/);
if (match) {
const message = match[1];
btn.removeAttribute('onclick');
btn.addEventListener('click', function(e) {
e.preventDefault();
const form = btn.closest('form');
Swal.fire({
title: 'Konfirmasi',
text: message,
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3b82f6',
cancelButtonColor: '#64748b',
confirmButtonText: 'Ya, Lanjutkan',
cancelButtonText: 'Batal',
reverseButtons: true,
customClass: {
confirmButton: 'rounded-xl',
cancelButton: 'rounded-xl mr-2'
}
}).then((result) => {
if (result.isConfirmed) {
if (form) {
form.submit();
} else if(btn.getAttribute('href')) {
window.location.href = btn.getAttribute('href');
}
}
});
});
}
});
});
</script>
</body>
</html>
@@ -0,0 +1,146 @@
@php
$role = session('user')['role'] ?? '';
$dashboardRoute = $role === 'superadmin' ? route('admin.superadmin.dashboard') : route('admin.dashboard');
$usersRoute = $role === 'superadmin' ? route('admin.superadmin.user.index') : route('admin.users');
$penugasanRoute = $role === 'superadmin' ? route('admin.superadmin.kegiatan.create') : route('admin.kegiatan.index');
$isDashboard = request()->routeIs('*.dashboard');
$isUsers = request()->routeIs('*.user*') || request()->routeIs('*.users*');
$isPenugasan = request()->routeIs('*.kegiatan.*');
$activeStyle = "bg-[#0D215C] text-white";
$inactiveStyle = "text-slate-500 bg-transparent hover:bg-slate-100 hover:text-[#0D215C]";
$roleVal = session('user')['role'] ?? 'Administrator';
if ($roleVal === 'admin') {
$roleVal = 'Administrator';
} elseif ($roleVal === 'superadmin') {
$roleVal = 'Super Admin';
} else {
$roleVal = 'Petugas';
}
@endphp
<nav class="bg-white border-b border-slate-200 shadow-[0_1px_12px_rgba(13,33,92,0.06)] sticky top-0 z-50">
<div class="max-w-[1440px] mx-auto px-4 md:px-6 lg:px-8">
<div class="flex justify-between items-center h-[66px]">
{{-- Logo & Brand --}}
<div class="flex items-center gap-2 md:gap-3">
<img src="{{ asset('Kalimantan-Barat-Logo-Vector.png') }}"
alt="Logo Kalbar"
class="w-9 md:w-[46px] h-auto object-contain">
<div class="border-l-2 border-slate-200 pl-2 md:pl-3">
<p class="text-[11px] md:text-[13.5px] font-bold text-[#0D215C] tracking-tight leading-tight m-0">
Dinas Tenaga Kerja dan Transmigrasi
</p>
<p class="text-[8px] md:text-[10px] font-medium text-slate-400 tracking-[1.8px] uppercase mt-0.5 m-0">
Provinsi Kalimantan Barat
</p>
</div>
</div>
<!-- Hamburger Button (Mobile) -->
<div class="flex md:hidden">
<button type="button" id="admin-menu-btn" class="text-slate-500 hover:text-[#0D215C] focus:outline-none p-2 rounded-md hover:bg-slate-100 transition-colors">
<i data-lucide="menu" class="w-6 h-6"></i>
</button>
</div>
{{-- Nav + User (Desktop) --}}
<div class="hidden md:flex items-center gap-2">
<div class="flex items-center gap-1 mr-2">
<a href="{{ $dashboardRoute }}" class="text-[11.5px] font-semibold tracking-[0.4px] px-[18px] py-2 rounded-lg flex items-center gap-[7px] transition-all duration-200 {{ $isDashboard ? $activeStyle : $inactiveStyle }}">
<i data-lucide="layout-dashboard" class="w-3.5 h-3.5"></i> Dashboard
</a>
<a href="{{ $usersRoute }}" class="text-[11.5px] font-semibold tracking-[0.4px] px-[18px] py-2 rounded-lg flex items-center gap-[7px] transition-all duration-200 {{ $isUsers ? $activeStyle : $inactiveStyle }}">
<i data-lucide="users" class="w-3.5 h-3.5"></i> Kelola Pengguna
</a>
<a href="{{ $penugasanRoute }}" class="text-[11.5px] font-semibold tracking-[0.4px] px-[18px] py-2 rounded-lg flex items-center gap-[7px] transition-all duration-200 {{ $isPenugasan ? $activeStyle : $inactiveStyle }}">
<i data-lucide="clipboard-list" class="w-3.5 h-3.5"></i> Penugasan
</a>
</div>
<div class="w-px h-7 bg-slate-200 mx-1"></div>
<div class="flex items-center gap-2.5">
<div class="text-right">
<p class="text-[13px] font-semibold text-slate-900 leading-none m-0">
{{ session('user')['name'] ?? 'User' }}
</p>
<p class="text-[10px] font-medium text-slate-400 uppercase tracking-[1.2px] mt-1 m-0">
{{ $roleVal }}
</p>
</div>
<div class="w-9 h-9 rounded-lg bg-gradient-to-br from-[#0D215C] to-blue-800 text-white flex items-center justify-center font-bold text-xs tracking-[0.5px]">
{{ strtoupper(substr(session('user')['name'] ?? 'US',0,2)) }}
</div>
<form method="POST" action="{{ route('logout') }}" class="m-0">
@csrf
<button type="submit" class="w-9 h-9 rounded-lg bg-slate-50 text-slate-400 border border-slate-200 cursor-pointer flex items-center justify-center transition-all duration-200 hover:bg-rose-50 hover:text-rose-500 hover:border-rose-200" title="Keluar dari Sistem">
<i data-lucide="log-out" class="w-3.5 h-3.5"></i>
</button>
</form>
</div>
</div>
</div>
</div>
<!-- Mobile Menu Panel -->
<div id="admin-mobile-menu" class="hidden md:hidden bg-white border-t border-slate-100 shadow-xl absolute w-full left-0 pb-4">
<div class="px-4 py-3 flex flex-col gap-2">
<a href="{{ $dashboardRoute }}" class="text-[13px] font-semibold tracking-[0.4px] px-4 py-3 rounded-lg flex items-center gap-3 transition-all duration-200 {{ $isDashboard ? $activeStyle : $inactiveStyle }}">
<i data-lucide="layout-dashboard" class="w-4 h-4"></i> Dashboard
</a>
<a href="{{ $usersRoute }}" class="text-[13px] font-semibold tracking-[0.4px] px-4 py-3 rounded-lg flex items-center gap-3 transition-all duration-200 {{ $isUsers ? $activeStyle : $inactiveStyle }}">
<i data-lucide="users" class="w-4 h-4"></i> Kelola Pengguna
</a>
<a href="{{ $penugasanRoute }}" class="text-[13px] font-semibold tracking-[0.4px] px-4 py-3 rounded-lg flex items-center gap-3 transition-all duration-200 {{ $isPenugasan ? $activeStyle : $inactiveStyle }}">
<i data-lucide="clipboard-list" class="w-4 h-4"></i> Penugasan
</a>
<div class="h-px w-full bg-slate-100 my-1"></div>
<div class="flex items-center justify-between px-2 py-2">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg bg-gradient-to-br from-[#0D215C] to-blue-800 text-white flex items-center justify-center font-bold text-xs tracking-[0.5px]">
{{ strtoupper(substr(session('user')['name'] ?? 'US',0,2)) }}
</div>
<div>
<p class="text-[13px] font-semibold text-slate-900 leading-none m-0">
{{ session('user')['name'] ?? 'User' }}
</p>
<p class="text-[10px] font-medium text-slate-400 uppercase tracking-[1.2px] mt-1 m-0">
{{ $roleVal }}
</p>
</div>
</div>
<form method="POST" action="{{ route('logout') }}" class="m-0">
@csrf
<button type="submit" class="w-9 h-9 rounded-lg bg-slate-50 text-slate-400 border border-slate-200 cursor-pointer flex items-center justify-center transition-all duration-200 hover:bg-rose-50 hover:text-rose-500 hover:border-rose-200" title="Keluar dari Sistem">
<i data-lucide="log-out" class="w-4 h-4"></i>
</button>
</form>
</div>
</div>
</div>
</nav>
<script>
document.addEventListener('DOMContentLoaded', function() {
const btn = document.getElementById('admin-menu-btn');
const menu = document.getElementById('admin-mobile-menu');
if(btn && menu) {
btn.addEventListener('click', function(e) {
e.stopPropagation();
menu.classList.toggle('hidden');
lucide.createIcons();
});
document.addEventListener('click', function(e) {
if(!menu.contains(e.target) && !btn.contains(e.target)) {
menu.classList.add('hidden');
}
});
}
});
</script>
@@ -0,0 +1,268 @@
@extends('admin.layouts.app')
@section('content')
<style>
@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; }
body, * { font-family: 'Plus Jakarta Sans', sans-serif; }
.user-wrap { padding: 2rem 2.5rem 3rem; max-width: 1200px; margin: 0 auto; }
/* ALERTS */
.alert { padding: 12px 18px; border-radius: 12px; margin-bottom: 1.2rem; font-size: 13px; font-weight: 600; }
.alert-success { background: #dcfce7; color: #166534; }
.alert-error { background: #fee2e2; color: #b91c1c; }
/* HEADER */
.user-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.user-title h2 { font-size: 26px; font-weight: 800; color: #0f172a; margin: 0; }
.user-title p { font-size: 13px; color: #94a3b8; margin-top: 5px; }
.top-btn { display: flex; gap: 12px; }
.btn-ui {
border: none;
padding: 10px 18px;
border-radius: 12px;
font-size: 13px;
font-weight: 700;
text-decoration: none;
transition: .2s;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
}
.btn-dashboard { background: #eef2ff; color: #3730a3; }
.btn-dashboard:hover { background: #dbeafe; transform: translateY(-2px); }
.btn-add { background: #0D215C; color: #fff; box-shadow: 0 4px 14px rgba(13,33,92,.2); }
.btn-add:hover { background: #1e3a8a; transform: translateY(-2px); }
/* STATS */
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 2rem;
}
.stat-card {
background: #fff;
border-radius: 18px;
padding: 1.2rem 1.4rem;
border: 1px solid #edf1f7;
transition: .25s;
cursor: default;
box-shadow: 0 2px 8px rgba(13,33,92,.04);
}
.stat-card:hover { transform: translateY(-4px); box-shadow: 0 12px 30px rgba(13,33,92,.1); }
.stat-label { font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: .5px; margin-bottom: 8px; }
.stat-number { font-size: 34px; font-weight: 800; color: #0f172a; }
/* TABLE */
.user-card {
background: #fff;
border-radius: 20px;
border: 1px solid #edf1f7;
overflow: hidden;
box-shadow: 0 2px 12px rgba(13,33,92,.04);
}
.user-card-header {
padding: 1.2rem 1.5rem;
border-bottom: 1px solid #f1f5f9;
background: #f8fafc;
}
.user-card-header h3 { font-size: 15px; font-weight: 700; color: #0D215C; margin: 0; }
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
thead { background: #f8fafc; }
thead th {
padding: 14px 18px;
font-size: 11px;
text-transform: uppercase;
color: #94a3b8;
text-align: left;
font-weight: 700;
letter-spacing: .5px;
}
tbody td {
padding: 15px 18px;
border-top: 1px solid #f1f5f9;
vertical-align: middle;
font-size: 13px;
color: #334155;
}
tbody tr:hover { background: #f8fafc; }
.user-info { display: flex; align-items: center; gap: 12px; }
.avatar {
width: 40px; height: 40px;
border-radius: 12px;
background: linear-gradient(135deg, #0D215C, #2563eb);
color: #fff;
display: flex; align-items: center; justify-content: center;
font-weight: 700;
font-size: 13px;
flex-shrink: 0;
}
.user-name { font-weight: 700; color: #0f172a; font-size: 14px; }
.user-id { font-size: 11px; color: #94a3b8; margin-top: 2px; }
.role-badge {
padding: 5px 14px;
border-radius: 50px;
font-size: 11px;
font-weight: 700;
letter-spacing: .3px;
}
.role-superadmin { background: #fef3c7; color: #92400e; }
.role-admin { background: #dbeafe; color: #1d4ed8; }
.role-user { background: #dcfce7; color: #166534; }
.action-group { display: flex; gap: 8px; }
.action-btn {
border: none;
padding: 7px 14px;
border-radius: 10px;
font-size: 11px;
font-weight: 700;
cursor: pointer;
text-decoration: none;
transition: .15s;
display: inline-flex;
align-items: center;
gap: 5px;
}
.btn-edit { background: #dbeafe; color: #2563eb; }
.btn-edit:hover { background: #bfdbfe; }
.btn-delete { background: #fee2e2; color: #dc2626; }
.btn-delete:hover { background: #fecaca; }
/* RESPONSIVE */
@media (max-width: 768px) {
.user-wrap { padding: 1rem 0.5rem; }
.user-header { flex-direction: column; align-items: flex-start; gap: 16px; margin-bottom: 1.5rem; }
.top-btn { flex-wrap: wrap; width: 100%; }
.top-btn .btn-ui { flex: 1; justify-content: center; text-align: center; }
.stats-grid { grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 1.5rem; }
.user-card-header { padding: 1rem; }
}
@media (max-width: 480px) {
.stats-grid { grid-template-columns: 1fr; }
.user-title h2 { font-size: 22px; }
.user-card { border-radius: 16px; }
}
</style>
<div class="user-wrap">
{{-- ALERTS --}}
@if(session('success'))
<div class="alert alert-success"> {{ session('success') }}</div>
@endif
@if(session('error'))
<div class="alert alert-error"> {{ session('error') }}</div>
@endif
{{-- HEADER --}}
<div class="user-header">
<div class="user-title">
<h2>Manajemen Pengguna</h2>
<p>Kelola seluruh akun dan hak akses pengguna sistem</p>
</div>
<div class="top-btn">
<a href="{{ route('admin.dashboard') }}" class="btn-ui btn-dashboard"> Dashboard</a>
<a href="{{ route('admin.users.create') }}" class="btn-ui btn-add">+ Tambah Pengguna</a>
</div>
</div>
{{-- STATS --}}
@php
$usersCol = collect($users);
$totalUsers = $usersCol->count();
$totalAdmin = $usersCol->where('role', 'admin')->count();
$totalPetugas = $usersCol->where('role', 'user')->count();
@endphp
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Total Pengguna</div>
<div class="stat-number">{{ $totalUsers }}</div>
</div>
<div class="stat-card">
<div class="stat-label">Administrator</div>
<div class="stat-number">{{ $totalAdmin }}</div>
</div>
<div class="stat-card">
<div class="stat-label">Petugas Lapangan</div>
<div class="stat-number">{{ $totalPetugas }}</div>
</div>
</div>
{{-- TABLE --}}
<div class="user-card">
<div class="user-card-header">
<h3>Daftar Pengguna Sistem</h3>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Pengguna</th>
<th>Username</th>
<th>Jabatan</th>
<th>Role</th>
<th>Terdaftar</th>
</tr>
</thead>
<tbody>
@forelse($users as $user)
@php $u = is_array($user) ? (object)$user : $user; @endphp
<tr>
<td>
<div class="user-info">
<div class="avatar">{{ strtoupper(substr($u->name ?? '-', 0, 2)) }}</div>
<div>
<div class="user-name">{{ $u->name ?? '-' }}</div>
<div class="user-id">ID #{{ $u->id ?? '-' }}</div>
</div>
</div>
</td>
<td>{{ $u->username ?? '-' }}</td>
<td>{{ $u->jabatan ?? '-' }}</td>
<td>
@if(($u->role ?? '') == 'superadmin')
<span class="role-badge role-superadmin">Super Admin</span>
@elseif(($u->role ?? '') == 'admin')
<span class="role-badge role-admin">Administrator</span>
@else
<span class="role-badge role-user">Petugas</span>
@endif
</td>
<td>
@if(!empty($u->created_at))
{{ \Carbon\Carbon::parse($u->created_at)->format('d M Y') }}
@else
-
@endif
</td>
</tr>
@empty
<tr>
<td colspan="5" style="text-align:center;padding:2.5rem;color:#94a3b8;font-size:13px;">
Belum ada data pengguna.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
@endsection