Initial commit for combined ProjectKP
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
@extends('user.layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
@php
|
||||
// Logika status agar sinkron dengan database
|
||||
$status = $activity->status ?? 'on_progres';
|
||||
|
||||
$statusLabel = match($status) {
|
||||
'selesai' => 'DISETUJUI',
|
||||
'on_progres' => 'ON PROGRES',
|
||||
'review' => 'REVIEW',
|
||||
default => strtoupper($status),
|
||||
};
|
||||
|
||||
$statusColor = match($status) {
|
||||
'selesai' => 'bg-green-100 text-green-700',
|
||||
'on_progres' => 'bg-blue-100 text-blue-700',
|
||||
'review' => 'bg-yellow-100 text-yellow-700',
|
||||
default => 'bg-gray-100 text-gray-700',
|
||||
};
|
||||
|
||||
$fileName = $activity->lampiran ? basename($activity->lampiran) : null;
|
||||
$shortName = $fileName && strlen($fileName) > 30
|
||||
? substr($fileName, 0, 30) . '...'
|
||||
: $fileName;
|
||||
@endphp
|
||||
|
||||
<div class="max-w-6xl mx-auto mt-10 px-4 pb-16">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="mb-8">
|
||||
<div class="flex gap-3 items-center mb-3">
|
||||
<span class="{{ $statusColor }} text-xs px-4 py-1.5 rounded-full font-semibold">
|
||||
{{ $statusLabel }}
|
||||
</span>
|
||||
<span class="text-sm text-slate-400">
|
||||
Terakhir diperbarui:
|
||||
{{ $activity->updated_at ? \Carbon\Carbon::parse($activity->updated_at)->translatedFormat('d M Y') : '-' }}
|
||||
</span>
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-[#0D215C]">{{ $activity->nama_kegiatan }}</h1>
|
||||
<p class="text-sm text-slate-400">Informasi lengkap aktivitas pegawai</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
|
||||
{{-- KIRI: KONTEN UTAMA --}}
|
||||
<div class="lg:col-span-2 bg-white p-7 rounded-2xl shadow-sm border border-slate-100">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-8">
|
||||
<div>
|
||||
<p class="text-xs text-slate-400 uppercase tracking-wide mb-1">Nama Pegawai</p>
|
||||
<p class="font-semibold text-lg text-slate-800">{{ $activity->user->name ?? 'User Tidak Ditemukan' }}</p>
|
||||
<p class="text-sm text-slate-400">Username: {{ $activity->user->username ?? '-' }}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-xs text-slate-400 uppercase tracking-wide mb-1">Lokasi</p>
|
||||
<p class="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<i data-lucide="map-pin" class="w-4 h-4 text-slate-400"></i>
|
||||
{{ $activity->lokasi ?? '-' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-xs text-slate-400 uppercase tracking-wide mb-1">Tanggal Mulai</p>
|
||||
<p class="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<i data-lucide="calendar" class="w-4 h-4 text-slate-400"></i>
|
||||
{{ $activity->tanggal ? \Carbon\Carbon::parse($activity->tanggal)->translatedFormat('d F Y') : '-' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-xs text-slate-400 uppercase tracking-wide mb-1">Tenggat Waktu</p>
|
||||
<p class="font-semibold text-slate-800 flex items-center gap-2">
|
||||
<i data-lucide="clock" class="w-4 h-4 text-slate-400"></i>
|
||||
@if($activity->tanggal_selesai)
|
||||
@php
|
||||
$isOverdue = \Carbon\Carbon::parse($activity->tanggal_selesai)->isPast() && $activity->status !== 'selesai' && $activity->status !== 'telah_ditinjau';
|
||||
@endphp
|
||||
<span class="{{ $isOverdue ? 'text-red-600 font-bold' : '' }}">
|
||||
{{ \Carbon\Carbon::parse($activity->tanggal_selesai)->translatedFormat('d F Y') }}
|
||||
@if($isOverdue) (Terlambat) @endif
|
||||
</span>
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mb-6 border-slate-100">
|
||||
|
||||
<div class="mb-8">
|
||||
<p class="text-xs text-slate-400 uppercase tracking-wide mb-2">Deskripsi Detail</p>
|
||||
<div class="text-sm text-slate-600 leading-relaxed text-justify">
|
||||
{{ $activity->deskripsi ?? 'Tidak ada deskripsi tambahan.' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(!empty($activity->status_note))
|
||||
<div class="mb-8 p-4 bg-blue-50 border border-blue-100 rounded-xl">
|
||||
<p class="text-xs text-blue-500 uppercase tracking-wide mb-2 font-semibold">Catatan Status Terakhir</p>
|
||||
<div class="text-sm text-blue-900 leading-relaxed text-justify">
|
||||
{{ $activity->status_note }}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- 📝 TIMELINE PROGRESS MONITORING --}}
|
||||
<div class="mb-8">
|
||||
<p class="text-xs text-slate-400 uppercase tracking-wide mb-4">Log Monitoring Progress</p>
|
||||
|
||||
@if(isset($activity->progress) && count($activity->progress) > 0)
|
||||
<div class="relative border-l-2 border-indigo-100 ml-3 pl-5 space-y-6">
|
||||
@foreach($activity->progress as $prog)
|
||||
<div class="relative">
|
||||
<div class="absolute -left-[27px] bg-indigo-500 h-3 w-3 rounded-full border-2 border-white top-1"></div>
|
||||
<div class="text-xs text-slate-400 mb-1">
|
||||
{{ \Carbon\Carbon::parse($prog['created_at'])->format('d M Y, H:i') }} •
|
||||
<span class="font-semibold text-indigo-500">{{ $prog['user']['name'] ?? 'Pegawai' }}</span>
|
||||
</div>
|
||||
<div class="text-sm text-slate-700 bg-slate-50 border border-slate-100 p-3 rounded-lg shadow-sm">
|
||||
{{ $prog['note'] }}
|
||||
@if(!empty($prog['attachment']))
|
||||
<div class="mt-3 pt-3 border-t border-slate-200">
|
||||
<a href="http://127.0.0.1:8000/{{ $prog['attachment'] }}" target="_blank"
|
||||
class="inline-flex items-center gap-2 px-3 py-1.5 bg-indigo-50 text-indigo-600 text-xs font-semibold rounded-lg hover:bg-indigo-100 transition-colors border border-indigo-100">
|
||||
<i data-lucide="paperclip" class="w-3.5 h-3.5"></i> Lihat Lampiran Bukti
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="text-sm text-slate-500 bg-slate-50 p-4 rounded-xl border border-dashed border-slate-200 text-center">
|
||||
Belum ada log progress yang dicatat untuk kegiatan ini.
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@php
|
||||
$isPetugas = false;
|
||||
if(isset($activity->petugas)) {
|
||||
foreach($activity->petugas as $p) {
|
||||
if($p['id'] == session('user_id')) {
|
||||
$isPetugas = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if($status !== 'selesai' && $isPetugas)
|
||||
<div class="mt-6 border-t border-slate-100 pt-5">
|
||||
<form action="{{ route('activity.progress', $activity->id) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<label class="block text-sm font-semibold text-slate-700 mb-2">Tambahkan Log Progress</label>
|
||||
<div class="flex flex-col gap-3">
|
||||
<textarea name="note" required rows="3" placeholder="Contoh: Tim sudah sampai di lokasi survei..." class="w-full bg-slate-50 border border-slate-200 text-slate-800 rounded-xl px-4 py-3 text-sm focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500 transition-all shadow-sm resize-y"></textarea>
|
||||
|
||||
<div class="flex justify-end gap-3 flex-wrap">
|
||||
<label class="cursor-pointer bg-slate-50 border border-slate-200 text-slate-500 hover:text-indigo-600 hover:border-indigo-300 px-5 py-2.5 rounded-xl text-sm font-medium transition-all shadow-sm flex items-center gap-2 whitespace-nowrap">
|
||||
<i data-lucide="image" class="w-4 h-4"></i> Bukti Foto
|
||||
<input type="file" name="attachment" accept="image/*,.pdf" class="hidden" onchange="this.parentElement.querySelector('span').innerText = this.files[0].name.substring(0,15) + '...'">
|
||||
<span class="text-xs text-slate-400"></span>
|
||||
</label>
|
||||
|
||||
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white px-6 py-2.5 rounded-xl text-sm font-medium shadow-sm shadow-indigo-200 transition-all flex items-center justify-center gap-2 whitespace-nowrap">
|
||||
<i data-lucide="send" class="w-4 h-4"></i> Kirim Log
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- ACTION BUTTONS --}}
|
||||
<div class="flex gap-3 flex-wrap border-t pt-6">
|
||||
@if($status !== 'selesai' && $isPetugas)
|
||||
<a href="{{ route('activity.edit', $activity->id) }}"
|
||||
class="inline-flex items-center gap-2 bg-yellow-500 hover:bg-yellow-600 text-white px-5 py-2.5 rounded-lg text-sm font-medium transition">
|
||||
<i data-lucide="pencil" class="w-4 h-4"></i> Edit Laporan
|
||||
</a>
|
||||
|
||||
<form action="{{ route('activity.selesai', $activity->id) }}" method="POST">
|
||||
@csrf
|
||||
<button type="submit" onclick="return confirm('Tandai kegiatan ini sebagai selesai?')"
|
||||
class="inline-flex items-center gap-2 bg-green-500 hover:bg-green-600 text-white px-5 py-2.5 rounded-lg text-sm font-medium transition">
|
||||
<i data-lucide="check" class="w-4 h-4"></i> Selesaikan Kegiatan
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- KANAN: SIDEBAR DETAIL --}}
|
||||
<div class="space-y-4">
|
||||
{{-- Lampiran --}}
|
||||
<div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-100">
|
||||
<h4 class="font-semibold mb-4 flex items-center gap-2 text-[#0D215C]">
|
||||
<i data-lucide="paperclip" class="w-4 h-4"></i> Lampiran
|
||||
</h4>
|
||||
|
||||
@if($activity->lampiran)
|
||||
<div class="flex items-center gap-3 bg-red-50 border border-red-100 rounded-xl px-4 py-3 mb-4">
|
||||
<div class="w-10 h-10 rounded-lg bg-red-100 flex items-center justify-center">
|
||||
<i data-lucide="image" class="w-5 h-5 text-red-500"></i>
|
||||
</div>
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm font-medium text-slate-700 truncate">{{ $shortName }}</p>
|
||||
<p class="text-xs text-slate-400">File Lampiran</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<a href="{{ rtrim(env('BACKEND_API_URL', 'http://127.0.0.1:8000'), '/api') . '/storage/' . $activity->lampiran }}" target="_blank"
|
||||
class="flex items-center justify-center gap-2 border border-slate-200 hover:bg-slate-50 text-slate-700 py-2 rounded-lg text-sm transition">
|
||||
<i data-lucide="eye" class="w-4 h-4"></i> Lihat
|
||||
</a>
|
||||
<a href="{{ rtrim(env('BACKEND_API_URL', 'http://127.0.0.1:8000'), '/api') . '/storage/' . $activity->lampiran }}" download
|
||||
class="flex items-center justify-center gap-2 border border-slate-200 hover:bg-slate-50 text-slate-700 py-2 rounded-lg text-sm transition">
|
||||
<i data-lucide="download" class="w-4 h-4"></i> Unduh
|
||||
</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="text-center py-4 border-2 border-dashed border-slate-100 rounded-xl">
|
||||
<p class="text-sm text-slate-400 italic">Tidak ada lampiran</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Metadata --}}
|
||||
<div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-100">
|
||||
<h4 class="font-semibold mb-4 text-[#0D215C]">Meta Data</h4>
|
||||
<div class="space-y-3 text-sm">
|
||||
<div class="flex justify-between text-slate-600">
|
||||
<span>ID Laporan</span>
|
||||
<span class="font-mono text-xs">#ACT-{{ str_pad($activity->id, 5, '0', STR_PAD_LEFT) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-slate-600">
|
||||
<span>Metode</span>
|
||||
<span>{{ $activity->metode ?? 'Luring' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('activity.list') }}" class="block text-center text-sm text-blue-600 hover:text-blue-800 transition py-2">
|
||||
← Kembali ke Daftar Laporan
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,94 @@
|
||||
@extends('user.layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="max-w-3xl mx-auto mt-10 px-4 pb-20">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="mb-8">
|
||||
<a href="{{ route('activity.detail', $activity->id) }}" class="text-sm text-blue-600 flex items-center gap-2 mb-4 no-underline hover:underline">
|
||||
<i data-lucide="arrow-left" class="w-4 h-4"></i> Kembali ke Detail
|
||||
</a>
|
||||
<h2 class="text-2xl font-bold text-[#0D215C]">
|
||||
Edit Laporan Kegiatan
|
||||
</h2>
|
||||
<p class="text-sm text-slate-400">Perbarui informasi laporan kegiatan Anda di bawah ini.</p>
|
||||
</div>
|
||||
|
||||
{{-- FORM CARD --}}
|
||||
<div class="bg-white p-8 rounded-2xl shadow-sm border border-slate-100">
|
||||
<form action="{{ route('activity.update', $activity->id) }}" method="POST" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Nama Kegiatan</label>
|
||||
<div class="relative">
|
||||
<input type="text" name="nama_kegiatan"
|
||||
value="{{ old('nama_kegiatan', $activity->nama_kegiatan) }}"
|
||||
placeholder="Masukkan nama kegiatan..."
|
||||
class="w-full p-3 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition" required>
|
||||
</div>
|
||||
@error('nama_kegiatan') <span class="text-red-500 text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Tanggal Pelaksanaan</label>
|
||||
<input type="date" name="tanggal"
|
||||
value="{{ old('tanggal', $activity->tanggal) }}"
|
||||
class="w-full p-3 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition" required>
|
||||
@error('tanggal') <span class="text-red-500 text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Lokasi</label>
|
||||
<input type="text" name="lokasi"
|
||||
value="{{ old('lokasi', $activity->lokasi) }}"
|
||||
placeholder="Contoh: Kantor Dinas..."
|
||||
class="w-full p-3 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition" required>
|
||||
@error('lokasi') <span class="text-red-500 text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Deskripsi Kegiatan</label>
|
||||
<textarea name="deskripsi" rows="5"
|
||||
placeholder="Jelaskan detail kegiatan yang dilakukan..."
|
||||
class="w-full p-3 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition">{{ old('deskripsi', $activity->deskripsi) }}</textarea>
|
||||
@error('deskripsi') <span class="text-red-500 text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Catatan Status Laporan (Node Petugas)</label>
|
||||
<textarea name="status_note" rows="3"
|
||||
placeholder="Contoh: Tugas ini sedang berjalan di desa darit..."
|
||||
class="w-full p-3 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition">{{ old('status_note', $activity->status_note ?? '') }}</textarea>
|
||||
@error('status_note') <span class="text-red-500 text-xs mt-1">{{ $message }}</span> @enderror
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-4 pt-4 border-t border-slate-100">
|
||||
<button type="submit"
|
||||
class="bg-[#0D215C] hover:bg-[#0a1a47] text-white px-8 py-3 rounded-xl font-bold transition shadow-lg shadow-blue-900/10 flex items-center gap-2">
|
||||
<i data-lucide="save" class="w-4 h-4"></i> Simpan Perubahan
|
||||
</button>
|
||||
|
||||
<a href="{{ route('activity.detail', $activity->id) }}"
|
||||
class="px-8 py-3 border border-slate-200 text-slate-600 rounded-xl font-semibold hover:bg-slate-50 transition">
|
||||
Batal
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,113 @@
|
||||
@extends('user.layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
{{-- PAGE TITLE --}}
|
||||
<div style="margin-bottom:28px; display: flex; justify-content: space-between; align-items: flex-end; padding: 0 4px; flex-wrap: wrap; gap: 16px;">
|
||||
<div>
|
||||
<div style="font-size:26px; font-weight:700; color:#0D215C; font-family:'Outfit', sans-serif;">
|
||||
Riwayat Aktivitas
|
||||
</div>
|
||||
<div style="font-size:14px; color:#6b7280; margin-top:6px; font-family:'Outfit', sans-serif;">
|
||||
Daftar seluruh laporan kegiatan yang telah Anda kirimkan.
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ route('input.activity') }}"
|
||||
style="text-decoration:none; background:#1d4ed8; color:white; padding:10px 24px; border-radius:10px; font-size:13px; font-weight:600; transition:.2s; display: flex; align-items: center; gap: 8px;">
|
||||
<i data-lucide="plus" style="width: 16px; height: 16px;"></i>
|
||||
Input Baru
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{{-- LIST CONTAINER --}}
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
|
||||
@forelse($activities as $item)
|
||||
<div class="card" style="padding: 20px; background: white; border-radius: 16px; border: 1px solid #f1f5f9; box-shadow: 0 1px 3px rgba(0,0,0,0.05); transition: .2s;"
|
||||
onmouseover="this.style.borderColor='#1d4ed8'; this.style.transform='translateY(-2px)'"
|
||||
onmouseout="this.style.borderColor='#f1f5f9'; this.style.transform='translateY(0)'">
|
||||
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;">
|
||||
|
||||
<div style="display: flex; gap: 16px; align-items: center;">
|
||||
{{-- Icon Box --}}
|
||||
<div style="width: 52px; height: 52px; background: #eff6ff; border-radius: 14px; display: flex; align-items: center; justify-content: center; color: #1d4ed8; flex-shrink: 0;">
|
||||
<i data-lucide="clipboard-list" style="width: 26px; height: 26px;"></i>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style="font-size: 17px; font-weight: 700; color: #1e293b; margin-bottom: 6px;">
|
||||
{{ $item->nama_kegiatan }}
|
||||
</div>
|
||||
<div style="display: flex; gap: 15px; align-items: center; flex-wrap: wrap;">
|
||||
<span style="font-size: 13px; color: #64748b; display: flex; align-items: center; gap: 6px;">
|
||||
<i data-lucide="user" style="width: 14px; height: 14px;"></i>
|
||||
<strong style="color: #475569;">{{ $item->user->name ?? 'Pegawai' }}</strong>
|
||||
</span>
|
||||
<span style="font-size: 13px; color: #64748b; display: flex; align-items: center; gap: 6px;">
|
||||
<i data-lucide="calendar" style="width: 14px; height: 14px;"></i>
|
||||
{{ $item->tanggal ? \Carbon\Carbon::parse($item->tanggal)->translatedFormat('d M Y') : '-' }}
|
||||
@if(!empty($item->tanggal_selesai))
|
||||
- {{ \Carbon\Carbon::parse($item->tanggal_selesai)->translatedFormat('d M Y') }}
|
||||
@endif
|
||||
</span>
|
||||
<span style="font-size: 13px; color: #64748b; display: flex; align-items: center; gap: 6px;">
|
||||
<i data-lucide="map-pin" style="width: 14px; height: 14px;"></i>
|
||||
{{ $item->lokasi ?? '-' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 20px;">
|
||||
{{-- Badge Status --}}
|
||||
@php
|
||||
$status = $item->status ?? 'on_progres';
|
||||
$isSelesai = $status === 'selesai';
|
||||
$isOverdue = !empty($item->tanggal_selesai) && \Carbon\Carbon::parse($item->tanggal_selesai)->isPast() && !$isSelesai && $status !== 'telah_ditinjau';
|
||||
@endphp
|
||||
<span style="padding: 6px 14px; border-radius: 99px; font-size: 11px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase;
|
||||
{{ $isSelesai ? 'background:#dcfce7; color:#15803d;' : ($isOverdue ? 'background:#fef2f2; color:#dc2626; border:1px solid #fecaca;' : 'background:#eff6ff; color:#1d4ed8;') }}">
|
||||
{{ $isSelesai ? 'Selesai' : ($isOverdue ? 'Terlambat' : 'On Progres') }}
|
||||
</span>
|
||||
|
||||
{{-- Action Button --}}
|
||||
<a href="{{ route('activity.detail', $item->id) }}"
|
||||
style="text-decoration: none; color: #1d4ed8; font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 4px; padding: 8px 12px; border-radius: 8px; transition: .2s;"
|
||||
onmouseover="this.style.background='#eff6ff'"
|
||||
onmouseout="this.style.background='transparent'">
|
||||
Detail
|
||||
<i data-lucide="chevron-right" style="width: 18px; height: 18px;"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
{{-- EMPTY STATE --}}
|
||||
<div style="text-align: center; padding: 80px 20px; background: white; border-radius: 20px; border: 2px dashed #e2e8f0;">
|
||||
<div style="background: #f8fafc; width: 72px; height: 72px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px;">
|
||||
<i data-lucide="inbox" style="width: 36px; height: 36px; color: #cbd5e1;"></i>
|
||||
</div>
|
||||
<div style="font-size: 18px; font-weight: 700; color: #475569;">Belum Ada Aktivitas</div>
|
||||
<div style="font-size: 14px; color: #94a3b8; margin-top: 6px; max-width: 300px; margin-left: auto; margin-right: auto;">
|
||||
Laporan kegiatan yang Anda kirimkan nantinya akan muncul di daftar ini.
|
||||
</div>
|
||||
<a href="{{ route('input.activity') }}" style="display: inline-block; margin-top: 24px; color: #1d4ed8; font-weight: 600; font-size: 14px; text-decoration: none;">
|
||||
Mulai buat laporan pertama →
|
||||
</a>
|
||||
</div>
|
||||
@endforelse
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Script untuk memastikan icon Lucide muncul --}}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,897 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Dashboard User</title>
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1d4ed8;
|
||||
--primary-soft: #e0e7ff;
|
||||
--bg: #f4f6fb;
|
||||
--card: #ffffff;
|
||||
--border: #e5e7eb;
|
||||
--text: #111827;
|
||||
--muted: #6b7280;
|
||||
--danger: #dc2626;
|
||||
--success: #16a34a;
|
||||
--warning: #f59e0b;
|
||||
--info: #2563eb;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
display: flex
|
||||
}
|
||||
|
||||
/* ─── SIDEBAR ─── */
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
padding: 24px 18px;
|
||||
border-right: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-bottom: 18px
|
||||
}
|
||||
|
||||
.logo img {
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
object-fit: contain
|
||||
}
|
||||
|
||||
.logo-text .top,
|
||||
.logo-text .mid {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
color: #111827
|
||||
}
|
||||
|
||||
.logo-text .bot {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
margin-top: 2px
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
margin: 14px 0
|
||||
}
|
||||
|
||||
.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px
|
||||
}
|
||||
|
||||
.menu a {
|
||||
padding: 11px 13px;
|
||||
border-radius: 10px;
|
||||
text-decoration: none;
|
||||
color: #374151;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 14px;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.menu a:hover {
|
||||
background: #f3f4f6
|
||||
}
|
||||
|
||||
.menu a.active {
|
||||
background: var(--primary-soft);
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu a svg {
|
||||
flex-shrink: 0;
|
||||
opacity: .7
|
||||
}
|
||||
|
||||
.menu a.active svg {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 11px 13px;
|
||||
color: #374151;
|
||||
text-decoration: none;
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #f3f4f6
|
||||
}
|
||||
|
||||
.menu-item svg {
|
||||
opacity: .7
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
width: 100%;
|
||||
padding: 11px 13px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--danger);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.logout-btn:hover {
|
||||
background: #fef2f2
|
||||
}
|
||||
|
||||
/* ─── MAIN ─── */
|
||||
.main {
|
||||
flex: 1;
|
||||
padding: 28px 32px;
|
||||
overflow-y: auto;
|
||||
height: 100vh
|
||||
}
|
||||
|
||||
/* ─── HEADER ─── */
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.search-wrap {
|
||||
position: relative;
|
||||
width: 340px
|
||||
}
|
||||
|
||||
.search-wrap svg {
|
||||
position: absolute;
|
||||
left: 13px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.search {
|
||||
width: 100%;
|
||||
padding: 10px 14px 10px 40px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
transition: .2s;
|
||||
font-size: 14px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.search:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(29, 78, 216, .08);
|
||||
}
|
||||
|
||||
/* ─── USER ─── */
|
||||
.user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #1e3a8a;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ─── NOTIF ─── */
|
||||
.notif {
|
||||
position: relative;
|
||||
cursor: pointer
|
||||
}
|
||||
|
||||
.notif-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.notif-btn:hover {
|
||||
background: #f3f4f6
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
border-radius: 999px;
|
||||
padding: 2px 6px;
|
||||
font-weight: 600;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50px;
|
||||
width: 320px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, .1);
|
||||
display: none;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.dropdown.show {
|
||||
display: block
|
||||
}
|
||||
|
||||
.dropdown-header {
|
||||
padding: 13px 16px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dropdown-header a {
|
||||
font-size: 12px;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
font-weight: 500
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.dropdown-item:last-child {
|
||||
border-bottom: none
|
||||
}
|
||||
|
||||
.dropdown-item.unread {
|
||||
background: #eef2ff
|
||||
}
|
||||
|
||||
.dropdown-empty {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
font-size: 13px
|
||||
}
|
||||
|
||||
/* ─── PAGE TITLE ─── */
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
color: var(--text)
|
||||
}
|
||||
|
||||
.page-subtitle {
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
margin-bottom: 22px
|
||||
}
|
||||
|
||||
/* ─── CARDS ─── */
|
||||
.cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 24px
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
padding: 20px 22px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--border);
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, .06)
|
||||
}
|
||||
|
||||
.card h4 {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: .05em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
font-size: 32px;
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
.card.total h4,
|
||||
.card.total h2 {
|
||||
color: var(--info)
|
||||
}
|
||||
|
||||
.card.proses h4,
|
||||
.card.proses h2 {
|
||||
color: var(--warning)
|
||||
}
|
||||
|
||||
.card.selesai h4,
|
||||
.card.selesai h2 {
|
||||
color: var(--success)
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card.total .card-icon {
|
||||
background: #dbeafe
|
||||
}
|
||||
|
||||
.card.proses .card-icon {
|
||||
background: #fef3c7
|
||||
}
|
||||
|
||||
.card.selesai .card-icon {
|
||||
background: #dcfce7
|
||||
}
|
||||
|
||||
/* ─── LIST ─── */
|
||||
.list {
|
||||
background: #fff;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--border);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list-header {
|
||||
padding: 16px 20px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: #fafafa;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.list-header a {
|
||||
font-size: 13px;
|
||||
color: var(--primary);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.list-header a:hover {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
transition: .2s;
|
||||
}
|
||||
|
||||
.item:last-child {
|
||||
border-bottom: none
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background: #f9fafb
|
||||
}
|
||||
|
||||
.item-left strong {
|
||||
display: block;
|
||||
margin-bottom: 3px;
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
.item-left small {
|
||||
color: var(--muted);
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.badge-status {
|
||||
padding: 5px 12px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background: #dcfce7;
|
||||
color: var(--success)
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background: #fef3c7;
|
||||
color: #b45309
|
||||
}
|
||||
|
||||
.badge-info {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8
|
||||
}
|
||||
|
||||
.badge-danger {
|
||||
background: #fee2e2;
|
||||
color: #dc2626
|
||||
}
|
||||
|
||||
/* ─── EMPTY STATE ─── */
|
||||
.empty-state {
|
||||
padding: 48px 20px;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.empty-state svg {
|
||||
margin: 0 auto 12px;
|
||||
display: block;
|
||||
opacity: .35
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 14px
|
||||
}
|
||||
|
||||
/* ─── NO RESULT ─── */
|
||||
.no-result {
|
||||
display: none;
|
||||
padding: 32px 20px;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ─── RESPONSIVE ─── */
|
||||
@media (max-width: 1024px) {
|
||||
.cards { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body { flex-direction: column; }
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
}
|
||||
.logo {
|
||||
justify-content: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.divider { display: none; }
|
||||
.menu {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
overflow-x: visible;
|
||||
gap: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.menu a { white-space: nowrap; justify-content: center; flex: 1 1 calc(50% - 8px); text-align: center; font-size: 13px; padding: 10px; }
|
||||
.bottom {
|
||||
flex-direction: row;
|
||||
margin-top: 8px;
|
||||
gap: 8px;
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 12px;
|
||||
}
|
||||
.bottom a, .bottom button { white-space: nowrap; justify-content: center; flex: 1 1 calc(50% - 8px); font-size: 13px; padding: 10px; }
|
||||
|
||||
.main { padding: 20px 16px; height: auto; }
|
||||
.header { flex-direction: column-reverse; align-items: flex-start; gap: 20px; margin-bottom: 20px; }
|
||||
.search-wrap { width: 100%; }
|
||||
.user { width: 100%; justify-content: space-between; }
|
||||
.dropdown { width: 300px; left: -10px; right: auto; top: 55px; max-width: calc(100vw - 32px); }
|
||||
|
||||
.cards { grid-template-columns: 1fr; gap: 12px; }
|
||||
.item { flex-direction: column; align-items: flex-start; gap: 12px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<!-- ═══ SIDEBAR ═══ -->
|
||||
<div class="sidebar">
|
||||
|
||||
<div class="logo">
|
||||
<img src="{{ asset('Kalimantan-Barat-Logo-Vector.png') }}" alt="Logo">
|
||||
<div class="logo-text">
|
||||
<div class="top">DINAS TENAGA KERJA</div>
|
||||
<div class="mid">DAN TRANSMIGRASI</div>
|
||||
<div class="bot">PROVINSI KALIMANTAN BARAT</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="menu">
|
||||
|
||||
<a href="{{ route('user.dashboard') }}"
|
||||
class="{{ request()->routeIs('user.dashboard') ? 'active' : '' }}">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="13" y="3" width="8" height="7" rx="1" />
|
||||
<rect x="13" y="13" width="8" height="8" rx="1" />
|
||||
<rect x="3" y="13" width="7" height="8" rx="1" />
|
||||
</svg>
|
||||
Dashboard
|
||||
</a>
|
||||
|
||||
<a href="{{ route('input.activity') }}"
|
||||
class="{{ request()->routeIs('input.activity') ? 'active' : '' }}">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path d="M11 5H6a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2v-5" />
|
||||
<path d="M17.5 3.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 8.5-8.5z" />
|
||||
</svg>
|
||||
Input Aktivitas
|
||||
</a>
|
||||
|
||||
<a href="{{ route('activity.list') }}"
|
||||
class="{{ request()->routeIs('activity.list') ? 'active' : '' }}">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<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" />
|
||||
<path d="M9 12h6M9 16h4" />
|
||||
</svg>
|
||||
Daftar Aktivitas
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bottom">
|
||||
<a href="{{ route('settings') }}" class="menu-item">
|
||||
<svg width="18" height="18" 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" />
|
||||
</svg>
|
||||
Pengaturan
|
||||
</a>
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button class="logout-btn" type="submit">
|
||||
<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="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
||||
<polyline points="16 17 21 12 16 7" />
|
||||
<line x1="21" y1="12" x2="9" y2="12" />
|
||||
</svg>
|
||||
Logout
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ═══ MAIN ═══ -->
|
||||
<div class="main">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="header">
|
||||
|
||||
<div class="search-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21-4.35-4.35" />
|
||||
</svg>
|
||||
<input type="text" class="search" placeholder="Cari aktivitas..."
|
||||
oninput="filterAktivitas(this.value)">
|
||||
</div>
|
||||
|
||||
<div class="user">
|
||||
|
||||
<!-- NOTIF -->
|
||||
<div class="notif">
|
||||
<div class="notif-btn" onclick="toggleNotif()">
|
||||
<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="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@if($unreadCount > 0)
|
||||
<div class="badge">{{ $unreadCount }}</div>
|
||||
@endif
|
||||
|
||||
<div class="dropdown" id="notifDropdown">
|
||||
<div class="dropdown-header">
|
||||
Notifikasi
|
||||
@if(count($notifications) > 0)
|
||||
<a href="#" onclick="markAllAsRead(event)">Tandai semua dibaca</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div id="notifListContainer">
|
||||
@forelse($notifications as $n)
|
||||
<div class="dropdown-item {{ !$n['read_at'] ? 'unread' : '' }}">
|
||||
{{ $n['data']['message'] ?? 'Ada notifikasi baru' }}
|
||||
<br><small style="color:var(--muted); font-size:11px;">{{ \Carbon\Carbon::parse($n['created_at'])->diffForHumans() }}</small>
|
||||
</div>
|
||||
@empty
|
||||
<div class="dropdown-empty">Tidak ada notifikasi</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- USER INFO -->
|
||||
<div>
|
||||
<strong style="font-size:14px">
|
||||
{{ session('user')['name'] ?? 'User' }}
|
||||
</strong><br>
|
||||
|
||||
<small style="color:var(--muted);font-size:12px">Pegawai</small>
|
||||
</div>
|
||||
|
||||
<div class="avatar">
|
||||
{{ strtoupper(substr(session('user')['name'] ?? 'U', 0, 1)) }}
|
||||
</div>
|
||||
|
||||
</div>{{-- end .user --}}
|
||||
|
||||
</div>{{-- end .header --}}
|
||||
|
||||
<!-- PAGE TITLE -->
|
||||
<div class="page-title">Dashboard Monitoring</div>
|
||||
<div class="page-subtitle">Pantau aktivitas kamu</div>
|
||||
|
||||
<!-- CARDS -->
|
||||
<div class="cards">
|
||||
<div class="card total">
|
||||
<div class="card-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
|
||||
stroke="#2563eb" stroke-width="2">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="13" y="3" width="8" height="7" rx="1" />
|
||||
<rect x="13" y="13" width="8" height="8" rx="1" />
|
||||
<rect x="3" y="13" width="7" height="8" rx="1" />
|
||||
</svg>
|
||||
</div>
|
||||
<h4>TOTAL</h4>
|
||||
<h2>{{ $activities->count() }}</h2>
|
||||
</div>
|
||||
<div class="card proses">
|
||||
<div class="card-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
|
||||
stroke="#f59e0b" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<polyline points="12 6 12 12 16 14" />
|
||||
</svg>
|
||||
</div>
|
||||
<h4>ON PROGRESS</h4>
|
||||
<h2>{{ $onProgres }}</h2>
|
||||
</div>
|
||||
<div class="card selesai">
|
||||
<div class="card-icon">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
|
||||
stroke="#16a34a" stroke-width="2">
|
||||
<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>
|
||||
<h4>SELESAI</h4>
|
||||
<h2>{{ $selesai }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RECENT ACTIVITIES -->
|
||||
<div class="list">
|
||||
<div class="list-header">
|
||||
<span>Recent Activities</span>
|
||||
<a href="{{ route('activity.list') }}">Lihat Semua →</a>
|
||||
</div>
|
||||
|
||||
<div id="activity-list">
|
||||
@forelse($activities->take(5) as $item)
|
||||
<div class="item" data-name="{{ strtolower($item->nama_kegiatan) }}">
|
||||
<div class="item-left">
|
||||
<strong>{{ $item->nama_kegiatan }}</strong>
|
||||
<small>
|
||||
{{ $item->user->name ?? 'Pegawai' }}
|
||||
·
|
||||
{{ \Carbon\Carbon::parse($item->tanggal ?? $item->created_at)->translatedFormat('d M Y') }}
|
||||
@if(!empty($item->tanggal_selesai))
|
||||
- {{ \Carbon\Carbon::parse($item->tanggal_selesai)->translatedFormat('d M Y') }}
|
||||
@endif
|
||||
|
||||
@if(!empty($item->lokasi))
|
||||
· {{ $item->lokasi }}
|
||||
@endif
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
@php
|
||||
$isOverdue = !empty($item->tanggal_selesai) && \Carbon\Carbon::parse($item->tanggal_selesai)->isPast() && $item->status !== 'selesai' && $item->status !== 'telah_ditinjau';
|
||||
@endphp
|
||||
@if($item->status == 'selesai')
|
||||
<span class="badge-status badge-success">Selesai</span>
|
||||
@elseif(!empty($item->reviewed_at) || $item->status == 'ditinjau' || $item->status == 'telah_ditinjau')
|
||||
<span class="badge-status badge-info">Ditinjau</span>
|
||||
@elseif($item->status == 'ditolak')
|
||||
<span class="badge-status badge-danger">Ditolak</span>
|
||||
@elseif($isOverdue)
|
||||
<span class="badge-status badge-danger" style="background:#fef2f2; color:#dc2626; border-color:#fecaca;">Terlambat</span>
|
||||
@else
|
||||
<span class="badge-status badge-warning">On Progress</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div class="empty-state">
|
||||
<svg width="44" height="44" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="1.5">
|
||||
<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" />
|
||||
<path d="M9 12h6M9 16h4" />
|
||||
</svg>
|
||||
<p>Belum ada aktivitas yang diinput.</p>
|
||||
<a href="{{ route('input.activity') }}"
|
||||
style="display:inline-block;margin-top:12px;padding:8px 18px;
|
||||
background:var(--primary);color:#fff;border-radius:8px;
|
||||
text-decoration:none;font-size:13px;font-weight:500">
|
||||
+ Input Aktivitas
|
||||
</a>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<!-- No result saat search -->
|
||||
<div class="no-result" id="no-result">
|
||||
Tidak ada aktivitas yang cocok dengan pencarian.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>{{-- end .list --}}
|
||||
|
||||
</div>{{-- end .main --}}
|
||||
|
||||
<script>
|
||||
// ─── TOGGLE NOTIF ───
|
||||
function toggleNotif() {
|
||||
document.getElementById("notifDropdown").classList.toggle("show");
|
||||
}
|
||||
document.addEventListener('click', function(e) {
|
||||
const notif = document.querySelector('.notif');
|
||||
if (notif && !notif.contains(e.target)) {
|
||||
document.getElementById('notifDropdown').classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
async function markAllAsRead(e) {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await fetch('/user/notifications-api/read', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
// Hapus badge dan ganti class unread
|
||||
const badge = document.querySelector('.badge');
|
||||
if (badge) badge.style.display = 'none';
|
||||
document.querySelectorAll('.dropdown-item.unread').forEach(el => {
|
||||
el.classList.remove('unread');
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── FILTER SEARCH ───
|
||||
function filterAktivitas(keyword) {
|
||||
const items = document.querySelectorAll('#activity-list .item');
|
||||
const noResult = document.getElementById('no-result');
|
||||
keyword = keyword.toLowerCase().trim();
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach(item => {
|
||||
const name = item.getAttribute('data-name') || '';
|
||||
if (name.includes(keyword)) {
|
||||
item.style.display = 'flex';
|
||||
visibleCount++;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
if (visibleCount === 0 && items.length > 0) {
|
||||
noResult.style.display = 'block';
|
||||
} else {
|
||||
noResult.style.display = 'none';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,123 @@
|
||||
@extends('user.layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
{{-- PAGE TITLE --}}
|
||||
<div style="margin-bottom:28px;">
|
||||
<div style="font-size:26px; font-weight:700; color:#111827; font-family:'Outfit', sans-serif;">
|
||||
Input Laporan Kegiatan
|
||||
</div>
|
||||
<div style="font-size:14px; color:#6b7280; margin-top:6px; font-family:'Outfit', sans-serif;">
|
||||
Pastikan data yang dimasukkan sudah sesuai dengan dokumentasi lapangan.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($errors->any())
|
||||
<div style="background-color: #fee2e2; border-left: 4px solid #ef4444; padding: 16px; margin-bottom: 24px; border-radius: 4px;">
|
||||
<div style="color: #b91c1c; font-weight: 600; margin-bottom: 8px;">Terdapat kesalahan pada input Anda:</div>
|
||||
<ul style="color: #b91c1c; font-size: 14px; margin: 0; padding-left: 20px;">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('activities.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
{{-- DATA PEGAWAI --}}
|
||||
<div class="card">
|
||||
|
||||
<div style="font-size:13px; font-weight:600; color:#1d4ed8; margin-bottom:16px;
|
||||
padding-bottom:12px; border-bottom:1px solid #e5e7eb; letter-spacing:0.04em;">
|
||||
DATA PEGAWAI
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<div>
|
||||
<label class="label">Nama Pegawai</label>
|
||||
<input type="text" value="{{ session('user')['name'] ?? 'Guest' }}" class="input readonly" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label">Username</label>
|
||||
<input type="text" value="{{ session('user')['username'] ?? '-' }}" class="input readonly" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- DETAIL KEGIATAN --}}
|
||||
<div class="card">
|
||||
|
||||
<div style="font-size:13px; font-weight:600; color:#1d4ed8; margin-bottom:16px;
|
||||
padding-bottom:12px; border-bottom:1px solid #e5e7eb; letter-spacing:0.04em;">
|
||||
DETAIL KEGIATAN
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="label">Judul Kegiatan</label>
|
||||
<input type="text" name="nama_kegiatan" class="input" placeholder="Monitoring perusahaan..." required>
|
||||
</div>
|
||||
|
||||
<div class="form-grid" style="margin-top:16px;">
|
||||
<div>
|
||||
<label class="label">Tanggal</label>
|
||||
<input type="date" name="tanggal" class="input" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="label">Lokasi</label>
|
||||
<input type="text" name="lokasi" class="input" placeholder="Kota / Alamat" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:16px;">
|
||||
<label class="label">Detail Kegiatan</label>
|
||||
<textarea name="deskripsi" class="textarea" placeholder="Tulis laporan lengkap di sini..." required></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- UPLOAD --}}
|
||||
<div class="card">
|
||||
|
||||
<div style="font-size:13px; font-weight:600; color:#1d4ed8; margin-bottom:16px;
|
||||
padding-bottom:12px; border-bottom:1px solid #e5e7eb; letter-spacing:0.04em;">
|
||||
LAMPIRAN DOKUMENTASI
|
||||
</div>
|
||||
|
||||
<label class="upload-box" id="upload-label">
|
||||
<input type="file" name="image" id="file-input" accept=".pdf,.jpg,.jpeg,.png" hidden>
|
||||
|
||||
{{-- Icon Upload --}}
|
||||
<svg width="36" height="36" viewBox="0 0 24 24" fill="none"
|
||||
stroke="#9ca3af" stroke-width="1.5" style="margin:0 auto 10px;">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="17 8 12 3 7 8"/>
|
||||
<line x1="12" y1="3" x2="12" y2="15"/>
|
||||
</svg>
|
||||
|
||||
<p style="font-weight:600; font-size:14px; color:#374151; margin-bottom:4px;" id="file-name">
|
||||
Klik untuk upload file
|
||||
</p>
|
||||
<small style="color:#9ca3af; font-size:12px;">PDF / JPG / PNG · Maksimal 2MB</small>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- SUBMIT --}}
|
||||
<button class="btn" type="submit">
|
||||
Kirim Laporan
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
{{-- Script: tampilkan nama file setelah dipilih --}}
|
||||
<script>
|
||||
document.getElementById('file-input').addEventListener('change', function() {
|
||||
const fileName = this.files[0] ? this.files[0].name : 'Klik untuk upload file';
|
||||
document.getElementById('file-name').textContent = fileName;
|
||||
});
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,215 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Panel Petugas – Disnakertrans Prov. Kalbar</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>
|
||||
/* ─── RESET + FONT ─── */
|
||||
*, *::before, *::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #F2F5FB;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
/* ─── SCROLLBAR ─── */
|
||||
::-webkit-scrollbar { width: 6px; }
|
||||
::-webkit-scrollbar-track { background: #f1f5f9; }
|
||||
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
|
||||
|
||||
/* ─── VARIABLES ─── */
|
||||
:root {
|
||||
--primary: #1d4ed8;
|
||||
--primary-soft: #e0e7ff;
|
||||
--border: #e5e7eb;
|
||||
--text: #111827;
|
||||
--muted: #6b7280;
|
||||
}
|
||||
|
||||
/* ─── CARD ─── */
|
||||
.card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 28px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* ─── FORM GRID ─── */
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* ─── LABEL ─── */
|
||||
.label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--muted);
|
||||
margin-bottom: 6px;
|
||||
display: block;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
/* ─── INPUT ─── */
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
background: #fff;
|
||||
color: var(--text);
|
||||
transition: .2s;
|
||||
}
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(29,78,216,.1);
|
||||
}
|
||||
.readonly {
|
||||
background: #f3f4f6;
|
||||
color: #6b7280;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ─── TEXTAREA ─── */
|
||||
.textarea {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
color: var(--text);
|
||||
min-height: 140px;
|
||||
resize: vertical;
|
||||
transition: .2s;
|
||||
}
|
||||
.textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(29,78,216,.1);
|
||||
}
|
||||
|
||||
/* ─── UPLOAD BOX ─── */
|
||||
.upload-box {
|
||||
border: 2px dashed var(--border);
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
border-radius: 14px;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
transition: .2s;
|
||||
}
|
||||
.upload-box:hover {
|
||||
background: #f9fafb;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/* ─── BUTTON ─── */
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
cursor: pointer;
|
||||
transition: .2s;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.btn:hover {
|
||||
background: #1e40af;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(29,78,216,.3);
|
||||
}
|
||||
.btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* ─── RESPONSIVE ─── */
|
||||
@media (max-width: 768px) {
|
||||
.form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
main {
|
||||
padding: 20px 0 !important;
|
||||
}
|
||||
.card {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@include('user.layouts.navbar')
|
||||
|
||||
<main style="padding: 40px 0;">
|
||||
<div style="max-width: 860px; margin: auto; padding: 0 24px;">
|
||||
@yield('content')
|
||||
</div>
|
||||
</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,136 @@
|
||||
<nav class="bg-[#f8fafc] border-b border-slate-200 font-['Outfit'] sticky top-0 z-50 shadow-sm">
|
||||
<div class="max-w-[1200px] mx-auto px-4 md:px-6 lg:px-10">
|
||||
<div class="flex items-center justify-between h-[70px]">
|
||||
<!-- LEFT: LOGO -->
|
||||
<div class="flex items-center gap-2 md:gap-3">
|
||||
<img src="{{ asset('Kalimantan-Barat-Logo-Vector.png') }}" class="h-9 md:h-[42px] w-auto object-contain" alt="Logo Kalbar">
|
||||
<div class="leading-[1.2] border-l-2 border-slate-200 pl-2 md:pl-3">
|
||||
<div class="font-bold text-[11px] md:text-[14px] text-blue-900 tracking-[0.02em]">
|
||||
DINAS TENAGA KERJA DAN TRANSMIGRASI
|
||||
</div>
|
||||
<div class="text-[8px] md:text-[11px] text-slate-500 font-medium">
|
||||
PROVINSI KALIMANTAN BARAT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hamburger Button (Mobile) -->
|
||||
<div class="flex md:hidden">
|
||||
<button type="button" id="user-menu-btn" class="text-slate-500 hover:text-blue-900 focus:outline-none p-2 rounded-md hover:bg-slate-200 transition-colors">
|
||||
<i data-lucide="menu" class="w-6 h-6"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- CENTER & RIGHT: NAVIGATION (Desktop) -->
|
||||
<div class="hidden md:flex items-center gap-6">
|
||||
<div class="flex items-center gap-2 text-[14px]">
|
||||
<a href="{{ route('user.dashboard') }}"
|
||||
class="flex items-center gap-1.5 px-4 py-2 rounded-lg transition-all duration-200 {{ request()->routeIs('user.dashboard') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-500 font-medium hover:bg-slate-100 hover:text-blue-800' }}">
|
||||
<i data-lucide="layout-dashboard" class="w-4 h-4"></i> Dashboard
|
||||
</a>
|
||||
<a href="{{ route('input.activity') }}"
|
||||
class="flex items-center gap-1.5 px-4 py-2 rounded-lg transition-all duration-200 {{ request()->routeIs('input.activity') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-500 font-medium hover:bg-slate-100 hover:text-blue-800' }}">
|
||||
<i data-lucide="edit" class="w-4 h-4"></i> Input Kegiatan
|
||||
</a>
|
||||
<a href="{{ route('activity.list') }}"
|
||||
class="flex items-center gap-1.5 px-4 py-2 rounded-lg transition-all duration-200 {{ request()->routeIs('activity.list') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-500 font-medium hover:bg-slate-100 hover:text-blue-800' }}">
|
||||
<i data-lucide="folder" class="w-4 h-4"></i> Kegiatan Saya
|
||||
</a>
|
||||
<a href="{{ route('settings') ?? '#' }}"
|
||||
class="flex items-center gap-1.5 px-4 py-2 rounded-lg transition-all duration-200 {{ request()->routeIs('settings') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-500 font-medium hover:bg-slate-100 hover:text-blue-800' }}">
|
||||
<i data-lucide="settings" class="w-4 h-4"></i> Pengaturan
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="w-px h-8 bg-slate-300"></div>
|
||||
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="w-[38px] h-[38px] rounded-full bg-blue-900 text-white flex items-center justify-center font-bold text-[16px]">
|
||||
{{ strtoupper(substr(session('user')['name'] ?? 'U', 0, 1)) }}
|
||||
</div>
|
||||
<div class="leading-[1.2]">
|
||||
<div class="font-semibold text-[14px] text-slate-900">
|
||||
{{ session('user')['name'] ?? 'User' }}
|
||||
</div>
|
||||
<div class="text-[11px] text-slate-500 font-medium">
|
||||
USER
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}" class="ml-2 m-0">
|
||||
@csrf
|
||||
<button type="submit" class="bg-transparent border-none cursor-pointer text-red-500 p-1.5 rounded-md hover:bg-red-50 transition-colors" title="Logout">
|
||||
<i data-lucide="log-out" class="w-[18px] h-[18px]"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Panel -->
|
||||
<div id="user-mobile-menu" class="hidden md:hidden bg-[#f8fafc] border-t border-slate-200 shadow-xl absolute w-full left-0 pb-4">
|
||||
<div class="px-4 py-3 flex flex-col gap-2">
|
||||
<a href="{{ route('user.dashboard') }}"
|
||||
class="flex items-center gap-3 px-4 py-3 rounded-lg transition-all duration-200 {{ request()->routeIs('user.dashboard') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-600 font-medium hover:bg-slate-200' }}">
|
||||
<i data-lucide="layout-dashboard" class="w-4 h-4"></i> Dashboard
|
||||
</a>
|
||||
<a href="{{ route('input.activity') }}"
|
||||
class="flex items-center gap-3 px-4 py-3 rounded-lg transition-all duration-200 {{ request()->routeIs('input.activity') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-600 font-medium hover:bg-slate-200' }}">
|
||||
<i data-lucide="edit" class="w-4 h-4"></i> Input Kegiatan
|
||||
</a>
|
||||
<a href="{{ route('activity.list') }}"
|
||||
class="flex items-center gap-3 px-4 py-3 rounded-lg transition-all duration-200 {{ request()->routeIs('activity.list') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-600 font-medium hover:bg-slate-200' }}">
|
||||
<i data-lucide="folder" class="w-4 h-4"></i> Kegiatan Saya
|
||||
</a>
|
||||
<a href="{{ route('settings') ?? '#' }}"
|
||||
class="flex items-center gap-3 px-4 py-3 rounded-lg transition-all duration-200 {{ request()->routeIs('settings') ? 'text-blue-800 bg-indigo-50 font-semibold' : 'text-slate-600 font-medium hover:bg-slate-200' }}">
|
||||
<i data-lucide="settings" class="w-4 h-4"></i> Pengaturan
|
||||
</a>
|
||||
|
||||
<div class="h-px w-full bg-slate-200 my-2"></div>
|
||||
|
||||
<div class="flex items-center justify-between px-2">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-[38px] h-[38px] rounded-full bg-blue-900 text-white flex items-center justify-center font-bold text-[16px]">
|
||||
{{ strtoupper(substr(session('user')['name'] ?? 'U', 0, 1)) }}
|
||||
</div>
|
||||
<div class="leading-[1.2]">
|
||||
<div class="font-semibold text-[14px] text-slate-900">
|
||||
{{ session('user')['name'] ?? 'User' }}
|
||||
</div>
|
||||
<div class="text-[11px] text-slate-500 font-medium">
|
||||
USER
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}" class="m-0">
|
||||
@csrf
|
||||
<button type="submit" class="bg-transparent border-none cursor-pointer text-red-500 p-2 rounded-md hover:bg-red-50 transition-colors flex items-center gap-2" title="Logout">
|
||||
<i data-lucide="log-out" class="w-5 h-5"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const btn = document.getElementById('user-menu-btn');
|
||||
const menu = document.getElementById('user-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,173 @@
|
||||
@php
|
||||
$role = session('user')['role'] ?? 'user';
|
||||
$layout = in_array($role, ['admin', 'superadmin']) ? 'admin.layouts.app' : 'user.layouts.app';
|
||||
|
||||
if ($role === 'superadmin') {
|
||||
$dashboardRoute = route('admin.superadmin.dashboard');
|
||||
} elseif ($role === 'admin') {
|
||||
$dashboardRoute = route('admin.dashboard');
|
||||
} else {
|
||||
$dashboardRoute = route('user.dashboard');
|
||||
}
|
||||
@endphp
|
||||
@extends($layout)
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="max-w-4xl mx-auto mt-10 px-4 pb-20">
|
||||
|
||||
{{-- HEADER --}}
|
||||
<div class="mb-8">
|
||||
<a href="{{ $dashboardRoute }}" class="inline-flex items-center gap-2 text-sm text-slate-500 hover:text-[#0D215C] font-semibold mb-4 transition">
|
||||
<svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path d="M19 12H5M12 5l-7 7 7 7"/></svg>
|
||||
Kembali ke Dashboard
|
||||
</a>
|
||||
<h2 class="text-2xl font-bold text-[#0D215C]">
|
||||
Pengaturan Profil
|
||||
</h2>
|
||||
<p class="text-sm text-slate-400">Kelola informasi akun dan kata sandi Anda.</p>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="mb-6 p-4 bg-green-100 border border-green-200 text-green-700 rounded-xl text-sm font-medium">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="mb-6 p-4 bg-red-100 border border-red-200 text-red-700 rounded-xl text-sm font-medium">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-6 p-4 bg-red-100 border border-red-200 text-red-700 rounded-xl text-sm font-medium">
|
||||
<ul class="list-disc list-inside">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid grid-cols-1 gap-8">
|
||||
|
||||
{{-- FORM INFORMASI PROFIL --}}
|
||||
<div class="bg-white p-8 rounded-2xl shadow-sm border border-slate-100">
|
||||
<h4 class="text-lg font-bold text-[#0D215C] mb-6 flex items-center gap-2">
|
||||
<i data-lucide="user" class="w-5 h-5"></i> Informasi Pribadi
|
||||
</h4>
|
||||
|
||||
<form action="{{ route('settings.update') }}" method="POST" class="space-y-5">
|
||||
@csrf
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Nama Lengkap</label>
|
||||
<input type="text" name="name" value="{{ session('user')['name'] ?? '' }}"
|
||||
class="w-full p-3 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Username <span class="text-red-500">*</span></label>
|
||||
<input type="text" name="username" value="{{ session('user')['username'] ?? '' }}"
|
||||
class="w-full p-3 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="pt-4">
|
||||
<button type="submit" class="bg-[#0D215C] hover:bg-[#0a1a47] text-white px-8 py-3 rounded-xl font-bold transition shadow-lg shadow-blue-900/10">
|
||||
Simpan Perubahan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@if($role !== 'user')
|
||||
{{-- FORM GANTI PASSWORD --}}
|
||||
<div class="bg-white p-8 rounded-2xl shadow-sm border border-slate-100">
|
||||
<h4 class="text-lg font-bold text-[#0D215C] mb-6 flex items-center gap-2">
|
||||
<i data-lucide="lock" class="w-5 h-5"></i> Keamanan Akun
|
||||
</h4>
|
||||
|
||||
<form action="{{ route('settings.update') }}" method="POST" class="space-y-5">
|
||||
@csrf
|
||||
<input type="hidden" name="username" value="{{ session('user')['username'] ?? '' }}">
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Kata Sandi Saat Ini</label>
|
||||
<div class="relative">
|
||||
<input type="password" name="current_password" id="current_password" autocomplete="off"
|
||||
class="w-full p-3 pr-10 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition">
|
||||
<button type="button" onclick="togglePassword('current_password', this)" class="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-blue-500 focus:outline-none">
|
||||
<i data-lucide="eye" class="w-5 h-5 eye-icon"></i>
|
||||
<i data-lucide="eye-off" class="w-5 h-5 eye-off-icon hidden"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Kata Sandi Baru</label>
|
||||
<div class="relative">
|
||||
<input type="password" name="password" id="password"
|
||||
class="w-full p-3 pr-10 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition">
|
||||
<button type="button" onclick="togglePassword('password', this)" class="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-blue-500 focus:outline-none">
|
||||
<i data-lucide="eye" class="w-5 h-5 eye-icon"></i>
|
||||
<i data-lucide="eye-off" class="w-5 h-5 eye-off-icon hidden"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-sm font-semibold text-slate-700 block mb-2">Konfirmasi Kata Sandi</label>
|
||||
<div class="relative">
|
||||
<input type="password" name="password_confirmation" id="password_confirmation"
|
||||
class="w-full p-3 pr-10 rounded-xl bg-slate-50 border border-slate-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-100 outline-none transition">
|
||||
<button type="button" onclick="togglePassword('password_confirmation', this)" class="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-blue-500 focus:outline-none">
|
||||
<i data-lucide="eye" class="w-5 h-5 eye-icon"></i>
|
||||
<i data-lucide="eye-off" class="w-5 h-5 eye-off-icon hidden"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-4">
|
||||
<button type="submit" class="bg-white border border-slate-200 hover:bg-slate-50 text-[#0D215C] px-8 py-3 rounded-xl font-bold transition">
|
||||
Perbarui Kata Sandi
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
}
|
||||
});
|
||||
|
||||
function togglePassword(inputId, btn) {
|
||||
const input = document.getElementById(inputId);
|
||||
const eye = btn.querySelector('.eye-icon');
|
||||
const eyeOff = btn.querySelector('.eye-off-icon');
|
||||
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
eye.classList.add('hidden');
|
||||
eyeOff.classList.remove('hidden');
|
||||
btn.classList.add('text-blue-500');
|
||||
btn.classList.remove('text-slate-400');
|
||||
} else {
|
||||
input.type = 'password';
|
||||
eye.classList.remove('hidden');
|
||||
eyeOff.classList.add('hidden');
|
||||
btn.classList.remove('text-blue-500');
|
||||
btn.classList.add('text-slate-400');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
Reference in New Issue
Block a user