update banyak
This commit is contained in:
parent
879ee41c76
commit
a2f7caf21f
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'timezone' => 'UTC',
|
'timezone' => 'Asia/Jakarta',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
@ -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, '"')})">
|
||||||
|
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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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">
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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">
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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
|
|
||||||
|
@include('admin.partials.sidebar-script')
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
@ -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') {
|
||||||
|
|
|
@ -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">‹</span>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</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')">›</a>
|
||||||
|
</li>
|
||||||
|
@else
|
||||||
|
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||||
|
<span class="page-link" aria-hidden="true">›</span>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
@endif
|
|
@ -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
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue