update banyak

This commit is contained in:
Endyfadlullah 2025-08-11 03:12:42 +07:00
parent 879ee41c76
commit a2f7caf21f
18 changed files with 1099 additions and 1521 deletions

View File

@ -5,6 +5,7 @@
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\DB;
use App\Models\User; use App\Models\User;
use App\Models\Admin; use App\Models\Admin;
use App\Models\Antrian; use App\Models\Antrian;
@ -76,7 +77,7 @@ public function manageUsers(Request $request)
}); });
} }
$users = $query->orderBy('created_at', 'desc')->get(); $users = $query->orderBy('created_at', 'desc')->paginate(15);
return view('admin.users.index', compact('users')); return view('admin.users.index', compact('users'));
} }
@ -527,6 +528,119 @@ public function batalAntrian(Request $request)
} }
} }
public function tambahAntrian()
{
$polis = Poli::all();
return view('admin.antrian.tambah', compact('polis'));
}
public function cariUser(Request $request)
{
$request->validate([
'search' => 'required|string|min:3'
]);
$users = User::where('nama', 'like', "%{$request->search}%")
->orWhere('no_ktp', 'like', "%{$request->search}%")
->orWhere('no_hp', 'like', "%{$request->search}%")
->limit(10)
->get(['id', 'nama', 'no_ktp', 'no_hp', 'jenis_kelamin', 'alamat']);
return response()->json([
'success' => true,
'users' => $users
]);
}
public function storeAntrianAdmin(Request $request)
{
$request->validate([
'user_id' => 'required|exists:users,id',
'poli_id' => 'required|exists:polis,id'
]);
try {
DB::beginTransaction();
$user = User::findOrFail($request->user_id);
$poli = Poli::findOrFail($request->poli_id);
// Check if user already has active queue in this poli today
$existingQueue = Antrian::where('user_id', $request->user_id)
->where('poli_id', $request->poli_id)
->whereDate('created_at', today())
->whereIn('status', ['menunggu', 'dipanggil'])
->first();
if ($existingQueue) {
return response()->json([
'success' => false,
'message' => 'User ini sudah memiliki antrian aktif di ' . $poli->nama_poli . ' hari ini.'
]);
}
// Get poli prefix
$prefix = $this->getPoliPrefix($poli->nama_poli);
// Get next queue number for the poli
$lastQueue = Antrian::where('poli_id', $request->poli_id)
->whereDate('created_at', today())
->orderBy('id', 'desc')
->first();
// Extract number from last queue (remove prefix)
$lastNumber = 0;
if ($lastQueue && $lastQueue->no_antrian) {
$lastNumber = (int) preg_replace('/^[A-Z]+/', '', $lastQueue->no_antrian);
}
$nextNumber = $lastNumber + 1;
$nextQueueNumber = $prefix . $nextNumber;
// Create new queue
$antrian = Antrian::create([
'user_id' => $request->user_id,
'poli_id' => $request->poli_id,
'no_antrian' => $nextQueueNumber,
'tanggal_antrian' => now()->toDateString(),
'status' => 'menunggu'
]);
DB::commit();
return response()->json([
'success' => true,
'message' => 'Antrian berhasil dibuat untuk ' . $user->nama . ' di ' . $poli->nama_poli . ' dengan nomor ' . $nextQueueNumber,
'antrian' => [
'id' => $antrian->id,
'no_antrian' => $nextQueueNumber,
'poli_name' => $poli->nama_poli,
'user_name' => $user->nama,
'status' => 'menunggu'
]
]);
} catch (\Exception $e) {
DB::rollback();
return response()->json([
'success' => false,
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
], 500);
}
}
private function getPoliPrefix($namaPoli)
{
$prefixMap = [
'umum' => 'U',
'gigi' => 'G',
'kesehatan jiwa' => 'J',
'kesehatan tradisional' => 'T'
];
return $prefixMap[strtolower($namaPoli)] ?? 'A';
}
public function cetakAntrian(Antrian $antrian) public function cetakAntrian(Antrian $antrian)
{ {
try { try {

View File

@ -8,11 +8,15 @@
use App\Models\Poli; use App\Models\Poli;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class DashboardController extends Controller class DashboardController extends Controller
{ {
public function index() public function index()
{ {
// Auto-batalkan antrian yang sudah dipanggil lebih dari 5 menit
$this->autoBatalkanAntrianLama();
// Get user's own queues // Get user's own queues
$antrianSaya = Antrian::with(['user', 'poli']) $antrianSaya = Antrian::with(['user', 'poli'])
->where('user_id', Auth::id()) ->where('user_id', Auth::id())
@ -25,6 +29,9 @@ public function index()
public function addQueue(Request $request) public function addQueue(Request $request)
{ {
// Auto-batalkan antrian yang sudah dipanggil lebih dari 5 menit
$this->autoBatalkanAntrianLama();
$request->validate([ $request->validate([
'poli_id' => 'required|exists:polis,id' 'poli_id' => 'required|exists:polis,id'
]); ]);
@ -71,20 +78,28 @@ public function addQueue(Request $request)
// Get poli info for prefix // Get poli info for prefix
$prefix = $this->getPoliPrefix($poli->nama_poli); $prefix = $this->getPoliPrefix($poli->nama_poli);
// Get next queue number for the poli // Get next queue number for the poli - perbaikan logika
$lastQueue = Antrian::where('poli_id', $request->poli_id) $lastQueue = Antrian::where('poli_id', $request->poli_id)
->whereDate('created_at', today()) ->whereDate('created_at', today())
->max('no_antrian'); ->orderBy('id', 'desc')
->first();
// Extract number from last queue (remove prefix) // Extract number from last queue (remove prefix)
$lastNumber = 0; $lastNumber = 0;
if ($lastQueue) { if ($lastQueue && $lastQueue->no_antrian) {
$lastNumber = (int) preg_replace('/[^0-9]/', '', $lastQueue); // Extract only the numeric part after the prefix
$lastNumber = (int) preg_replace('/^[A-Z]+/', '', $lastQueue->no_antrian);
// Debug log
\Log::info("Last queue: {$lastQueue->no_antrian}, Extracted number: {$lastNumber}");
} }
$nextNumber = $lastNumber + 1; $nextNumber = $lastNumber + 1;
$nextQueueNumber = $prefix . $nextNumber; $nextQueueNumber = $prefix . $nextNumber;
// Debug log
\Log::info("Generated next queue number: {$nextQueueNumber} for poli: {$poliName}");
// Create new queue using current user's data // Create new queue using current user's data
$antrian = Antrian::create([ $antrian = Antrian::create([
'user_id' => $user->id, 'user_id' => $user->id,
@ -188,6 +203,36 @@ private function getPoliPrefix($namaPoli)
} }
} }
/**
* Auto-batalkan antrian yang sudah dipanggil lebih dari 5 menit
* tanpa dikonfirmasi selesai oleh admin
*/
private function autoBatalkanAntrianLama()
{
try {
// Cari antrian yang sudah dipanggil lebih dari 5 menit
$antrianLama = Antrian::where('status', 'dipanggil')
->where('waktu_panggil', '<=', now()->subMinutes(5))
->get();
foreach ($antrianLama as $antrian) {
// Update status menjadi 'batal'
$antrian->update(['status' => 'batal']);
// Log untuk tracking (optional)
\Log::info("Antrian {$antrian->no_antrian} otomatis dibatalkan karena lewat 5 menit sejak dipanggil");
}
// Jika ada antrian yang dibatalkan, log jumlahnya
if ($antrianLama->count() > 0) {
\Log::info("Total {$antrianLama->count()} antrian otomatis dibatalkan karena timeout");
}
} catch (\Exception $e) {
// Log error jika terjadi masalah
\Log::error("Error saat auto-batalkan antrian: " . $e->getMessage());
}
}
public function batalAntrian(Request $request) public function batalAntrian(Request $request)
{ {
try { try {

View File

@ -19,6 +19,7 @@ public function register(): void
*/ */
public function boot(): void public function boot(): void
{ {
// // Set pagination to use Tailwind CSS styling
\Illuminate\Pagination\Paginator::useTailwind();
} }
} }

View File

@ -65,7 +65,7 @@
| |
*/ */
'timezone' => 'UTC', 'timezone' => 'Asia/Jakarta',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -0,0 +1,263 @@
@extends('layouts.app')
@section('content')
<div class="min-h-screen bg-gray-50">
@include('admin.partials.top-nav')
<div class="flex">
@include('admin.partials.sidebar')
<!-- Main Content -->
<div class="flex-1 lg:ml-0">
<div class="px-4 sm:px-6 lg:px-8 py-6 md:py-8">
<!-- Header -->
<div class="mb-6">
<h1 class="text-2xl font-bold text-gray-900 mb-2">Tambah Antrian Manual</h1>
<p class="text-gray-600">Bantu pasien yang tidak bisa antri online dengan membuat antrian manual</p>
</div>
<!-- Search User Section -->
<div class="bg-white rounded-lg shadow-sm border p-6 mb-6">
<h2 class="text-lg font-semibold text-gray-900 mb-4">Cari Pasien</h2>
<div class="flex space-x-4 mb-4">
<div class="flex-1">
<input type="text" id="searchInput" placeholder="Cari berdasarkan nama, NIK, atau nomor HP..."
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<button id="searchBtn" class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:ring-2 focus:ring-blue-500">
Cari
</button>
</div>
<!-- Search Results -->
<div id="searchResults" class="hidden">
<h3 class="text-md font-medium text-gray-900 mb-3">Hasil Pencarian:</h3>
<div id="userList" class="space-y-2"></div>
</div>
<!-- No Results Message -->
<div id="noResults" class="hidden text-center py-4 text-gray-500">
Tidak ada hasil yang ditemukan
</div>
</div>
<!-- Selected User & Poli Selection -->
<div id="userSelection" class="hidden bg-white rounded-lg shadow-sm border p-6 mb-6">
<h2 class="text-lg font-semibold text-gray-900 mb-4">Pilih Poli</h2>
<div class="mb-4">
<h3 class="text-md font-medium text-gray-700 mb-2">Pasien yang dipilih:</h3>
<div id="selectedUserInfo" class="bg-gray-50 rounded-lg p-3"></div>
</div>
<div class="mb-4">
<label for="poliSelect" class="block text-sm font-medium text-gray-700 mb-2">Pilih Poli:</label>
<select id="poliSelect" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="">Pilih Poli</option>
@foreach($polis as $poli)
<option value="{{ $poli->id }}">{{ ucfirst($poli->nama_poli) }}</option>
@endforeach
</select>
</div>
<button id="createQueueBtn" class="w-full px-6 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 focus:ring-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed" disabled>
Buat Antrian
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Logout Form -->
<form id="logout-form" method="POST" action="{{ route('logout') }}" class="hidden">
@csrf
</form>
<script>
let selectedUser = null;
// Search functionality
document.getElementById('searchBtn').addEventListener('click', function() {
const searchTerm = document.getElementById('searchInput').value.trim();
if (searchTerm.length < 3) {
Swal.fire({
icon: 'warning',
title: 'Peringatan',
text: 'Masukkan minimal 3 karakter untuk pencarian',
confirmButtonColor: '#3B82F6'
});
return;
}
searchUsers(searchTerm);
});
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('searchBtn').click();
}
});
function searchUsers(searchTerm) {
fetch('{{ route("admin.antrian.cari-user") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({ search: searchTerm })
})
.then(response => response.json())
.then(data => {
if (data.success && data.users.length > 0) {
displaySearchResults(data.users);
} else {
showNoResults();
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Terjadi kesalahan saat mencari user',
confirmButtonColor: '#EF4444'
});
});
}
function displaySearchResults(users) {
const userList = document.getElementById('userList');
const searchResults = document.getElementById('searchResults');
const noResults = document.getElementById('noResults');
userList.innerHTML = '';
users.forEach(user => {
const userDiv = document.createElement('div');
userDiv.className = 'flex items-center justify-between p-3 border border-gray-200 rounded-lg hover:bg-gray-50 cursor-pointer';
userDiv.innerHTML = `
<div>
<div class="font-medium text-gray-900">${user.nama}</div>
<div class="text-sm text-gray-600">NIK: ${user.no_ktp} | HP: ${user.no_hp}</div>
<div class="text-sm text-gray-500">${user.jenis_kelamin} - ${user.alamat}</div>
</div>
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm" onclick="selectUser(${JSON.stringify(user).replace(/"/g, '&quot;')})">
Pilih
</button>
`;
userList.appendChild(userDiv);
});
searchResults.classList.remove('hidden');
noResults.classList.add('hidden');
}
function showNoResults() {
document.getElementById('searchResults').classList.add('hidden');
document.getElementById('noResults').classList.remove('hidden');
}
function selectUser(user) {
selectedUser = user;
// Display selected user info
const selectedUserInfo = document.getElementById('selectedUserInfo');
selectedUserInfo.innerHTML = `
<div class="font-medium text-gray-900">${user.nama}</div>
<div class="text-sm text-gray-600">NIK: ${user.no_ktp} | HP: ${user.no_hp}</div>
<div class="text-sm text-gray-500">${user.jenis_kelamin} - ${user.alamat}</div>
`;
// Show user selection section
document.getElementById('userSelection').classList.remove('hidden');
// Reset poli selection
document.getElementById('poliSelect').value = '';
document.getElementById('createQueueBtn').disabled = true;
// Hide search results
document.getElementById('searchResults').classList.add('hidden');
document.getElementById('searchInput').value = '';
}
// Poli selection change
document.getElementById('poliSelect').addEventListener('change', function() {
const createQueueBtn = document.getElementById('createQueueBtn');
createQueueBtn.disabled = !this.value;
});
// Create queue
document.getElementById('createQueueBtn').addEventListener('click', function() {
if (!selectedUser || !document.getElementById('poliSelect').value) {
Swal.fire({
icon: 'warning',
title: 'Peringatan',
text: 'Pilih user dan poli terlebih dahulu',
confirmButtonColor: '#3B82F6'
});
return;
}
createQueue();
});
function createQueue() {
const poliId = document.getElementById('poliSelect').value;
fetch('{{ route("admin.antrian.store") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
user_id: selectedUser.id,
poli_id: poliId
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: data.message,
confirmButtonColor: '#10B981'
}).then(() => {
resetForm();
});
} else {
Swal.fire({
icon: 'error',
title: 'Error',
text: data.message,
confirmButtonColor: '#EF4444'
});
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Terjadi kesalahan saat membuat antrian',
confirmButtonColor: '#EF4444'
});
});
}
function resetForm() {
selectedUser = null;
document.getElementById('userSelection').classList.add('hidden');
document.getElementById('searchResults').classList.add('hidden');
document.getElementById('noResults').classList.add('hidden');
document.getElementById('searchInput').value = '';
document.getElementById('poliSelect').value = '';
document.getElementById('createQueueBtn').disabled = true;
}
</script>
@include('admin.partials.sidebar-script')
@endsection

View File

@ -4,143 +4,10 @@
@section('content') @section('content')
<div class="min-h-screen bg-gray-50"> <div class="min-h-screen bg-gray-50">
<!-- Top Navigation --> @include('admin.partials.top-nav')
<nav class="bg-white shadow-lg sticky top-0 z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<button id="sidebar-toggle" class="lg:hidden text-gray-700 hover:text-primary mr-4">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="flex-shrink-0">
<h1 class="text-xl md:text-2xl font-bold text-primary">🏥 Admin Puskesmas</h1>
</div>
</div>
<div class="hidden md:flex items-center space-x-4">
<a href="{{ route('display') }}"
class="text-gray-700 hover:text-primary px-3 py-2 rounded-md text-sm font-medium transition duration-200">Display</a>
<span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">
Logout
</button>
</div>
</div>
</div>
</nav>
<div class="flex"> <div class="flex">
<!-- Sidebar --> @include('admin.partials.sidebar')
<aside id="sidebar"
class="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-xl transform -translate-x-full lg:translate-x-0 lg:static lg:inset-0 transition duration-200 ease-in-out">
<div class="flex items-center justify-between h-16 px-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">Menu Admin</h2>
<button id="sidebar-close" class="lg:hidden text-gray-500 hover:text-gray-700">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12">
</path>
</svg>
</button>
</div>
<nav class="px-6 py-6">
<div class="space-y-6">
<!-- Dashboard -->
<div>
<a href="{{ route('admin.dashboard') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.dashboard') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5a2 2 0 012-2h4a2 2 0 012 2v6H8V5z"></path>
</svg>
<span class="font-medium">Dashboard</span>
</a>
</div>
<!-- Daftar Antrian -->
<div>
<div
class="flex items-center px-4 py-2 text-sm font-semibold text-gray-500 uppercase tracking-wider">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2">
</path>
</svg>
Daftar Antrian
</div>
<div class="mt-3 space-y-1">
<a href="{{ route('admin.poli.umum') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.umum') ? 'bg-blue-50 text-blue-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-blue-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Umum</span>
</a>
<a href="{{ route('admin.poli.gigi') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.gigi') ? 'bg-green-50 text-green-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-green-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Gigi</span>
</a>
<a href="{{ route('admin.poli.jiwa') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.jiwa') ? 'bg-purple-50 text-purple-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-purple-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Jiwa</span>
</a>
<a href="{{ route('admin.poli.tradisional') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.tradisional') ? 'bg-yellow-50 text-yellow-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-yellow-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Tradisional</span>
</a>
</div>
</div>
<!-- Kelola User -->
<div>
<a href="{{ route('admin.users.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.users.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z">
</path>
</svg>
<span class="font-medium">Kelola User</span>
</a>
</div>
<!-- Laporan -->
<div>
<a href="{{ route('admin.laporan.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.laporan.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
</path>
</svg>
<span class="font-medium">Laporan</span>
</a>
</div>
<!-- Display -->
<div>
<a href="{{ route('display') }}"
class="flex items-center px-4 py-3 rounded-xl text-gray-700 hover:bg-gray-50 transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z">
</path>
</svg>
<span class="font-medium">Display</span>
</a>
</div>
</div>
</nav>
</aside>
<!-- Overlay for mobile -->
<div id="sidebar-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden hidden"></div>
<!-- Main Content --> <!-- Main Content -->
<div class="flex-1 lg:ml-0"> <div class="flex-1 lg:ml-0">
@ -218,194 +85,114 @@ class="flex items-center px-4 py-3 rounded-xl text-gray-700 hover:bg-gray-50 tra
</div> </div>
</div> </div>
<!-- Recent Queue Table --> <!-- Quick Actions -->
<div class="bg-white rounded-2xl shadow-xl overflow-hidden"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<div class="px-6 py-4 border-b border-gray-200"> <div class="bg-white rounded-2xl shadow-xl p-6">
<h3 class="text-lg font-semibold text-gray-900">Antrian Terbaru</h3> <div class="flex items-center mb-4">
<div class="p-3 rounded-full bg-blue-100 text-blue-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
</div>
<h3 class="text-lg font-semibold text-gray-900 ml-3">Tambah Antrian</h3>
</div>
<p class="text-gray-600 mb-4">Bantu pasien yang tidak bisa antri online</p>
<a href="{{ route('admin.antrian.tambah') }}"
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition duration-200">
Tambah Antrian
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5l7 7-7 7"></path>
</svg>
</a>
</div> </div>
<!-- Desktop Table --> <div class="bg-white rounded-2xl shadow-xl p-6">
<div class="hidden lg:block overflow-x-auto"> <div class="flex items-center mb-4">
<table class="min-w-full divide-y divide-gray-200"> <div class="p-3 rounded-full bg-green-100 text-green-600">
<thead class="bg-gray-50"> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<tr>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
No. Antrian</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Nama</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Poli</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Waktu</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse($antrianTerbaru ?? [] as $antrian)
<tr class="hover:bg-gray-50 transition duration-200">
<td class="px-6 py-4 whitespace-nowrap">
<span
class="text-lg font-semibold text-primary">{{ $antrian->no_antrian ?? 'N/A' }}</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">
{{ $antrian->user?->nama ?? 'N/A' }}
</div>
<div class="text-sm text-gray-500">{{ $antrian->user?->no_hp ?? 'N/A' }}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800">
{{ $antrian->poli->nama_poli ?? 'N/A' }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
@if (($antrian->status ?? '') == 'menunggu')
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-yellow-100 text-yellow-800">
Menunggu
</span>
@elseif(($antrian->status ?? '') == 'dipanggil')
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800">
Dipanggil
</span>
@elseif(($antrian->status ?? '') == 'selesai')
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
Selesai
</span>
@else
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
Batal
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ $antrian->created_at?->format('H:i') ?? 'N/A' }}
</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-6 py-8 text-center text-gray-500">
<svg class="w-12 h-12 mx-auto mb-4 text-gray-400" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"> d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z">
</path> </path>
</svg> </svg>
<p class="text-lg font-medium">Belum ada antrian hari ini</p>
<p class="text-sm text-gray-400">Antrian akan muncul di sini setelah ada
pendaftaran</p>
</td>
</tr>
@endforelse
</tbody>
</table>
</div> </div>
<h3 class="text-lg font-semibold text-gray-900 ml-3">Display Antrian</h3>
<!-- Mobile Table -->
<div class="lg:hidden">
@forelse($antrianTerbaru ?? [] as $antrian)
<div class="border-b border-gray-200 p-4 hover:bg-gray-50">
<!-- 4 Kolom Penting untuk Mobile -->
<div class="grid grid-cols-2 gap-4 mb-3">
<div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">No.
Antrian</p>
<span
class="text-lg font-semibold text-primary">{{ $antrian->no_antrian ?? 'N/A' }}</span>
</div> </div>
<div> <p class="text-gray-600 mb-4">Lihat display antrian untuk pasien</p>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Nama</p> <a href="{{ route('display') }}"
<div class="text-sm font-medium text-gray-900"> class="inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition duration-200">
{{ $antrian->user?->nama ?? 'N/A' }}</div> Lihat Display
</div> <svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Poli</p>
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800">
{{ $antrian->poli->nama_poli ?? 'N/A' }}
</span>
</div>
<div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Status
</p>
@if (($antrian->status ?? '') == 'menunggu')
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-yellow-100 text-yellow-800">
Menunggu
</span>
@elseif(($antrian->status ?? '') == 'dipanggil')
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800">
Dipanggil
</span>
@elseif(($antrian->status ?? '') == 'selesai')
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
Selesai
</span>
@else
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
Batal
</span>
@endif
</div>
</div>
<!-- Button Selengkapnya untuk Mobile -->
<div class="border-t border-gray-100 pt-3">
<button onclick="toggleDashboardDetails({{ $antrian->id }})"
class="text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center">
<span id="dashboard-btn-text-{{ $antrian->id }}">Selengkapnya</span>
<svg id="dashboard-icon-{{ $antrian->id }}"
class="w-4 h-4 ml-1 transform transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path> d="M9 5l7 7-7 7"></path>
</svg> </svg>
</button> </a>
</div>
<!-- Detail Tambahan (Hidden by default) --> <div class="bg-white rounded-2xl shadow-xl p-6">
<div id="dashboard-details-{{ $antrian->id }}" class="hidden mt-3 space-y-2"> <div class="flex items-center mb-4">
<div class="grid grid-cols-1 gap-2 text-sm"> <div class="p-3 rounded-full bg-purple-100 text-purple-600">
<div> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<span class="font-medium text-gray-700">No. HP:</span>
<span
class="text-gray-600">{{ $antrian->user?->no_hp ?? 'N/A' }}</span>
</div>
<div>
<span class="font-medium text-gray-700">Waktu Daftar:</span>
<span
class="text-gray-600">{{ $antrian->created_at?->format('H:i') ?? 'N/A' }}</span>
</div>
</div>
</div>
</div>
</div>
@empty
<div class="p-8 text-center text-gray-500">
<svg class="w-12 h-12 mx-auto mb-4 text-gray-400" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"> d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
</path> </path>
</svg> </svg>
<p class="text-lg font-medium">Belum ada antrian hari ini</p>
<p class="text-sm text-gray-400">Antrian akan muncul di sini setelah ada
pendaftaran</p>
</div> </div>
@endforelse <h3 class="text-lg font-semibold text-gray-900 ml-3">Laporan</h3>
</div>
<p class="text-gray-600 mb-4">Lihat laporan dan statistik antrian</p>
<a href="{{ route('admin.laporan.index') }}"
class="inline-flex items-center px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition duration-200">
Lihat Laporan
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5l7 7-7 7"></path>
</svg>
</a>
</div>
</div>
<!-- Recent Activity -->
<div class="bg-white rounded-2xl shadow-xl p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Aktivitas Terbaru</h3>
<div class="space-y-4">
<div class="flex items-center p-4 bg-gray-50 rounded-lg">
<div class="p-2 rounded-full bg-blue-100 text-blue-600">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Antrian baru ditambahkan</p>
<p class="text-xs text-gray-500">Poli Umum - 2 menit yang lalu</p>
</div>
</div>
<div class="flex items-center p-4 bg-gray-50 rounded-lg">
<div class="p-2 rounded-full bg-green-100 text-green-600">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7"></path>
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Antrian selesai</p>
<p class="text-xs text-gray-500">Poli Gigi - 5 menit yang lalu</p>
</div>
</div>
<div class="flex items-center p-4 bg-gray-50 rounded-lg">
<div class="p-2 rounded-full bg-yellow-100 text-yellow-600">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900">Antrian dipanggil</p>
<p class="text-xs text-gray-500">Poli Jiwa - 8 menit yang lalu</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -413,116 +200,10 @@ class="text-gray-600">{{ $antrian->created_at?->format('H:i') ?? 'N/A' }}</span>
</div> </div>
</div> </div>
@push('scripts') <!-- Logout Form -->
<script> <form id="logout-form" method="POST" action="{{ route('logout') }}" class="hidden">
// Sidebar toggle for mobile @csrf
document.getElementById('sidebar-toggle').addEventListener('click', function() { </form>
document.getElementById('sidebar').classList.remove('-translate-x-full');
document.getElementById('sidebar-overlay').classList.remove('hidden');
});
document.getElementById('sidebar-close').addEventListener('click', function() { @include('admin.partials.sidebar-script')
document.getElementById('sidebar').classList.add('-translate-x-full');
document.getElementById('sidebar-overlay').classList.add('hidden');
});
document.getElementById('sidebar-overlay').addEventListener('click', function() {
document.getElementById('sidebar').classList.add('-translate-x-full');
document.getElementById('sidebar-overlay').classList.add('hidden');
});
// Show SweetAlert2 for success messages
@if (session('success'))
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: '{{ session('success') }}',
confirmButtonText: 'OK',
confirmButtonColor: '#10B981',
timer: 3000,
timerProgressBar: true
});
@endif
// Show welcome message for admin
@if (session('success') && str_contains(session('success'), 'Selamat datang'))
Swal.fire({
icon: 'success',
title: 'Selamat Datang!',
text: '{{ session('success') }}',
confirmButtonText: 'OK',
confirmButtonColor: '#10B981',
timer: 4000,
timerProgressBar: true,
showClass: {
popup: 'animate__animated animate__fadeInDown'
},
hideClass: {
popup: 'animate__animated animate__fadeOutUp'
}
});
@endif
// Show SweetAlert2 for error messages
@if (session('error'))
Swal.fire({
icon: 'error',
title: 'Error!',
text: '{{ session('error') }}',
confirmButtonText: 'OK',
confirmButtonColor: '#EF4444',
timer: 4000,
timerProgressBar: true
});
@endif
// Function to confirm logout
function confirmLogout() {
Swal.fire({
title: 'Konfirmasi Logout',
text: 'Apakah Anda yakin ingin keluar dari sistem?',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Logout',
cancelButtonText: 'Batal',
confirmButtonColor: '#EF4444',
cancelButtonColor: '#6B7280'
}).then((result) => {
if (result.isConfirmed) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '{{ route('logout') }}';
const csrfToken = document.createElement('input');
csrfToken.type = 'hidden';
csrfToken.name = '_token';
csrfToken.value = '{{ csrf_token() }}';
form.appendChild(csrfToken);
document.body.appendChild(form);
form.submit();
}
});
}
// Function to toggle dashboard details
function toggleDashboardDetails(antrianId) {
const detailsElement = document.getElementById(`dashboard-details-${antrianId}`);
const buttonTextElement = document.getElementById(`dashboard-btn-text-${antrianId}`);
const iconElement = document.getElementById(`dashboard-icon-${antrianId}`);
if (detailsElement.classList.contains('hidden')) {
detailsElement.classList.remove('hidden');
buttonTextElement.textContent = 'Sembunyikan';
iconElement.classList.remove('transform', 'rotate-180');
iconElement.classList.add('transform', 'rotate-0');
} else {
detailsElement.classList.add('hidden');
buttonTextElement.textContent = 'Selengkapnya';
iconElement.classList.remove('transform', 'rotate-0');
iconElement.classList.add('transform', 'rotate-180');
}
}
</script>
@endpush
@endsection @endsection

View File

@ -1,536 +0,0 @@
@extends('layouts.app')
@section('title', 'Indonesian TTS Settings')
@section('content')
<div class="min-h-screen bg-gray-50">
<!-- Top Navigation -->
<nav class="bg-white shadow-lg sticky top-0 z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<button id="sidebar-toggle" class="lg:hidden text-gray-700 hover:text-primary mr-4">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="flex-shrink-0">
<h1 class="text-xl md:text-2xl font-bold text-primary">🏥 Admin Puskesmas</h1>
</div>
</div>
<div class="hidden md:flex items-center space-x-4">
<a href="{{ route('display') }}"
class="text-gray-700 hover:text-primary px-3 py-2 rounded-md text-sm font-medium transition duration-200">Display</a>
<span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">
Logout
</button>
</div>
</div>
</div>
</nav>
<div class="flex">
<!-- Sidebar -->
<aside id="sidebar"
class="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-xl transform -translate-x-full lg:translate-x-0 lg:static lg:inset-0 transition duration-200 ease-in-out">
<div class="flex items-center justify-between h-16 px-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">Menu Admin</h2>
<button id="sidebar-close" class="lg:hidden text-gray-500 hover:text-gray-700">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12">
</path>
</svg>
</button>
</div>
<nav class="px-6 py-6">
<div class="space-y-6">
<!-- Dashboard -->
<div>
<a href="{{ route('admin.dashboard') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.dashboard') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5a2 2 0 012-2h4a2 2 0 012 2v6H8V5z"></path>
</svg>
<span class="font-medium">Dashboard</span>
</a>
</div>
<!-- Daftar Antrian -->
<div>
<div
class="flex items-center px-4 py-2 text-sm font-semibold text-gray-500 uppercase tracking-wider">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2">
</path>
</svg>
Daftar Antrian
</div>
<div class="mt-3 space-y-1">
<a href="{{ route('admin.poli.umum') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.umum') ? 'bg-blue-50 text-blue-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-blue-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Umum</span>
</a>
<a href="{{ route('admin.poli.gigi') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.gigi') ? 'bg-green-50 text-green-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-green-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Gigi</span>
</a>
<a href="{{ route('admin.poli.jiwa') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.jiwa') ? 'bg-purple-50 text-purple-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-purple-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Jiwa</span>
</a>
<a href="{{ route('admin.poli.tradisional') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.tradisional') ? 'bg-yellow-50 text-yellow-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-yellow-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Tradisional</span>
</a>
</div>
</div>
<!-- Kelola User -->
<div>
<a href="{{ route('admin.users.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.users.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z">
</path>
</svg>
<span class="font-medium">Kelola User</span>
</a>
</div>
<!-- Laporan -->
<div>
<a href="{{ route('admin.laporan.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.laporan.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
</path>
</svg>
<span class="font-medium">Laporan</span>
</a>
</div>
<!-- Indonesian TTS Settings -->
<div>
<a href="{{ route('admin.indonesian-tts.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.indonesian-tts.*') ? 'bg-green-50 text-green-700 border border-green-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z">
</path>
</svg>
<span class="font-medium">Indonesian TTS</span>
</a>
</div>
<!-- Display -->
<div>
<a href="{{ route('display') }}"
class="flex items-center px-4 py-3 rounded-xl text-gray-700 hover:bg-gray-50 transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z">
</path>
</svg>
<span class="font-medium">Display</span>
</a>
</div>
</div>
</nav>
</aside>
<!-- Overlay for mobile -->
<div id="sidebar-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden hidden"></div>
<!-- Main Content -->
<div class="flex-1 lg:ml-0">
<div class="px-4 sm:px-6 lg:px-8 py-6 md:py-8">
<!-- Header -->
<div class="mb-8 animate-fade-in">
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-2">Indonesian TTS Settings</h1>
<p class="text-gray-600 text-lg">Kelola pengaturan Text-to-Speech Indonesia</p>
</div>
<!-- Status Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Indonesian TTS Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="indonesian-tts-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Indonesian TTS</h3>
<p id="indonesian-tts-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
<!-- Coqui TTS Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="coqui-tts-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Coqui TTS</h3>
<p id="coqui-tts-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
<!-- Model Files Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="model-files-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Model Files</h3>
<p id="model-files-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
<!-- Speakers Status -->
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div id="speakers-status"
class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<svg class="w-5 h-5 text-gray-500" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900">Available Speakers</h3>
<p id="speakers-text" class="text-sm text-gray-500">Checking...</p>
</div>
</div>
</div>
</div>
<!-- Test TTS Section -->
<div class="bg-white rounded-2xl shadow-xl p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Test Indonesian TTS</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label for="test-text" class="block text-sm font-medium text-gray-700 mb-2">Text untuk
Test</label>
<textarea id="test-text" rows="4"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Masukkan text untuk test TTS...">Nomor antrian U Lima, silakan menuju ke Poli Umum</textarea>
</div>
<div class="flex items-end">
<button id="test-tts-btn"
class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-md font-medium transition duration-200">
Test TTS
</button>
</div>
</div>
<div id="test-result" class="mt-4 hidden">
<div class="bg-gray-50 rounded-lg p-4">
<h4 class="font-semibold text-gray-900 mb-2">Hasil Test:</h4>
<p id="test-result-text" class="text-sm text-gray-600"></p>
</div>
</div>
</div>
<!-- Installation Instructions -->
<div class="bg-white rounded-2xl shadow-xl p-8 mb-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Instalasi Indonesian TTS</h2>
<div id="installation-instructions" class="prose max-w-none">
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-yellow-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z">
</path>
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-yellow-800">Perhatian</h3>
<div class="mt-2 text-sm text-yellow-700">
<p>Indonesian TTS memerlukan instalasi Coqui TTS dan model files. Ikuti
langkah-langkah di bawah ini.</p>
</div>
</div>
</div>
</div>
<div id="installation-steps" class="space-y-4">
<!-- Installation steps will be loaded here -->
</div>
</div>
</div>
<!-- Download Model Files -->
<div class="bg-white rounded-2xl shadow-xl p-8">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Download Model Files</h2>
<div id="download-info" class="space-y-4">
<!-- Download info will be loaded here -->
</div>
</div>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
// Sidebar toggle for mobile
document.getElementById('sidebar-toggle').addEventListener('click', function() {
document.getElementById('sidebar').classList.remove('-translate-x-full');
document.getElementById('sidebar-overlay').classList.remove('hidden');
});
document.getElementById('sidebar-close').addEventListener('click', function() {
document.getElementById('sidebar').classList.add('-translate-x-full');
document.getElementById('sidebar-overlay').classList.add('hidden');
});
document.getElementById('sidebar-overlay').addEventListener('click', function() {
document.getElementById('sidebar').classList.add('-translate-x-full');
document.getElementById('sidebar-overlay').classList.add('hidden');
});
// Load status on page load
document.addEventListener('DOMContentLoaded', function() {
loadStatus();
loadInstallationInstructions();
loadDownloadInfo();
});
// Load Indonesian TTS status
function loadStatus() {
fetch('{{ route('admin.indonesian-tts.status') }}')
.then(response => response.json())
.then(data => {
updateStatusCard('indonesian-tts', data.indonesian_tts_available, 'Indonesian TTS');
updateStatusCard('coqui-tts', data.coqui_tts_installed, 'Coqui TTS');
updateStatusCard('model-files', data.model_files_exist, 'Model Files');
const speakersCount = Object.keys(data.available_speakers || {}).length;
updateStatusCard('speakers', speakersCount > 0, `${speakersCount} Speakers Available`);
})
.catch(error => {
console.error('Error loading status:', error);
});
}
// Update status card
function updateStatusCard(id, isAvailable, text) {
const statusElement = document.getElementById(`${id}-status`);
const textElement = document.getElementById(`${id}-text`);
if (isAvailable) {
statusElement.className = 'w-8 h-8 rounded-full bg-green-100 flex items-center justify-center';
statusElement.innerHTML =
'<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>';
textElement.textContent = text;
textElement.className = 'text-sm text-green-600';
} else {
statusElement.className = 'w-8 h-8 rounded-full bg-red-100 flex items-center justify-center';
statusElement.innerHTML =
'<svg class="w-5 h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>';
textElement.textContent = 'Not Available';
textElement.className = 'text-sm text-red-600';
}
}
// Load installation instructions
function loadInstallationInstructions() {
fetch('{{ route('admin.indonesian-tts.install') }}')
.then(response => response.json())
.then(data => {
const container = document.getElementById('installation-steps');
const steps = data.instructions.steps;
let html = '<ol class="list-decimal list-inside space-y-2">';
steps.forEach(step => {
if (step.trim() === '') {
html += '</ol><ol class="list-decimal list-inside space-y-2 mt-4">';
} else {
html += `<li class="text-sm text-gray-700">${step}</li>`;
}
});
html += '</ol>';
container.innerHTML = html;
})
.catch(error => {
console.error('Error loading installation instructions:', error);
});
}
// Load download info
function loadDownloadInfo() {
fetch('{{ route('admin.indonesian-tts.download') }}')
.then(response => response.json())
.then(data => {
const container = document.getElementById('download-info');
const info = data.download_info;
let html = `
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
<h3 class="text-lg font-semibold text-blue-900 mb-2">${info.title}</h3>
<p class="text-blue-700">${info.description}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
`;
Object.entries(info.files).forEach(([filename, url]) => {
html += `
<div class="bg-gray-50 rounded-lg p-4">
<h4 class="font-semibold text-gray-900 mb-2">${filename}</h4>
<a href="${url}" target="_blank"
class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Download
</a>
</div>
`;
});
html += '</div>';
html += '<div class="bg-gray-50 rounded-lg p-4">';
html += '<h4 class="font-semibold text-gray-900 mb-2">Langkah Manual:</h4>';
html += '<ol class="list-decimal list-inside space-y-1 text-sm text-gray-700">';
info.manual_steps.forEach(step => {
html += `<li>${step}</li>`;
});
html += '</ol></div>';
container.innerHTML = html;
})
.catch(error => {
console.error('Error loading download info:', error);
});
}
// Test TTS functionality
document.getElementById('test-tts-btn').addEventListener('click', function() {
const text = document.getElementById('test-text').value;
const button = this;
const resultDiv = document.getElementById('test-result');
const resultText = document.getElementById('test-result-text');
if (!text.trim()) {
Swal.fire({
icon: 'warning',
title: 'Text Kosong',
text: 'Masukkan text untuk test TTS'
});
return;
}
button.disabled = true;
button.textContent = 'Testing...';
fetch('{{ route('admin.indonesian-tts.test') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
text: text
})
})
.then(response => response.json())
.then(data => {
resultDiv.classList.remove('hidden');
if (data.success) {
resultText.textContent = `TTS berhasil dibuat! Type: ${data.tts_type}`;
resultText.className = 'text-sm text-green-600';
if (data.audio_url) {
resultText.innerHTML +=
`<br><audio controls class="mt-2"><source src="${data.audio_url}" type="audio/wav">Your browser does not support the audio element.</audio>`;
}
} else {
resultText.textContent = `TTS gagal: ${data.message}`;
resultText.className = 'text-sm text-red-600';
}
})
.catch(error => {
resultDiv.classList.remove('hidden');
resultText.textContent = `Error: ${error.message}`;
resultText.className = 'text-sm text-red-600';
})
.finally(() => {
button.disabled = false;
button.textContent = 'Test TTS';
});
});
function confirmLogout() {
Swal.fire({
title: 'Konfirmasi Logout',
text: 'Apakah Anda yakin ingin keluar dari sistem?',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Logout',
cancelButtonText: 'Batal',
confirmButtonColor: '#EF4444',
cancelButtonColor: '#6B7280'
}).then((result) => {
if (result.isConfirmed) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '{{ route('logout') }}';
const csrfToken = document.createElement('input');
csrfToken.type = 'hidden';
csrfToken.name = '_token';
csrfToken.value = '{{ csrf_token() }}';
form.appendChild(csrfToken);
document.body.appendChild(form);
form.submit();
}
});
}
</script>
@endpush
@endsection

View File

@ -20,8 +20,7 @@
</div> </div>
</div> </div>
<div class="hidden md:flex items-center space-x-4"> <div class="hidden md:flex items-center space-x-4">
<a href="{{ route('display') }}"
class="text-gray-700 hover:text-primary px-3 py-2 rounded-md text-sm font-medium transition duration-200">Display</a>
<span class="text-gray-700">Selamat datang, Admin</span> <span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()" <button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200"> class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">

View File

@ -0,0 +1,49 @@
<script>
// Sidebar functionality
document.addEventListener('DOMContentLoaded', function() {
const sidebar = document.getElementById('sidebar');
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebarClose = document.getElementById('sidebar-close');
const sidebarOverlay = document.getElementById('sidebar-overlay');
const mainContent = document.querySelector('.flex-1');
// Toggle sidebar on mobile
if (sidebarToggle) {
sidebarToggle.addEventListener('click', function() {
sidebar.classList.remove('-translate-x-full');
sidebarOverlay.classList.remove('hidden');
});
}
// Close sidebar on mobile
if (sidebarClose) {
sidebarClose.addEventListener('click', function() {
sidebar.classList.add('-translate-x-full');
sidebarOverlay.classList.add('hidden');
});
}
// Close sidebar when clicking overlay
if (sidebarOverlay) {
sidebarOverlay.addEventListener('click', function() {
sidebar.classList.add('-translate-x-full');
sidebarOverlay.classList.add('hidden');
});
}
// Close sidebar on escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
sidebar.classList.add('-translate-x-full');
sidebarOverlay.classList.add('hidden');
}
});
});
// Logout confirmation
function confirmLogout() {
if (confirm('Apakah Anda yakin ingin keluar?')) {
document.getElementById('logout-form').submit();
}
}
</script>

View File

@ -0,0 +1,121 @@
<!-- Sidebar -->
<aside id="sidebar"
class="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-xl transform -translate-x-full lg:translate-x-0 lg:static lg:inset-0 transition duration-200 ease-in-out">
<div class="flex items-center justify-between h-16 px-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">Menu Admin</h2>
<button id="sidebar-close" class="lg:hidden text-gray-500 hover:text-gray-700">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12">
</path>
</svg>
</button>
</div>
<nav class="px-6 py-6">
<div class="space-y-6">
<!-- Dashboard -->
<div>
<a href="{{ route('admin.dashboard') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.dashboard') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5a2 2 0 012-2h4a2 2 0 012 2v6H8V5z"></path>
</svg>
<span class="font-medium">Dashboard</span>
</a>
</div>
<!-- Daftar Antrian -->
<div>
<div class="flex items-center px-4 py-2 text-sm font-semibold text-gray-500 uppercase tracking-wider">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2">
</path>
</svg>
Daftar Antrian
</div>
<div class="mt-3 space-y-1">
<a href="{{ route('admin.poli.umum') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.umum') ? 'bg-blue-50 text-blue-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-blue-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Umum</span>
</a>
<a href="{{ route('admin.poli.gigi') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.gigi') ? 'bg-green-50 text-green-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-green-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Gigi</span>
</a>
<a href="{{ route('admin.poli.jiwa') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.jiwa') ? 'bg-purple-50 text-purple-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-purple-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Jiwa</span>
</a>
<a href="{{ route('admin.poli.tradisional') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.tradisional') ? 'bg-yellow-50 text-yellow-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-yellow-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Tradisional</span>
</a>
</div>
</div>
<!-- Tambah Antrian Manual -->
<div>
<a href="{{ route('admin.antrian.tambah') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.antrian.tambah') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
<span class="font-medium">Tambah Antrian</span>
</a>
</div>
<!-- Kelola User -->
<div>
<a href="{{ route('admin.users.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.users.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z">
</path>
</svg>
<span class="font-medium">Kelola User</span>
</a>
</div>
<!-- Laporan -->
<div>
<a href="{{ route('admin.laporan.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.laporan.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
</path>
</svg>
<span class="font-medium">Laporan</span>
</a>
</div>
<!-- Display -->
<div>
<a href="{{ route('display') }}"
class="flex items-center px-4 py-3 rounded-xl text-gray-700 hover:bg-gray-50 transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z">
</path>
</svg>
<span class="font-medium">Display</span>
</a>
</div>
</div>
</nav>
</aside>
<!-- Overlay for mobile -->
<div id="sidebar-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden hidden"></div>

View File

@ -0,0 +1,26 @@
<!-- Top Navigation -->
<nav class="bg-white shadow-lg sticky top-0 z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<button id="sidebar-toggle" class="lg:hidden text-gray-700 hover:text-primary mr-4">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="flex-shrink-0">
<h1 class="text-xl md:text-2xl font-bold text-primary">🏥 Admin Puskesmas</h1>
</div>
</div>
<div class="hidden md:flex items-center space-x-4">
<span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">
Logout
</button>
</div>
</div>
</div>
</nav>

View File

@ -20,8 +20,7 @@
</div> </div>
</div> </div>
<div class="hidden md:flex items-center space-x-4"> <div class="hidden md:flex items-center space-x-4">
<a href="{{ route('display') }}"
class="text-gray-700 hover:text-primary px-3 py-2 rounded-md text-sm font-medium transition duration-200">Display</a>
<span class="text-gray-700">Selamat datang, Admin</span> <span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()" <button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200"> class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">

View File

@ -20,8 +20,7 @@
</div> </div>
</div> </div>
<div class="hidden md:flex items-center space-x-4"> <div class="hidden md:flex items-center space-x-4">
<a href="{{ route('display') }}"
class="text-gray-700 hover:text-primary px-3 py-2 rounded-md text-sm font-medium transition duration-200">Display</a>
<span class="text-gray-700">Selamat datang, Admin</span> <span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()" <button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200"> class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">

View File

@ -4,198 +4,23 @@
@section('content') @section('content')
<div class="min-h-screen bg-gray-50"> <div class="min-h-screen bg-gray-50">
<!-- Top Navigation --> @include('admin.partials.top-nav')
<nav class="bg-white shadow-lg sticky top-0 z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<button id="sidebar-toggle" class="lg:hidden text-gray-700 hover:text-primary mr-4">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="flex-shrink-0">
<h1 class="text-xl md:text-2xl font-bold text-primary">🏥 Admin Puskesmas</h1>
</div>
</div>
<div class="hidden md:flex items-center space-x-4">
<a href="{{ route('display') }}"
class="text-gray-700 hover:text-primary px-3 py-2 rounded-md text-sm font-medium transition duration-200">Display</a>
<span class="text-gray-700">Selamat datang, Admin</span>
<button onclick="confirmLogout()"
class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-md text-sm font-medium transition duration-200">
Logout
</button>
</div>
</div>
</div>
</nav>
<div class="flex"> <div class="flex">
<!-- Sidebar --> @include('admin.partials.sidebar')
<aside id="sidebar"
class="fixed inset-y-0 left-0 z-50 w-64 bg-white shadow-xl transform -translate-x-full lg:translate-x-0 lg:static lg:inset-0 transition duration-200 ease-in-out">
<div class="flex items-center justify-between h-16 px-6 border-b border-gray-200">
<h2 class="text-lg font-semibold text-gray-900">Menu Admin</h2>
<button id="sidebar-close" class="lg:hidden text-gray-500 hover:text-gray-700">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12">
</path>
</svg>
</button>
</div>
<nav class="px-6 py-6">
<div class="space-y-6">
<!-- Dashboard -->
<div>
<a href="{{ route('admin.dashboard') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.dashboard') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2H5a2 2 0 00-2-2z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M8 5a2 2 0 012-2h4a2 2 0 012 2v6H8V5z"></path>
</svg>
<span class="font-medium">Dashboard</span>
</a>
</div>
<!-- Daftar Antrian -->
<div>
<div
class="flex items-center px-4 py-2 text-sm font-semibold text-gray-500 uppercase tracking-wider">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2">
</path>
</svg>
Daftar Antrian
</div>
<div class="mt-3 space-y-1">
<a href="{{ route('admin.poli.umum') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.umum') ? 'bg-blue-50 text-blue-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-blue-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Umum</span>
</a>
<a href="{{ route('admin.poli.gigi') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.gigi') ? 'bg-green-50 text-green-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-green-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Gigi</span>
</a>
<a href="{{ route('admin.poli.jiwa') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.jiwa') ? 'bg-purple-50 text-purple-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-purple-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Jiwa</span>
</a>
<a href="{{ route('admin.poli.tradisional') }}"
class="flex items-center px-4 py-2 rounded-lg {{ request()->routeIs('admin.poli.tradisional') ? 'bg-yellow-50 text-yellow-700' : 'text-gray-600 hover:bg-gray-50' }} transition duration-200">
<div class="w-2 h-2 bg-yellow-500 rounded-full mr-3"></div>
<span class="text-sm">Poli Tradisional</span>
</a>
</div>
</div>
<!-- Kelola User -->
<div>
<a href="{{ route('admin.users.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.users.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z">
</path>
</svg>
<span class="font-medium">Kelola User</span>
</a>
</div>
<!-- Laporan -->
<div>
<a href="{{ route('admin.laporan.index') }}"
class="flex items-center px-4 py-3 rounded-xl {{ request()->routeIs('admin.laporan.*') ? 'bg-blue-50 text-blue-700 border border-blue-200' : 'text-gray-700 hover:bg-gray-50' }} transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z">
</path>
</svg>
<span class="font-medium">Laporan</span>
</a>
</div>
<!-- Display -->
<div>
<a href="{{ route('display') }}"
class="flex items-center px-4 py-3 rounded-xl text-gray-700 hover:bg-gray-50 transition duration-200">
<svg class="w-5 h-5 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z">
</path>
</svg>
<span class="font-medium">Display</span>
</a>
</div>
</div>
</nav>
</aside>
<!-- Sidebar Overlay -->
<div id="sidebar-overlay"
class="fixed inset-0 bg-black bg-opacity-50 z-40 lg:hidden hidden transition duration-200 ease-in-out">
</div>
<!-- Main Content --> <!-- Main Content -->
<div class="flex-1 lg:ml-0"> <div class="flex-1 lg:ml-0">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 md:py-8"> <div class="px-4 sm:px-6 lg:px-8 py-6 md:py-8">
<!-- Header --> <!-- Header -->
<div class="mb-8 animate-fade-in"> <div class="mb-6">
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-2">Kelola Data User</h1> <div class="flex items-center justify-between">
<p class="text-gray-600 text-lg">Kelola data pengguna sistem antrian Puskesmas</p> <div>
<h1 class="text-2xl font-bold text-gray-900 mb-2">Kelola Data User</h1>
<p class="text-gray-600">Kelola data pengguna yang terdaftar dalam sistem</p>
</div> </div>
<!-- Search Box -->
<div class="bg-white rounded-2xl shadow-xl p-6 mb-8">
<form method="GET" action="{{ route('admin.users.index') }}"
class="flex flex-col md:flex-row gap-4">
<div class="flex-1">
<label for="search" class="block text-sm font-medium text-gray-700 mb-2">Cari
User</label>
<div class="relative">
<input type="text" id="search" name="search" value="{{ request('search') }}"
class="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition duration-200"
placeholder="Cari berdasarkan nama, KTP, HP, alamat, pekerjaan, atau jenis kelamin...">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
</div>
</div>
<div class="flex items-end space-x-3">
<button type="submit"
class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-xl font-medium transition duration-200 flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
Cari
</button>
<a href="{{ route('admin.users.index') }}"
class="px-6 py-3 border border-gray-300 rounded-xl text-gray-700 font-medium hover:bg-gray-50 transition duration-200">
Reset
</a>
</div>
</form>
</div>
<!-- Users Table -->
<div class="bg-white rounded-2xl shadow-xl overflow-hidden animate-slide-up">
<div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
<h3 class="text-lg font-semibold text-gray-900">Daftar User</h3>
<a href="{{ route('admin.users.create') }}" <a href="{{ route('admin.users.create') }}"
class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-md transition duration-200"> class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 transition duration-200">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path> d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
@ -203,65 +28,86 @@ class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-whi
Tambah User Tambah User
</a> </a>
</div> </div>
</div>
<!-- Search and Filter -->
<div class="bg-white rounded-lg shadow-sm border p-6 mb-6">
<div class="flex flex-col sm:flex-row gap-4">
<div class="flex-1">
<input type="text" id="searchInput"
placeholder="Cari berdasarkan nama, NIK, atau nomor HP..."
value="{{ request('search') }}"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<button id="searchBtn"
class="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:ring-2 focus:ring-blue-500">
Cari
</button>
@if(request('search'))
<a href="{{ route('admin.users.index') }}"
class="px-6 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 focus:ring-2 focus:ring-gray-500">
Clear
</a>
@endif
</div>
</div>
<!-- Users Table -->
<div class="bg-white rounded-lg shadow-sm border overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-900">Daftar User</h3>
</div>
<!-- Desktop Table --> <!-- Desktop Table -->
<div class="hidden lg:block overflow-x-auto"> <div class="hidden lg:block overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 table-fixed"> <table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50"> <thead class="bg-gray-50">
<tr> <tr>
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/4"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Nama & Alamat</th> Nama</th>
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/6"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
No. KTP</th> NIK</th>
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/6"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
No. HP</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/6">
Jenis Kelamin</th> Jenis Kelamin</th>
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/6"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Pekerjaan</th> No. HP</th>
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/6"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Alamat</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Aksi</th> Aksi</th>
</tr> </tr>
</thead> </thead>
<tbody class="bg-white divide-y divide-gray-200"> <tbody id="usersTableBody" class="bg-white divide-y divide-gray-200">
@forelse($users as $user) @forelse($users as $user)
<tr class="hover:bg-gray-50 transition duration-200"> <tr class="hover:bg-gray-50 transition duration-200">
<td class="px-6 py-4"> <td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">{{ $user->nama }}</div> <div class="text-sm font-medium text-gray-900">{{ $user->nama }}</div>
<div class="text-sm text-gray-500 max-w-xs truncate hover:text-gray-700"
title="{{ $user->alamat }}">
{{ Str::limit($user->alamat, 50) }}
@if (strlen($user->alamat) > 50)
<span class="text-blue-500">...</span>
@endif
</div>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900"> <td class="px-6 py-4 whitespace-nowrap">
{{ $user->no_ktp }} <div class="text-sm text-gray-900">{{ $user->no_ktp }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{ $user->no_hp }}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
<span <span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{ $user->jenis_kelamin == 'laki-laki' ? 'bg-blue-100 text-blue-800' : 'bg-pink-100 text-pink-800' }}"> class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{ $user->jenis_kelamin == 'Laki-laki' ? 'bg-blue-100 text-blue-800' : 'bg-pink-100 text-pink-800' }}">
{{ $user->jenis_kelamin == 'laki-laki' ? 'Laki-laki' : 'Perempuan' }} {{ $user->jenis_kelamin }}
</span> </span>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900"> <td class="px-6 py-4 whitespace-nowrap">
{{ $user->pekerjaan }} <div class="text-sm text-gray-900">{{ $user->no_hp }}</div>
</td>
<td class="px-6 py-4">
<div class="text-sm text-gray-900 max-w-xs truncate">{{ $user->alamat }}
</div>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium"> <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<button onclick="viewUser({{ $user->id }})" <a href="{{ route('admin.users.show', $user->id) }}"
class="text-blue-600 hover:text-blue-900 mr-3">Detail</button> class="text-blue-600 hover:text-blue-900 mr-3">Detail</a>
<button onclick="resetPassword({{ $user->id }})"
class="text-green-600 hover:text-green-900">Reset Password</button>
</td> </td>
</tr> </tr>
@empty @empty
@ -273,8 +119,9 @@ class="text-green-600 hover:text-green-900">Reset Password</button>
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z"> d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z">
</path> </path>
</svg> </svg>
<p class="text-lg font-medium">Belum ada user</p> <p class="text-lg font-medium">Belum ada user terdaftar</p>
<p class="text-sm text-gray-400">Tidak ada data user yang tersedia</p> <p class="text-sm text-gray-400">User akan muncul di sini setelah ada
pendaftaran</p>
</td> </td>
</tr> </tr>
@endforelse @endforelse
@ -286,360 +133,110 @@ class="text-green-600 hover:text-green-900">Reset Password</button>
<div class="lg:hidden"> <div class="lg:hidden">
@forelse($users as $user) @forelse($users as $user)
<div class="border-b border-gray-200 p-4 hover:bg-gray-50"> <div class="border-b border-gray-200 p-4 hover:bg-gray-50">
<!-- 4 Kolom Penting untuk Mobile -->
<div class="grid grid-cols-2 gap-4 mb-3"> <div class="grid grid-cols-2 gap-4 mb-3">
<div> <div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Nama</p> <p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Nama</p>
<p class="text-sm font-medium text-gray-900">{{ $user->nama }}</p> <div class="text-sm font-medium text-gray-900">{{ $user->nama }}</div>
</div> </div>
<div> <div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">No. KTP <p class="text-xs font-medium text-gray-500 uppercase tracking-wider">NIK</p>
</p> <div class="text-sm text-gray-900">{{ $user->no_ktp }}</div>
<p class="text-sm text-gray-900">{{ $user->no_ktp }}</p>
</div>
<div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">No. HP
</p>
<p class="text-sm text-gray-900">{{ $user->no_hp }}</p>
</div> </div>
<div> <div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Jenis <p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Jenis
Kelamin</p> Kelamin</p>
<span <span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{ $user->jenis_kelamin == 'laki-laki' ? 'bg-blue-100 text-blue-800' : 'bg-pink-100 text-pink-800' }}"> class="inline-flex px-2 py-1 text-xs font-semibold rounded-full {{ $user->jenis_kelamin == 'Laki-laki' ? 'bg-blue-100 text-blue-800' : 'bg-pink-100 text-pink-800' }}">
{{ $user->jenis_kelamin == 'laki-laki' ? 'Laki-laki' : 'Perempuan' }} {{ $user->jenis_kelamin }}
</span> </span>
</div> </div>
<div>
<p class="text-xs font-medium text-gray-500 uppercase tracking-wider">No. HP</p>
<div class="text-sm text-gray-900">{{ $user->no_hp }}</div>
</div>
</div> </div>
<!-- Button Selengkapnya untuk Mobile -->
<div class="border-t border-gray-100 pt-3"> <div class="border-t border-gray-100 pt-3">
<button onclick="toggleUserDetails({{ $user->id }})" <div class="mb-2">
class="text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center"> <p class="text-xs font-medium text-gray-500 uppercase tracking-wider">Alamat</p>
<span id="user-btn-text-{{ $user->id }}">Selengkapnya</span> <div class="text-sm text-gray-900">{{ $user->alamat }}</div>
<svg id="user-icon-{{ $user->id }}" </div>
class="w-4 h-4 ml-1 transform transition-transform" fill="none" <a href="{{ route('admin.users.show', $user->id) }}"
stroke="currentColor" viewBox="0 0 24 24"> class="inline-flex items-center text-blue-600 hover:text-blue-900 text-sm font-medium">
Lihat Detail
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"></path> d="M9 5l7 7-7 7"></path>
</svg> </svg>
</button> </a>
<!-- Detail Tambahan (Hidden by default) -->
<div id="user-details-{{ $user->id }}" class="hidden mt-3 space-y-2">
<div class="grid grid-cols-1 gap-2 text-sm">
<div>
<span class="font-medium text-gray-700">Alamat:</span>
<span class="text-gray-600">{{ $user->alamat }}</span>
</div>
<div>
<span class="font-medium text-gray-700">Pekerjaan:</span>
<span class="text-gray-600">{{ $user->pekerjaan }}</span>
</div>
<div class="flex space-x-2 pt-2">
<button onclick="viewUser({{ $user->id }})"
class="text-blue-600 hover:text-blue-900 text-sm font-medium">Detail</button>
<button onclick="resetPassword({{ $user->id }})"
class="text-green-600 hover:text-green-900 text-sm font-medium">Reset
Password</button>
</div>
</div>
</div>
</div> </div>
</div> </div>
@empty @empty
<div class="p-8 text-center text-gray-500"> <div class="p-8 text-center text-gray-500">
<svg class="w-12 h-12 mx-auto mb-4 text-gray-400" fill="none" <svg class="w-12 h-12 mx-auto mb-4 text-gray-400" fill="none" stroke="currentColor"
stroke="currentColor" viewBox="0 0 24 24"> viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z"> d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z">
</path> </path>
</svg> </svg>
<p class="text-lg font-medium">Belum ada user</p> <p class="text-lg font-medium">Belum ada user terdaftar</p>
<p class="text-sm text-gray-400">Tidak ada data user yang tersedia</p> <p class="text-sm text-gray-400">User akan muncul di sini setelah ada pendaftaran</p>
</div> </div>
@endforelse @endforelse
</div> </div>
</div> </div>
</div>
</div>
<!-- User Detail Modal --> <!-- Pagination -->
<div id="userModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden"> @if ($users->hasPages())
<div class="flex items-center justify-center min-h-screen p-4"> <div class="mt-6">
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-2xl max-h-screen overflow-y-auto"> {{ $users->links() }}
<div class="p-6">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-gray-900">Detail User</h3>
<button onclick="closeUserModal()" class="text-gray-400 hover:text-gray-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div> </div>
@endif
<div id="userDetailContent"> <!-- Show total count -->
<!-- User detail content will be loaded here --> <div class="mt-4 text-sm text-gray-600 text-center">
Total: {{ $users->total() }} user
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- Reset Password Modal --> <!-- Logout Form -->
<div id="resetPasswordModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden"> <form id="logout-form" method="POST" action="{{ route('logout') }}" class="hidden">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-md">
<div class="p-6">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-gray-900">Reset Password User</h3>
<button onclick="closeResetPasswordModal()" class="text-gray-400 hover:text-gray-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
</div>
<form id="resetPasswordForm" class="space-y-6">
@csrf @csrf
<input type="hidden" id="resetUserId" name="user_id">
<div>
<label for="new_password" class="block text-sm font-medium text-gray-700 mb-2">
Password Baru
</label>
<input type="password" name="new_password" id="new_password" required
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition duration-200"
placeholder="Masukkan password baru (min. 8 karakter)">
</div>
<div class="flex justify-end space-x-3 pt-6">
<button type="button" onclick="closeResetPasswordModal()"
class="px-6 py-3 border border-gray-300 rounded-xl text-gray-700 font-medium hover:bg-gray-50 transition duration-200">
Batal
</button>
<button type="submit"
class="px-6 py-3 bg-green-600 hover:bg-green-700 text-white rounded-xl font-medium transition duration-200">
Reset Password
</button>
</div>
</form> </form>
</div>
</div>
</div>
</div>
@push('scripts')
<script> <script>
// Sidebar functionality // Search functionality
document.addEventListener('DOMContentLoaded', function() { document.getElementById('searchBtn').addEventListener('click', function() {
const sidebar = document.getElementById('sidebar'); const searchTerm = document.getElementById('searchInput').value.trim();
const sidebarToggle = document.getElementById('sidebar-toggle'); if (searchTerm.length < 3) {
const sidebarClose = document.getElementById('sidebar-close');
const sidebarOverlay = document.getElementById('sidebar-overlay');
// Toggle sidebar on mobile
sidebarToggle.addEventListener('click', function() {
sidebar.classList.remove('-translate-x-full');
sidebarOverlay.classList.remove('hidden');
});
// Close sidebar
sidebarClose.addEventListener('click', function() {
sidebar.classList.add('-translate-x-full');
sidebarOverlay.classList.add('hidden');
});
// Close sidebar when clicking overlay
sidebarOverlay.addEventListener('click', function() {
sidebar.classList.add('-translate-x-full');
sidebarOverlay.classList.add('hidden');
});
});
// View user detail
function viewUser(userId) {
fetch(`/admin/users/${userId}`)
.then(response => response.text())
.then(html => {
document.getElementById('userDetailContent').innerHTML = html;
document.getElementById('userModal').classList.remove('hidden');
document.body.style.overflow = 'hidden';
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Terjadi kesalahan saat memuat data user',
confirmButtonText: 'OK'
});
});
}
// Close user modal
function closeUserModal() {
document.getElementById('userModal').classList.add('hidden');
document.body.style.overflow = 'auto';
}
// Reset password
function resetPassword(userId) {
document.getElementById('resetUserId').value = userId;
document.getElementById('resetPasswordModal').classList.remove('hidden');
document.body.style.overflow = 'hidden';
}
// Close reset password modal
function closeResetPasswordModal() {
document.getElementById('resetPasswordModal').classList.add('hidden');
document.body.style.overflow = 'auto';
document.getElementById('resetPasswordForm').reset();
}
// Close modals when clicking outside
document.getElementById('userModal').addEventListener('click', function(e) {
if (e.target === this) {
closeUserModal();
}
});
document.getElementById('resetPasswordModal').addEventListener('click', function(e) {
if (e.target === this) {
closeResetPasswordModal();
}
});
// Reset password form submission
document.getElementById('resetPasswordForm').addEventListener('submit', function(e) {
e.preventDefault();
const userId = document.getElementById('resetUserId').value;
const newPassword = document.getElementById('new_password').value;
if (newPassword.length < 8) {
Swal.fire({ Swal.fire({
icon: 'warning', icon: 'warning',
title: 'Password Terlalu Pendek!', title: 'Peringatan',
text: 'Password harus minimal 8 karakter.', text: 'Masukkan minimal 3 karakter untuk pencarian',
confirmButtonText: 'OK', confirmButtonColor: '#3B82F6'
confirmButtonColor: '#F59E0B'
}); });
return; return;
} }
searchUsers(searchTerm);
const formData = new FormData(this);
fetch(`/admin/users/${userId}/reset-password`, {
method: 'POST',
body: formData,
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: data.message,
confirmButtonText: 'OK'
}).then(() => {
closeResetPasswordModal();
});
} else {
Swal.fire({
icon: 'error',
title: 'Error!',
text: data.message,
confirmButtonText: 'OK'
});
}
})
.catch(error => {
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Terjadi kesalahan saat reset password',
confirmButtonText: 'OK'
});
});
}); });
// Function to confirm logout document.getElementById('searchInput').addEventListener('keypress', function(e) {
function confirmLogout() { if (e.key === 'Enter') {
Swal.fire({ document.getElementById('searchBtn').click();
title: 'Konfirmasi Logout',
text: 'Apakah Anda yakin ingin keluar dari sistem?',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Ya, Logout',
cancelButtonText: 'Batal',
confirmButtonColor: '#EF4444',
cancelButtonColor: '#6B7280'
}).then((result) => {
if (result.isConfirmed) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '{{ route('logout') }}';
const csrfToken = document.createElement('input');
csrfToken.type = 'hidden';
csrfToken.name = '_token';
csrfToken.value = '{{ csrf_token() }}';
form.appendChild(csrfToken);
document.body.appendChild(form);
form.submit();
} }
}); });
}
// Show SweetAlert2 for success messages function searchUsers(searchTerm) {
@if (session('success')) // Redirect to search with query parameter
Swal.fire({ const currentUrl = new URL(window.location);
icon: 'success', currentUrl.searchParams.set('search', searchTerm);
title: 'Berhasil!', window.location.href = currentUrl.toString();
text: '{{ session('success') }}',
confirmButtonText: 'OK',
confirmButtonColor: '#10B981',
timer: 3000,
timerProgressBar: true
});
@endif
// Show SweetAlert2 for error messages
@if (session('error'))
Swal.fire({
icon: 'error',
title: 'Error!',
text: '{{ session('error') }}',
confirmButtonText: 'OK',
confirmButtonColor: '#EF4444',
timer: 4000,
timerProgressBar: true
});
@endif
// Function to toggle user details on mobile
function toggleUserDetails(userId) {
const detailsDiv = document.getElementById(`user-details-${userId}`);
const buttonText = document.getElementById(`user-btn-text-${userId}`);
const icon = document.getElementById(`user-icon-${userId}`);
if (detailsDiv.classList.contains('hidden')) {
detailsDiv.classList.remove('hidden');
buttonText.textContent = 'Lebih Sedikit';
icon.classList.remove('transform', 'rotate-180');
} else {
detailsDiv.classList.add('hidden');
buttonText.textContent = 'Selengkapnya';
icon.classList.add('transform', 'rotate-180');
}
} }
</script> </script>
@endpush
@endsection @include('admin.partials.sidebar-script')
@endsection

View File

@ -634,10 +634,51 @@ function closeEditModal() {
title: 'Berhasil!', title: 'Berhasil!',
text: data.message, text: data.message,
confirmButtonText: 'OK', confirmButtonText: 'OK',
confirmButtonColor: '#10B981' confirmButtonColor: '#10B981',
timer: 3000, // Auto close after 3 seconds
timerProgressBar: true,
showConfirmButton: false
}).then(() => {
// After success alert closes, show reminder alert
Swal.fire({
icon: 'info',
title: 'Pengingat Penting!',
html: `
<div class="text-center">
<div class="mb-4">
<svg class="w-16 h-16 text-blue-500 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<p class="text-lg font-semibold text-gray-800 mb-3">
Harap Hadir 30 Menit Sebelum Nomor Dipanggil!
</p>
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
<div class="flex items-center justify-center mb-2">
<svg class="w-5 h-5 text-blue-600 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path>
</svg>
<span class="font-medium text-blue-800">Tips:</span>
</div>
<ul class="text-sm text-blue-700 text-left space-y-1">
<li> Pastikan membawa dokumen yang diperlukan</li>
<li> Ikuti protokol kesehatan yang berlaku</li>
<li> Tunggu di area yang ditentukan</li>
</ul>
</div>
<p class="text-sm text-gray-600">
Terima kasih telah menggunakan layanan kami!
</p>
</div>
`,
confirmButtonText: 'Saya Mengerti',
confirmButtonColor: '#3B82F6',
allowOutsideClick: false,
allowEscapeKey: false
}).then(() => { }).then(() => {
location.reload(); location.reload();
}); });
});
} else { } else {
// Handle different error types // Handle different error types
if (data.type === 'existing_queue') { if (data.type === 'existing_queue') {

View File

@ -0,0 +1,46 @@
@if ($paginator->hasPages())
<nav>
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
<span class="page-link" aria-hidden="true">&lsaquo;</span>
</li>
@else
<li class="page-item">
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">&lsaquo;</a>
</li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
@else
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li class="page-item">
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">&rsaquo;</a>
</li>
@else
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
<span class="page-link" aria-hidden="true">&rsaquo;</span>
</li>
@endif
</ul>
</nav>
@endif

View File

@ -0,0 +1,130 @@
@if ($paginator->hasPages())
<nav role="navigation" aria-label="{{ __('Pagination Navigation') }}" class="flex items-center justify-between">
<div class="flex justify-between flex-1 sm:hidden">
@if ($paginator->onFirstPage())
<span
class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
{!! __('pagination.previous') !!}
</span>
@else
<a href="{{ $paginator->previousPageUrl() }}"
class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
{!! __('pagination.previous') !!}
</a>
@endif
@if ($paginator->hasMorePages())
<a href="{{ $paginator->nextPageUrl() }}"
class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
{!! __('pagination.next') !!}
</a>
@else
<span
class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md">
{!! __('pagination.next') !!}
</span>
@endif
</div>
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div>
<p class="text-sm text-gray-700 leading-5">
{!! __('Showing') !!}
@if ($paginator->firstItem())
<span class="font-medium">{{ $paginator->firstItem() }}</span>
{!! __('to') !!}
<span class="font-medium">{{ $paginator->lastItem() }}</span>
@else
{{ $paginator->count() }}
@endif
{!! __('of') !!}
<span class="font-medium">{{ $paginator->total() }}</span>
{!! __('results') !!}
</p>
</div>
<div>
<span class="relative z-0 inline-flex shadow-sm rounded-md">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<span aria-disabled="true" aria-label="{{ __('pagination.previous') }}">
<span
class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5"
aria-hidden="true">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
clip-rule="evenodd" />
</svg>
</span>
</span>
@else
<a href="{{ $paginator->previousPageUrl() }}" rel="prev"
class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150"
aria-label="{{ __('pagination.previous') }}">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
clip-rule="evenodd" />
</svg>
</a>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<span aria-disabled="true">
<span
class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5">{{ $element }}</span>
</span>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<span aria-current="page">
<span
class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-white bg-blue-600 border border-blue-600 cursor-default leading-5">{{ $page }}</span>
</span>
@else
<a href="{{ $url }}"
class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150"
aria-label="{{ __('Go to page :page', ['page' => $page]) }}">
{{ $page }}
</a>
@endif
@endforeach
@endif
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<a href="{{ $paginator->nextPageUrl() }}" rel="next"
class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150"
aria-label="{{ __('pagination.next') }}">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
</svg>
</a>
@else
<span aria-disabled="true" aria-label="{{ __('pagination.next') }}">
<span
class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5"
aria-hidden="true">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
</svg>
</span>
</span>
@endif
@endforeach
</span>
</div>
</div>
</nav>
@endif

View File

@ -80,6 +80,9 @@
Route::get('/admin/laporan/export-excel', [AdminController::class, 'exportExcel'])->name('admin.laporan.export-excel'); Route::get('/admin/laporan/export-excel', [AdminController::class, 'exportExcel'])->name('admin.laporan.export-excel');
// Antrian Routes // Antrian Routes
Route::get('/admin/antrian/tambah', [AdminController::class, 'tambahAntrian'])->name('admin.antrian.tambah');
Route::post('/admin/antrian/cari-user', [AdminController::class, 'cariUser'])->name('admin.antrian.cari-user');
Route::post('/admin/antrian/store', [AdminController::class, 'storeAntrianAdmin'])->name('admin.antrian.store');
Route::get('/admin/antrian/{antrian}/cetak', [AdminController::class, 'cetakAntrian'])->name('admin.antrian.cetak'); Route::get('/admin/antrian/{antrian}/cetak', [AdminController::class, 'cetakAntrian'])->name('admin.antrian.cetak');
// TTS Routes // TTS Routes