TA-bibit-cabai/resources/views/admin/orders.blade.php

603 lines
26 KiB
PHP

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Orders Management - Bibit Cabai Admin</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f8f9fa;
}
.admin-container {
display: flex;
min-height: 100vh;
}
/* Sidebar */
.sidebar {
width: 280px;
background: linear-gradient(180deg, #28a745, #20c997);
color: white;
min-height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
}
.sidebar-header {
padding: 25px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.sidebar-header h3 {
font-size: 1.8rem;
font-weight: bold;
margin-bottom: 5px;
}
.sidebar-header p { opacity: 0.8; font-size: 0.9rem; }
.sidebar-menu { padding: 20px 0; }
.menu-item {
display: block;
padding: 15px 25px;
color: white;
text-decoration: none;
transition: all 0.3s;
border-left: 4px solid transparent;
}
.menu-item:hover, .menu-item.active {
background: rgba(255,255,255,0.2);
color: white;
text-decoration: none;
border-left-color: #fff;
transform: translateX(5px);
}
.menu-item i { width: 20px; margin-right: 10px; }
/* Main Content */
.main-content {
margin-left: 280px;
flex: 1;
min-height: 100vh;
}
.admin-header {
background: white;
padding: 20px 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-bottom: 1px solid #dee2e6;
}
.content-area { padding: 30px; }
.page-header {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
margin-bottom: 30px;
}
.breadcrumb { background: transparent; padding: 0; margin-bottom: 10px; }
.breadcrumb-item a { color: #28a745; text-decoration: none; }
/* Stats Cards */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
display: flex;
justify-content: space-between;
align-items: center;
border-left: 4px solid;
}
.stat-card.primary { border-left-color: #007bff; }
.stat-card.warning { border-left-color: #ffc107; }
.stat-card.info { border-left-color: #17a2b8; }
.stat-card.success { border-left-color: #28a745; }
.stat-label {
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
margin-bottom: 5px;
}
.stat-card.primary .stat-label { color: #007bff; }
.stat-card.warning .stat-label { color: #ffc107; }
.stat-card.info .stat-label { color: #17a2b8; }
.stat-card.success .stat-label { color: #28a745; }
.stat-value { font-size: 1.5rem; font-weight: 700; color: #333; }
.stat-icon { font-size: 2rem; color: #dee2e6; }
/* Table */
.data-table {
width: 100%;
border-collapse: collapse;
background: white;
}
.data-table th, .data-table td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #dee2e6;
}
.data-table th {
background: #f8f9fa;
font-weight: 600;
color: #495057;
font-size: 0.85rem;
}
.data-table tbody tr:hover { background-color: #f8f9fc; }
/* Badges */
.badge-status {
padding: 4px 10px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
}
.badge-pending { background: #fff3cd; color: #856404; }
.badge-processing { background: #d1ecf1; color: #0c5460; }
.badge-shipped { background: #cce5ff; color: #004085; }
.badge-delivered { background: #d4edda; color: #155724; }
.badge-cancelled { background: #f8d7da; color: #721c24; }
.badge-paid { background: #d4edda; color: #155724; }
.badge-failed { background: #f8d7da; color: #721c24; }
/* Action Buttons */
.btn-action {
padding: 6px 10px;
border: none;
border-radius: 6px;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
display: inline-flex;
align-items: center;
}
.btn-action:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
text-decoration: none;
color: inherit;
}
.btn-view { background: #17a2b8; color: white; }
.btn-edit { background: #ffc107; color: #212529; }
.btn-print { background: #6c757d; color: white; }
/* Section Header */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid #f8f9fa;
}
.section-title { font-size: 1.3rem; font-weight: 600; color: #333; }
/* Back Button */
.back-button {
position: fixed;
top: 20px;
right: 20px;
z-index: 1100;
background: linear-gradient(45deg, #dc3545, #c82333);
color: white;
border: none;
padding: 12px 20px;
border-radius: 25px;
font-weight: 600;
transition: all 0.3s;
text-decoration: none;
}
.back-button:hover {
background: linear-gradient(45deg, #c82333, #a71e2a);
transform: translateY(-2px);
color: white;
text-decoration: none;
}
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Pagination Fix */
.pagination {
display: flex;
gap: 4px;
margin: 0;
padding: 0;
list-style: none;
flex-wrap: wrap;
}
.pagination .page-item .page-link {
padding: 6px 12px;
border-radius: 6px;
border: 1px solid #dee2e6;
color: #28a745;
font-size: 0.85rem;
text-decoration: none;
background: white;
}
.pagination .page-item.active .page-link {
background: #28a745;
border-color: #28a745;
color: white;
}
.pagination .page-item.disabled .page-link {
color: #adb5bd;
pointer-events: none;
}
.pagination .page-item .page-link:hover {
background: #e9f7ef;
border-color: #28a745;
color: #28a745;
}
/* Cancelled row styling */
tr.row-cancelled {
background-color: #e9ecef !important;
opacity: 0.75;
}
tr.row-cancelled td {
color: #6c757d !important;
}
tr.row-cancelled strong,
tr.row-cancelled .text-success {
color: #6c757d !important;
}
tr.row-cancelled select,
tr.row-cancelled .btn-action {
pointer-events: none;
opacity: 0.5;
cursor: not-allowed;
}
@media (max-width: 768px) {
.sidebar { width: 100%; position: relative; }
.main-content { margin-left: 0; }
.stats-grid { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>
<!-- Back Button -->
<a href="{{ route('home') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="admin-container">
<!-- Sidebar -->
<nav class="sidebar">
<div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3>
<p>Admin Dashboard</p>
<small class="text-light">{{ Auth::user()->name ?? 'Admin User' }}</small>
</div>
<div class="sidebar-menu">
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i>Dashboard
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item">
<i class="fas fa-seedling"></i>Kelola Produk
</a>
<a href="{{ route('admin.orders') }}" class="menu-item active">
<i class="fas fa-shopping-cart"></i>Pesanan
</a>
{{-- TAMBAHKAN DI SINI --}}
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i>Pengajuan Batal
</a>
<a href="{{ route('admin.users') }}" class="menu-item">
<i class="fas fa-users"></i>Pengguna
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i>Laporan
</a>
<!-- <a href="{{ route('admin.settings') }}" class="menu-item">
<i class="fas fa-cog"></i>Pengaturan
</a> -->
<div class="mt-4 px-3">
<form method="POST" action="{{ route('admin.logout') }}">
@csrf
<button type="submit" class="btn btn-outline-light btn-sm w-100">
<i class="fas fa-sign-out-alt me-2"></i>Logout
</button>
</form>
</div>
</div>
</nav>
<!-- Main Content -->
<div class="main-content">
<div class="admin-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">
<a href="{{ route('admin.dashboard') }}">
<i class="fas fa-home"></i> Dashboard
</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Pesanan</li>
</ol>
</nav>
<h2>Orders Management</h2>
<p class="mb-0">Kelola semua pesanan pelanggan di sini!</p>
</div>
<div class="content-area fade-in">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle me-2"></i>{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card primary">
<div>
<div class="stat-label">Total Orders</div>
<div class="stat-value">{{ $orders->total() }}</div>
</div>
<div class="stat-icon"><i class="fas fa-shopping-cart"></i></div>
</div>
<div class="stat-card warning">
<div>
<div class="stat-label">Pending Orders</div>
<div class="stat-value">{{ \App\Models\Transaksi::where('order_status', 'pending')->count() }}</div>
</div>
<div class="stat-icon"><i class="fas fa-clock"></i></div>
</div>
<div class="stat-card info">
<div>
<div class="stat-label">Processing Orders</div>
<div class="stat-value">{{ \App\Models\Transaksi::where('order_status', 'processing')->count() }}</div>
</div>
<div class="stat-icon"><i class="fas fa-box"></i></div>
</div>
<div class="stat-card success">
<div>
<div class="stat-label">Delivered Orders</div>
<div class="stat-value">{{ \App\Models\Transaksi::where('order_status', 'delivered')->count() }}</div>
</div>
<div class="stat-icon"><i class="fas fa-check-circle"></i></div>
</div>
</div>
<!-- Orders Table -->
<div class="page-header">
<div class="section-header">
<h3 class="section-title">
<i class="fas fa-shopping-cart text-success me-2"></i>
Daftar Pesanan ({{ $orders->total() }})
</h3>
<div class="d-flex gap-2 align-items-center">
<select class="form-select form-select-sm" id="filterStatus" onchange="filterOrders()" style="width: 160px;">
<option value="">Semua Status</option>
<option value="pending">Pending</option>
<option value="processing">Processing</option>
<option value="shipped">Shipped</option>
<option value="delivered">Delivered</option>
<option value="cancelled">Cancelled</option>
</select>
<a href="{{ route('admin.transaksis.export') }}" class="btn btn-success btn-sm">
<i class="fas fa-file-excel me-1"></i>Export Excel
</a>
</div>
</div>
<div class="table-responsive">
<table class="data-table" id="ordersTable">
<thead>
<tr>
<th>Invoice Number</th>
<th>Customer</th>
<th>Phone</th>
<th>Order Status</th>
<th>Payment Status</th>
<th>Metode Pembayaran</th>
<th>Total</th>
<th>Order Date</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($orders as $order)
<tr data-status="{{ $order->order_status }}"
class="{{ $order->order_status === 'cancelled' ? 'row-cancelled' : '' }}">
<td><strong>{{ $order->invoice_number }}</strong></td>
<td>{{ $order->customer_name }}</td>
<td>{{ $order->customer_phone }}</td>
<td>
<select class="badge-status badge-{{ $order->order_status }} border-0 cursor-pointer"
style="cursor:pointer; appearance:auto;"
onchange="updateStatus({{ $order->id }}, 'order_status', this.value, this)">
<option value="pending" {{ $order->order_status == 'pending' ? 'selected' : '' }}>Pending</option>
<option value="processing" {{ $order->order_status == 'processing' ? 'selected' : '' }}>Processing</option>
<option value="shipped" {{ $order->order_status == 'shipped' ? 'selected' : '' }}>Shipped</option>
<option value="delivered" {{ $order->order_status == 'delivered' ? 'selected' : '' }}>Delivered</option>
<option value="cancelled" {{ $order->order_status == 'cancelled' ? 'selected' : '' }}>Cancelled</option>
</select>
@if($order->cancellation && $order->cancellation->status === 'pending')
<br><span class="badge-status badge-warning mt-1" style="font-size:0.7rem;">
Minta Batal
</span>
@endif
</td>
<td>
<select class="badge-status badge-{{ $order->payment_status }} border-0"
style="cursor:pointer; appearance:auto;"
onchange="updateStatus({{ $order->id }}, 'payment_status', this.value, this)">
<option value="pending" {{ $order->payment_status == 'pending' ? 'selected' : '' }}>Pending</option>
<option value="paid" {{ $order->payment_status == 'paid' ? 'selected' : '' }}>Paid</option>
<option value="failed" {{ $order->payment_status == 'failed' ? 'selected' : '' }}>Failed</option>
</select>
</td>
<td>
<span class="badge-status" style="background:#ced4da;color:#212529;">
{{ ucwords(str_replace('_', ' ', $order->payment_method ?? '-')) }}
</span>
</td>
<td><strong class="text-success">Rp {{ number_format($order->total_amount, 0, ',', '.') }}</strong></td>
<td><small>{{ $order->created_at->format('d M Y H:i') }}</small></td>
<td>
<div class="d-flex gap-1">
<a href="{{ route('admin.transaksis.show', $order->id) }}"
class="btn-action btn-view" title="View Details">
<i class="fas fa-eye"></i>
</a>
<a href="{{ route('admin.transaksis.edit', $order->id) }}"
class="btn-action btn-edit" title="Edit">
<i class="fas fa-edit"></i>
</a>
<a href="{{ route('admin.transaksis.print', $order->id) }}"
class="btn-action btn-print" title="Print Invoice" target="_blank">
<i class="fas fa-print"></i>
</a>
</div>
</td>
</tr>
@empty
@endforelse
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="d-flex justify-content-between align-items-center mt-4">
<small class="text-muted">
Menampilkan {{ $orders->firstItem() ?? 0 }} sampai {{ $orders->lastItem() ?? 0 }}
dari {{ $orders->total() }} pesanan
</small>
<div>{{ $orders->links() }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function filterOrders() {
const filterValue = document.getElementById('filterStatus').value.toLowerCase();
const rows = document.querySelectorAll('#ordersTable tbody tr[data-status]');
rows.forEach(row => {
const status = row.getAttribute('data-status');
row.style.display = (filterValue === '' || status === filterValue) ? '' : 'none';
});
}
function updateStatus(orderId, field, value, selectEl) {
// Jika status sudah cancelled, tidak bisa diubah
if (field === 'order_status') {
const row = selectEl.closest('tr');
const currentStatus = row.getAttribute('data-status');
if (currentStatus === 'cancelled' || currentStatus === 'delivered') {
alert('Pesanan yang sudah selesai atau dibatalkan tidak dapat diubah kembali.');
selectEl.value = currentStatus;
return;
}
}
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
fetch(`/admin/orders/${orderId}/update-status`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': csrfToken
},
body: JSON.stringify({ [field]: value })
})
.then(res => res.json())
.then(data => {
if (data.success) {
// Update warna badge sesuai nilai baru
const badgeClasses = ['badge-pending','badge-processing','badge-shipped','badge-delivered','badge-cancelled','badge-paid','badge-failed'];
selectEl.classList.remove(...badgeClasses);
selectEl.classList.add('badge-' + value);
// Update data-status di row jika yang diubah adalah order_status
if (field === 'order_status') {
const row = selectEl.closest('tr');
row.setAttribute('data-status', value);
// Tambahkan/hapus class abu-abu jika status cancelled
if (value === 'cancelled') {
row.classList.add('row-cancelled');
} else {
row.classList.remove('row-cancelled');
}
}
// Tampilkan notifikasi kecil
showToast('Status berhasil diupdate!');
} else {
alert('Gagal update: ' + data.message);
}
})
.catch(() => alert('Terjadi kesalahan koneksi.'));
}
function showToast(msg) {
const toast = document.createElement('div');
toast.innerText = msg;
toast.style.cssText = 'position:fixed;bottom:20px;right:20px;background:#28a745;color:white;padding:10px 20px;border-radius:8px;z-index:9999;font-size:14px;';
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2500);
}
</script>
</body>
</html>