perbaikan responsive

belum100%
This commit is contained in:
bayughazali 2026-05-20 00:08:47 +07:00
parent b68eca055b
commit b5acd49443
17 changed files with 8489 additions and 4008 deletions

View File

@ -82,6 +82,7 @@ public function showVerifyOtpForm(Request $request)
// ─────────────────────────────────────────────────────────
// STEP 2 — Proses: verifikasi OTP
// ─────────────────────────────────────────────────────────
// STEP 2 — Proses: verifikasi OTP
public function verifyOtp(Request $request)
{
$request->validate([
@ -104,13 +105,17 @@ public function verifyOtp(Request $request)
return back()->with('error', 'Kode OTP tidak ditemukan. Silakan kirim ulang.');
}
if (Carbon::now()->diffInMinutes(Carbon::parse($record->created_at)) > 10) {
// ✅ CEK EXPIRED — hapus record & paksa user kirim ulang
if (Carbon::now()->diffInSeconds(Carbon::parse($record->created_at)) >= 180) {
DB::table('password_reset_tokens')->where('email', $email)->delete();
return back()->with('error', 'Kode OTP sudah kadaluarsa. Silakan kirim ulang kode.');
return back()->with('otp_expired', true) // flag khusus untuk view
->with('error', 'Kode OTP sudah kadaluarsa (3 menit). Silakan kirim ulang kode.');
}
if (!Hash::check($request->otp, $record->token)) {
return back()->with('error', 'Kode OTP salah. Periksa kembali kode yang Anda masukkan.');
return back()
->with('error', 'Kode OTP salah. Waktu berkurang 30 detik.')
->with('otp_wrong', true); // ← flag untuk kurangi timer di frontend
}
$verifiedToken = Str::random(60);
@ -123,7 +128,6 @@ public function verifyOtp(Request $request)
return redirect()->route('admin.password.reset-form')
->with('success', 'Kode berhasil diverifikasi! Silakan buat password baru.');
}
// ─────────────────────────────────────────────────────────
// STEP 2 — Kirim ulang OTP
// ─────────────────────────────────────────────────────────
@ -182,6 +186,9 @@ public function showResetForm(Request $request)
// ─────────────────────────────────────────────────────────
// STEP 3 — Proses: simpan password baru
// ─────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────
// STEP 3 — Proses: simpan password baru
// ─────────────────────────────────────────────────────────
public function resetPassword(Request $request)
{
$request->validate([
@ -209,6 +216,15 @@ public function resetPassword(Request $request)
->with('error', 'Token tidak valid. Silakan ulangi dari awal.');
}
// ✅ CEK PASSWORD LAMA — tolak jika sama dengan password sekarang
$admin = User::where('email', $email)->where('is_admin', true)->first();
if ($admin && Hash::check($request->password, $admin->password)) {
return back()->withErrors([
'password' => 'Password baru tidak boleh sama dengan password yang sedang digunakan.',
])->withInput();
}
User::where('email', $email)
->where('is_admin', true)
->update(['password' => Hash::make($request->password)]);

View File

@ -0,0 +1,23 @@
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
class FonnteService
{
public function sendOTP($nomor, $otp)
{
// Format nomor: 08xxx → 628xxx
$nomor = '62' . ltrim($nomor, '0');
$response = Http::withHeaders([
'Authorization' => env('FONNTE_TOKEN'),
])->post('https://api.fonnte.com/send', [
'target' => $nomor,
'message' => "Kode OTP reset password Anda: *$otp*\n\nBerlaku selama 10 menit. Jangan berikan kode ini kepada siapapun.",
]);
return $response->json();
}
}

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('no_hp')->nullable()->after('email');
});
}
/**
* Reverse the migrations.
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('no_hp');
});
}
};

View File

@ -3,181 +3,544 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pengajuan Pembatalan - Admin</title>
<title>Pengajuan Pembatalan - Bibit Cabai Admin</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<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', sans-serif; background: #f8f9fa; }
.admin-container { display: flex; min-height: 100vh; }
:root {
--sidebar-width: 260px;
--green-dark: #15803d;
--green-main: #16a34a;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f1f5f2;
overflow-x: hidden;
}
/* ===== SIDEBAR ===== */
.sidebar {
width: 280px; background: linear-gradient(180deg, #28a745, #20c997);
color: white; min-height: 100vh; position: fixed; top: 0; left: 0; z-index: 1000;
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0; left: 0;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.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; }
.sidebar-header {
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.sidebar-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-header h3 { font-size: 1.35rem; font-weight: 700; margin-bottom: 2px; }
.sidebar-header .subtitle { opacity: 0.65; font-size: 0.78rem; margin-bottom: 6px; }
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-menu { padding: 12px 0; flex: 1; }
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display: block; padding: 15px 25px; color: white;
text-decoration: none; transition: all 0.3s; border-left: 4px solid transparent;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
}
.menu-item:hover, .menu-item.active {
background: rgba(255,255,255,0.2); color: white;
border-left-color: #fff; transform: translateX(5px);
.menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.menu-item i { width: 20px; margin-right: 10px; }
.main-content { margin-left: 280px; flex: 1; }
.admin-header {
background: white; padding: 20px 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-bottom: 1px solid #dee2e6;
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
font-weight: 600;
}
.content-area { padding: 30px; }
.badge-status { padding: 4px 10px; border-radius: 12px; font-size: 0.75rem; font-weight: 600; }
.badge-pending { background: #fff3cd; color: #856404; }
.badge-approved { background: #d4edda; color: #155724; }
.badge-rejected { background: #f8d7da; color: #721c24; }
.card { border: none; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); }
.card-header { background: white; border-bottom: 2px solid #f8f9fa; border-radius: 12px 12px 0 0 !important; }
.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; text-decoration: none; transition: all 0.3s;
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.back-button:hover { color: white; transform: translateY(-2px); }
.table th { background: #f8f9fa; font-weight: 600; color: #495057; font-size: 0.85rem; }
.table td { vertical-align: middle; }
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.action-btn { padding: 5px 12px; border-radius: 6px; font-size: 0.8rem; font-weight: 600; border: none; cursor: pointer; }
.action-btn.btn-success { background-color: #28a745 !important; color: white !important; opacity: 1 !important; }
.action-btn.btn-danger { background-color: #dc3545 !important; color: white !important; opacity: 1 !important; }
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ===== TOPBAR ===== */
.topbar {
background: white;
height: 64px;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.topbar-left { display: flex; align-items: center; gap: 14px; }
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.hamburger-btn:hover { background: #f3f4f6; }
.topbar-breadcrumb { font-size: 0.8rem; color: #6b7280; margin-bottom: 2px; }
.topbar-breadcrumb a { color: var(--green-main); text-decoration: none; }
.topbar-title h2 { font-size: 1.1rem; font-weight: 700; color: #111827; margin: 0; }
.topbar-title p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.btn-back-website {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
border: none;
padding: 9px 18px;
border-radius: 8px;
font-weight: 600;
font-size: 0.825rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 7px;
transition: all 0.2s;
white-space: nowrap;
}
.btn-back-website:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(220,38,38,0.35);
color: white;
text-decoration: none;
}
/* ===== CONTENT AREA ===== */
.content-area { padding: 20px 24px 24px; flex: 1; }
/* ===== STATS GRID ===== */
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 20px;
}
.stat-card {
background: white;
border-radius: 12px;
padding: 18px 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
display: flex;
justify-content: space-between;
align-items: center;
border-left: 4px solid;
}
.stat-card.warning { border-left-color: #d97706; }
.stat-card.success { border-left-color: var(--green-main); }
.stat-card.danger { border-left-color: #dc2626; }
.stat-label {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
margin-bottom: 4px;
}
.stat-card.warning .stat-label { color: #d97706; }
.stat-card.success .stat-label { color: var(--green-main); }
.stat-card.danger .stat-label { color: #dc2626; }
.stat-value { font-size: 1.5rem; font-weight: 800; color: #111827; }
.stat-icon { font-size: 1.75rem; color: #e5e7eb; }
/* ===== TABLE CARD ===== */
.table-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
overflow: hidden;
}
.table-card-header {
padding: 18px 20px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.section-title {
font-size: 1rem;
font-weight: 700;
color: #111827;
display: flex;
align-items: center;
gap: 8px;
}
/* ===== TABLE ===== */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th, .data-table td {
padding: 11px 14px;
text-align: left;
border-bottom: 1px solid #f3f4f6;
font-size: 0.875rem;
vertical-align: middle;
}
.data-table th {
background: #f9fafb;
font-weight: 600;
color: #6b7280;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.04em;
white-space: nowrap;
}
.data-table tbody tr:hover { background: #f9fafb; }
/* ===== BADGES ===== */
.badge-status {
padding: 4px 10px;
border-radius: 12px;
font-size: 0.72rem;
font-weight: 600;
display: inline-block;
}
.badge-pending { background: #fef3c7; color: #92400e; }
.badge-approved { background: #dcfce7; color: #166534; }
.badge-rejected { background: #fee2e2; color: #991b1b; }
/* ===== ACTION BUTTONS ===== */
.btn-action {
padding: 6px 12px;
border: none;
border-radius: 6px;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
}
.btn-approve { background: #16a34a; color: white; }
.btn-reject { background: #dc2626; color: white; }
.btn-label-approved { background: #dcfce7; color: #166534; cursor: default; }
.btn-label-rejected { background: #fee2e2; color: #991b1b; cursor: default; }
.btn-action:hover:not([style*="cursor:default"]) {
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
color: inherit;
}
/* ===== TABLE FOOTER ===== */
.table-footer {
padding: 14px 20px;
border-top: 1px solid #f3f4f6;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
/* ===== MOBILE CARDS ===== */
.mobile-card-list { display: none; }
.cancel-card {
background: white;
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
}
.cancel-card-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px;
}
.cancel-card-invoice { font-weight: 700; font-size: 0.9rem; color: #111827; }
.cancel-card-amount { font-size: 0.78rem; color: #9ca3af; margin-top: 2px; }
.cancel-card-meta {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 10px;
font-size: 0.8rem;
}
.cancel-card-meta-item .label { color: #9ca3af; font-size: 0.72rem; text-transform: uppercase; font-weight: 600; margin-bottom: 2px; }
.cancel-card-meta-item .value { color: #374151; }
.cancel-card-reason {
background: #f9fafb;
border-radius: 8px;
padding: 10px 12px;
font-size: 0.8rem;
color: #374151;
margin-bottom: 12px;
}
.cancel-card-reason .reason-label { font-weight: 700; color: #6b7280; font-size: 0.72rem; text-transform: uppercase; margin-bottom: 4px; }
.cancel-card-actions { display: flex; gap: 8px; }
.cancel-card-actions .btn-action { flex: 1; justify-content: center; }
/* ===== FADE IN ===== */
.fade-in { animation: fadeIn 0.3s ease; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0,0,0,0.2); }
.main-content { margin-left: 0; }
.hamburger-btn { display: flex; align-items: center; justify-content: center; }
}
@media (max-width: 767px) {
.desktop-table-wrap { display: none; }
.mobile-card-list { display: block; }
.stats-grid { grid-template-columns: 1fr 1fr; }
.stats-grid .stat-card:last-child { grid-column: span 2; }
.content-area { padding: 14px 14px 20px; }
.topbar { padding: 0 14px; }
.btn-back-website span { display: none; }
.btn-back-website { padding: 9px 12px; }
.table-footer { flex-direction: column; align-items: flex-start; }
}
@media (max-width: 400px) {
.stats-grid { grid-template-columns: 1fr; }
.stats-grid .stat-card:last-child { grid-column: span 1; }
}
</style>
</head>
<body>
<a href="{{ route('home') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<div class="admin-container">
{{-- Sidebar --}}
<nav class="sidebar">
<!-- Sidebar -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3>
<p>Admin Dashboard</p>
<small class="text-light">{{ Auth::user()->name ?? 'Admin' }}</small>
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div>
<div class="sidebar-menu">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i>Dashboard
<i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item">
<i class="fas fa-seedling"></i>Kelola Produk
<i class="fas fa-seedling"></i><span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item">
<i class="fas fa-shopping-cart"></i>Pesanan
<i class="fas fa-shopping-cart"></i><span>Pesanan</span>
</a>
<a href="{{ route('admin.cancellations') }}" class="menu-item active">
<i class="fas fa-times-circle"></i>Pengajuan Batal
<i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item">
<i class="fas fa-users"></i>Pengguna
<i class="fas fa-users"></i><span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i>Laporan
<i class="fas fa-chart-line"></i><span>Laporan</span>
</a>
<!-- <a href="{{ route('admin.settings') }}" class="menu-item">
<i class="fas fa-cog"></i>Pengaturan
</a> -->
<div class="mt-4 px-3">
</div>
<div class="sidebar-footer">
<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 type="submit" class="btn-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</button>
</form>
</div>
</div>
</nav>
{{-- Main Content --}}
<!-- Main Content -->
<div class="main-content">
<div class="admin-header">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-1">
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
<li class="breadcrumb-item active">Pengajuan Pembatalan</li>
</ol>
</nav>
<h2>Pengajuan Pembatalan Pesanan</h2>
<p class="mb-0">Kelola semua permintaan pembatalan dari pelanggan.</p>
</div>
<div class="content-area">
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" onclick="toggleSidebar()" aria-label="Toggle menu">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<div class="topbar-breadcrumb">
<a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
&nbsp;/&nbsp;Pengajuan Pembatalan
</div>
<h2>Pengajuan Pembatalan Pesanan</h2>
<p>Kelola semua permintaan pembatalan dari pelanggan.</p>
</div>
</div>
<a href="{{ route('home') }}" class="btn-back-website">
<i class="fas fa-arrow-left"></i>
<span>Kembali ke Website</span>
</a>
</header>
<!-- Content -->
<div class="content-area fade-in">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show">
<div class="alert alert-success alert-dismissible fade show mb-4" 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 --}}
<div class="row g-3 mb-4">
<div class="col-md-4">
<div class="card p-3 border-start border-warning border-4">
<div class="d-flex justify-content-between align-items-center">
<!-- Stats -->
<div class="stats-grid">
<div class="stat-card warning">
<div>
<div class="text-warning fw-bold small">MENUNGGU REVIEW</div>
<div class="fs-3 fw-bold">{{ $cancellations->where('status','pending')->count() }}</div>
<div class="stat-label">Menunggu Review</div>
<div class="stat-value">{{ $cancellations->where('status','pending')->count() }}</div>
</div>
<i class="fas fa-clock fa-2x text-muted"></i>
<div class="stat-icon"><i class="fas fa-clock"></i></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card p-3 border-start border-success border-4">
<div class="d-flex justify-content-between align-items-center">
<div class="stat-card success">
<div>
<div class="text-success fw-bold small">DISETUJUI</div>
<div class="fs-3 fw-bold">{{ $cancellations->where('status','approved')->count() }}</div>
<div class="stat-label">Disetujui</div>
<div class="stat-value">{{ $cancellations->where('status','approved')->count() }}</div>
</div>
<i class="fas fa-check-circle fa-2x text-muted"></i>
<div class="stat-icon"><i class="fas fa-check-circle"></i></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card p-3 border-start border-danger border-4">
<div class="d-flex justify-content-between align-items-center">
<div class="stat-card danger">
<div>
<div class="text-danger fw-bold small">DITOLAK</div>
<div class="fs-3 fw-bold">{{ $cancellations->where('status','rejected')->count() }}</div>
</div>
<i class="fas fa-times-circle fa-2x text-muted"></i>
</div>
<div class="stat-label">Ditolak</div>
<div class="stat-value">{{ $cancellations->where('status','rejected')->count() }}</div>
</div>
<div class="stat-icon"><i class="fas fa-times-circle"></i></div>
</div>
</div>
{{-- Tabel --}}
<div class="card">
<div class="card-header py-3">
<h6 class="mb-0 fw-bold text-success">
<i class="fas fa-times-circle me-2"></i>
<!-- Table Card -->
<div class="table-card">
<div class="table-card-header">
<div class="section-title">
<i class="fas fa-times-circle text-success"></i>
Daftar Pengajuan Pembatalan ({{ $cancellations->total() }})
</h6>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
</div>
<!-- DESKTOP TABLE -->
<div class="desktop-table-wrap table-responsive">
<table class="data-table">
<thead>
<tr>
<th class="ps-3">Invoice</th>
@ -193,68 +556,62 @@
@forelse($cancellations as $item)
<tr>
<td class="ps-3">
<strong class="d-block">{{ $item->transaksi->invoice_number ?? '-' }}</strong>
<small class="text-muted">
<strong class="d-block" style="color:#111827;">{{ $item->transaksi->invoice_number ?? '-' }}</strong>
<small style="color:#9ca3af;">
Rp{{ number_format($item->transaksi->total_amount ?? 0, 0, ',', '.') }}
</small>
</td>
<td>
<strong>{{ $item->user->name ?? '-' }}</strong><br>
<small class="text-muted">{{ $item->user->email ?? '-' }}</small>
<strong style="color:#111827;">{{ $item->user->name ?? '-' }}</strong><br>
<small style="color:#9ca3af;">{{ $item->user->email ?? '-' }}</small>
</td>
<td style="max-width:160px;">
<small style="color:#374151;">{{ $item->reason }}</small>
</td>
<td style="max-width:180px;">
<small>{{ $item->reason }}</small>
</td>
<td style="max-width:200px;">
<small class="text-muted">{{ $item->description ?? '-' }}</small>
<small style="color:#6b7280;">{{ $item->description ?? '-' }}</small>
</td>
<td>
<small>{{ $item->created_at->format('d M Y') }}</small><br>
<small class="text-muted">{{ $item->created_at->format('H:i') }}</small>
<small style="color:#374151;">{{ $item->created_at->format('d M Y') }}</small><br>
<small style="color:#9ca3af;">{{ $item->created_at->format('H:i') }}</small>
</td>
<td>
<span class="badge-status badge-{{ $item->status }}">
{{ $item->status === 'pending' ? 'Menunggu' : ($item->status === 'approved' ? 'Disetujui' : 'Ditolak') }}
</span>
@if($item->reviewed_at)
<br><small class="text-muted">{{ $item->reviewed_at->format('d M Y') }}</small>
<br><small style="color:#9ca3af;">{{ $item->reviewed_at->format('d M Y') }}</small>
@endif
</td>
<td class="text-center">
@if($item->status === 'pending')
{{-- Tombol Setujui --}}
<form action="{{ route('admin.cancellations.approve', $item->id) }}" method="POST"
class="d-inline"
onsubmit="return confirm('Setujui pembatalan pesanan {{ $item->transaksi->invoice_number ?? '' }}?')">
@csrf
<textarea name="admin_note" style="display:none"> disetujui</textarea>
<button type="submit" class="action-btn btn-success text-white me-1">
<i class="fas fa-check me-1"></i>Setujui
<button type="submit" class="btn-action btn-approve me-1">
<i class="fas fa-check"></i>Setujui
</button>
</form>
{{-- Tombol Tolak --}}
<button type="button"
class="action-btn btn-danger text-white"
<button type="button" class="btn-action btn-reject"
onclick="showRejectModal({{ $item->id }}, '{{ $item->transaksi->invoice_number ?? '' }}')">
<i class="fas fa-times me-1"></i>Tolak
<i class="fas fa-times"></i>Tolak
</button>
@else
@if($item->status === 'approved')
<span class="action-btn btn-success">Disetujui</span>
@elseif($item->status === 'approved')
<span class="btn-action btn-label-approved">Disetujui</span>
@elseif($item->status === 'rejected')
<span class="action-btn btn-danger">Ditolak</span>
<span class="btn-action btn-label-rejected">Ditolak</span>
@else
<span class="text-muted">-</span>
@endif
<span style="color:#9ca3af;">-</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center py-5">
<i class="fas fa-inbox fa-3x text-muted mb-3 d-block"></i>
<p class="text-muted">Belum ada pengajuan pembatalan</p>
<td colspan="7" style="text-align:center; padding:50px 20px; color:#9ca3af;">
<i class="fas fa-inbox" style="font-size:2.5rem; display:block; margin-bottom:12px; opacity:0.3;"></i>
Belum ada pengajuan pembatalan
</td>
</tr>
@endforelse
@ -262,28 +619,94 @@ class="action-btn btn-danger text-white"
</table>
</div>
{{-- Pagination --}}
<!-- MOBILE CARD LIST -->
<div class="mobile-card-list" style="padding:16px;">
@forelse($cancellations as $item)
<div class="cancel-card">
<div class="cancel-card-top">
<div>
<div class="cancel-card-invoice">{{ $item->transaksi->invoice_number ?? '-' }}</div>
<div class="cancel-card-amount">Rp{{ number_format($item->transaksi->total_amount ?? 0, 0, ',', '.') }}</div>
</div>
<span class="badge-status badge-{{ $item->status }}">
{{ $item->status === 'pending' ? 'Menunggu' : ($item->status === 'approved' ? 'Disetujui' : 'Ditolak') }}
</span>
</div>
<div class="cancel-card-meta">
<div class="cancel-card-meta-item">
<div class="label">Pelanggan</div>
<div class="value">{{ $item->user->name ?? '-' }}</div>
</div>
<div class="cancel-card-meta-item">
<div class="label">Tanggal</div>
<div class="value">{{ $item->created_at->format('d M Y') }}</div>
</div>
<div class="cancel-card-meta-item" style="grid-column:span 2;">
<div class="label">Email</div>
<div class="value">{{ $item->user->email ?? '-' }}</div>
</div>
</div>
<div class="cancel-card-reason">
<div class="reason-label">Alasan</div>
{{ $item->reason }}
@if($item->description)
<div style="margin-top:6px; color:#6b7280; font-size:0.78rem;">{{ $item->description }}</div>
@endif
</div>
@if($item->status === 'pending')
<div class="cancel-card-actions">
<form action="{{ route('admin.cancellations.approve', $item->id) }}" method="POST"
style="flex:1;"
onsubmit="return confirm('Setujui pembatalan pesanan {{ $item->transaksi->invoice_number ?? '' }}?')">
@csrf
<textarea name="admin_note" style="display:none"> disetujui</textarea>
<button type="submit" class="btn-action btn-approve" style="width:100%; justify-content:center;">
<i class="fas fa-check"></i>Setujui
</button>
</form>
<button type="button" class="btn-action btn-reject"
style="flex:1; justify-content:center;"
onclick="showRejectModal({{ $item->id }}, '{{ $item->transaksi->invoice_number ?? '' }}')">
<i class="fas fa-times"></i>Tolak
</button>
</div>
@elseif($item->status === 'approved')
<span class="btn-action btn-label-approved" style="width:100%; justify-content:center; display:flex;">Disetujui</span>
@elseif($item->status === 'rejected')
<span class="btn-action btn-label-rejected" style="width:100%; justify-content:center; display:flex;">Ditolak</span>
@endif
</div>
@empty
<div style="text-align:center; padding:50px 20px; color:#9ca3af;">
<i class="fas fa-inbox" style="font-size:2.5rem; display:block; margin-bottom:12px; opacity:0.3;"></i>
Belum ada pengajuan pembatalan
</div>
@endforelse
</div>
<!-- Pagination -->
@if($cancellations->hasPages())
<div class="d-flex justify-content-between align-items-center p-3 border-top">
<small class="text-muted">
Menampilkan {{ $cancellations->firstItem() }} - {{ $cancellations->lastItem() }}
<div class="table-footer">
<small style="color:#6b7280;">
Menampilkan {{ $cancellations->firstItem() }}{{ $cancellations->lastItem() }}
dari {{ $cancellations->total() }} pengajuan
</small>
{{ $cancellations->links() }}
<div>{{ $cancellations->links() }}</div>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
{{-- Modal Tolak --}}
<!-- Modal Tolak -->
<div class="modal fade" id="rejectModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" style="max-width:420px;">
<div class="modal-content border-0" style="border-radius:14px; overflow:hidden;">
<div class="modal-header bg-danger text-white">
<div class="modal-header bg-danger text-white border-0">
<h6 class="modal-title fw-bold mb-0">
<i class="fas fa-times-circle me-2"></i>Tolak Pengajuan
</h6>
@ -292,8 +715,8 @@ class="action-btn btn-danger text-white"
<form id="rejectForm" method="POST">
@csrf
<div class="modal-body p-4">
<p class="mb-3">Pesanan: <strong id="rejectInvoice"></strong></p>
<label class="form-label fw-semibold">
<p class="mb-3" style="font-size:0.9rem;">Pesanan: <strong id="rejectInvoice"></strong></p>
<label class="form-label fw-semibold" style="font-size:0.875rem;">
Alasan Penolakan <span class="text-danger">*</span>
</label>
<textarea name="admin_note" class="form-control" rows="3"
@ -314,12 +737,31 @@ class="action-btn btn-danger text-white"
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.toggle('open');
overlay.classList.toggle('show');
document.body.style.overflow = sidebar.classList.contains('open') ? 'hidden' : '';
}
function closeSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.remove('open');
overlay.classList.remove('show');
document.body.style.overflow = '';
}
window.addEventListener('resize', function() {
if (window.innerWidth > 900) closeSidebar();
});
function showRejectModal(id, invoice) {
document.getElementById('rejectInvoice').textContent = invoice;
document.getElementById('rejectForm').action = `/admin/cancellations/${id}/reject`;
new bootstrap.Modal(document.getElementById('rejectModal')).show();
}
</script>
</body>
</html>

View File

@ -10,143 +10,551 @@
<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; transition: all 0.3s ease; }
.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; cursor: pointer; }
.menu-item:hover, .menu-item.active { background: rgba(255,255,255,0.1); color: white; text-decoration: none; border-left-color: #fff; transform: translateX(5px); }
.menu-item i { width: 20px; margin-right: 10px; }
:root {
--sidebar-width: 260px;
--header-height: 64px;
--green-dark: #15803d;
--green-main: #16a34a;
--green-light: #22c55e;
}
/* 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; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f1f5f2;
overflow-x: hidden;
}
/* Stats Cards */
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 25px; margin-bottom: 40px; }
.stat-card { background: white; padding: 25px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); border-left: 5px solid; transition: transform 0.3s ease; }
.stat-card:hover { transform: translateY(-5px); }
.stat-card.products { border-left-color: #28a745; }
.stat-card.orders { border-left-color: #007bff; }
.stat-card.revenue { border-left-color: #ffc107; }
.stat-card.users { border-left-color: #6f42c1; }
.stat-number { font-size: 2.5rem; font-weight: bold; margin-bottom: 8px; }
.stat-label { color: #6c757d; font-size: 1rem; font-weight: 500; }
.stat-icon { float: right; font-size: 2.5rem; opacity: 0.3; margin-top: -10px; }
/* ===== SIDEBAR ===== */
.sidebar {
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
/* 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 ease; text-decoration: none; }
.back-button:hover { background: linear-gradient(45deg, #c82333, #a71e2a); transform: translateY(-2px); color: white; text-decoration: none; }
.sidebar-header {
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
/* Chart Card Header */
.chart-card-header {
.sidebar-header .brand-icon {
font-size: 2rem;
margin-bottom: 6px;
display: block;
}
.sidebar-header h3 {
font-size: 1.35rem;
font-weight: 700;
margin-bottom: 2px;
letter-spacing: -0.3px;
}
.sidebar-header .subtitle {
opacity: 0.65;
font-size: 0.78rem;
margin-bottom: 6px;
}
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-menu {
padding: 12px 0;
flex: 1;
}
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
cursor: pointer;
}
.menu-item i {
width: 18px;
font-size: 0.95rem;
flex-shrink: 0;
text-align: center;
}
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
font-weight: 600;
}
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
/* Sidebar Overlay (mobile) */
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ===== TOPBAR ===== */
.topbar {
background: white;
height: var(--header-height);
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.topbar-left {
display: flex;
align-items: center;
gap: 14px;
}
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.hamburger-btn:hover { background: #f3f4f6; }
.topbar-title h2 {
font-size: 1.15rem;
font-weight: 700;
color: #111827;
margin: 0;
line-height: 1.2;
}
.topbar-title p {
font-size: 0.78rem;
color: #6b7280;
margin: 0;
}
.btn-back-website {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
border: none;
padding: 9px 18px;
border-radius: 8px;
font-weight: 600;
font-size: 0.825rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 7px;
transition: all 0.2s;
white-space: nowrap;
}
.btn-back-website:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(220,38,38,0.35);
color: white;
text-decoration: none;
}
/* ===== CONTENT AREA ===== */
.content-area {
padding: 24px;
flex: 1;
}
/* ===== STATS GRID ===== */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 28px;
}
.stat-card {
background: white;
padding: 22px 20px;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
border-left: 4px solid;
transition: transform 0.25s ease, box-shadow 0.25s ease;
position: relative;
overflow: hidden;
}
.stat-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.1);
}
.stat-card.products { border-left-color: #16a34a; }
.stat-card.orders { border-left-color: #2563eb; }
.stat-card.revenue { border-left-color: #d97706; }
.stat-card.users { border-left-color: #7c3aed; }
.stat-number {
font-size: 2rem;
font-weight: 800;
margin-bottom: 4px;
line-height: 1;
letter-spacing: -0.5px;
}
.stat-label {
color: #6b7280;
font-size: 0.85rem;
font-weight: 500;
}
.stat-icon {
position: absolute;
top: 18px;
right: 18px;
font-size: 2rem;
opacity: 0.12;
}
/* ===== CHART CARD ===== */
.chart-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
overflow: hidden;
margin-bottom: 28px;
}
.chart-card-header {
background: linear-gradient(135deg, var(--green-main), var(--green-light));
padding: 16px 20px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.chart-card-header h5 {
color: white;
font-size: 0.975rem;
font-weight: 700;
margin: 0;
}
/* Lihat Detail Button */
.btn-lihat-detail {
background: rgba(255,255,255,0.2);
color: white;
border: 2px solid rgba(255,255,255,0.6);
padding: 7px 18px;
border: 1.5px solid rgba(255,255,255,0.55);
padding: 6px 16px;
border-radius: 20px;
font-size: 0.85rem;
font-size: 0.8rem;
font-weight: 600;
text-decoration: none;
transition: all 0.3s ease;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.btn-lihat-detail:hover {
background: white;
color: #28a745;
color: var(--green-main);
border-color: white;
text-decoration: none;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transform: translateY(-1px);
box-shadow: 0 4px 10px rgba(0,0,0,0.12);
}
.chart-card-body {
padding: 20px;
}
/* Empty State */
.empty-state { text-align: center; padding: 60px 20px; color: #6c757d; }
.empty-state i { font-size: 4rem; margin-bottom: 20px; opacity: 0.3; }
.empty-state h4 { font-size: 1.5rem; margin-bottom: 10px; }
.empty-state p { font-size: 1rem; margin-bottom: 20px; }
.empty-state {
text-align: center;
padding: 48px 20px;
color: #9ca3af;
}
@media (max-width: 768px) {
.sidebar { width: 100%; position: relative; min-height: auto; }
.main-content { margin-left: 0; }
.stats-grid { grid-template-columns: 1fr; }
.empty-state i { font-size: 3rem; margin-bottom: 16px; opacity: 0.35; display: block; }
.empty-state h4 { font-size: 1.1rem; color: #6b7280; margin-bottom: 8px; }
.empty-state p { font-size: 0.875rem; margin-bottom: 16px; }
.btn-buka-laporan {
background: var(--green-main);
color: white;
border: none;
padding: 9px 20px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 600;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 7px;
transition: all 0.2s;
}
.btn-buka-laporan:hover {
background: var(--green-dark);
color: white;
text-decoration: none;
transform: translateY(-1px);
}
/* ===== RESPONSIVE ===== */
/* Large tablets & small desktops */
@media (max-width: 1100px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Tablets (sidebar collapses) */
@media (max-width: 900px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.open {
transform: translateX(0);
box-shadow: 4px 0 24px rgba(0,0,0,0.2);
}
.main-content {
margin-left: 0;
}
.hamburger-btn {
display: flex;
align-items: center;
justify-content: center;
}
}
/* Mobile phones */
@media (max-width: 600px) {
.stats-grid {
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.stat-card {
padding: 16px 14px;
}
.stat-number {
font-size: 1.6rem;
}
.stat-label {
font-size: 0.78rem;
}
.stat-icon {
font-size: 1.5rem;
}
.content-area {
padding: 16px;
}
.topbar {
padding: 0 16px;
}
.btn-back-website span {
display: none;
}
.btn-back-website {
padding: 9px 12px;
}
.chart-card-header h5 {
font-size: 0.875rem;
}
}
@media (max-width: 380px) {
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<a href="{{ route('home') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
<!-- Sidebar Overlay (mobile) -->
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<!-- Sidebar -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div>
<div class="sidebar-menu">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
<i class="fas fa-tachometer-alt"></i>
<span>Dashboard</span>
</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 {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
<i class="fas fa-tachometer-alt"></i>Dashboard
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item {{ request()->routeIs('admin.products.*') ? 'active' : '' }}">
<i class="fas fa-seedling"></i>Kelola Produk
<i class="fas fa-seedling"></i>
<span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item {{ request()->routeIs('admin.orders') ? 'active' : '' }}">
<i class="fas fa-shopping-cart"></i>Pesanan
<i class="fas fa-shopping-cart"></i>
<span>Pesanan</span>
</a>
{{-- TAMBAHKAN DI SINI --}}
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i>Pengajuan Batal
<a href="{{ route('admin.cancellations') }}" class="menu-item {{ request()->routeIs('admin.cancellations') ? 'active' : '' }}">
<i class="fas fa-times-circle"></i>
<span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item {{ request()->routeIs('admin.users') ? 'active' : '' }}">
<i class="fas fa-users"></i>Pengguna
<i class="fas fa-users"></i>
<span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item {{ request()->routeIs('admin.laporan') ? 'active' : '' }}">
<i class="fas fa-chart-line"></i>Laporan
<i class="fas fa-chart-line"></i>
<span>Laporan</span>
</a>
<!-- <a href="{{ route('admin.settings') }}" class="menu-item {{ request()->routeIs('admin.settings') ? 'active' : '' }}">
<i class="fas fa-cog"></i>Pengaturan
</a> -->
<div class="mt-4 px-3">
</div>
<div class="sidebar-footer">
<form method="POST" action="{{ route('admin.logout') }}">
@csrf
<button type="submit" class="btn btn-outline-light btn-sm w-100" onclick="return confirm('Apakah Anda yakin ingin logout?')">
<i class="fas fa-sign-out-alt me-2"></i>Logout
<button type="submit" class="btn-logout" onclick="return confirm('Apakah Anda yakin ingin logout?')">
<i class="fas fa-sign-out-alt"></i>
<span>Logout</span>
</button>
</form>
</div>
</div>
</nav>
<!-- Main Content -->
<div class="main-content">
<div class="admin-header">
<h2>Dashboard</h2>
<p class="mb-0">Selamat datang di dashboard admin!</p>
</div>
<div class="main-content" id="mainContent">
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" id="hamburgerBtn" onclick="toggleSidebar()" aria-label="Toggle menu">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<h2>Dashboard</h2>
<p>Selamat datang di dashboard admin!</p>
</div>
</div>
<a href="{{ route('home') }}" class="btn-back-website">
<i class="fas fa-arrow-left"></i>
<span>Kembali ke Website</span>
</a>
</header>
<!-- Content -->
<div class="content-area">
<!-- Stats Grid -->
<div class="stats-grid">
<div class="stat-card products">
<div class="stat-number text-success">{{ $stats['total_products'] }}</div>
@ -170,19 +578,15 @@
</div>
</div>
<div class="row">
<div class="col-12 mb-4">
<div class="card">
<div class="card-header bg-success text-white">
<!-- Chart Card -->
<div class="chart-card">
<div class="chart-card-header">
<h5 class="mb-0">📈 Grafik Pendapatan Harian (30 Hari Terakhir)</h5>
{{-- Tombol Lihat Detail mengarah ke halaman laporan --}}
<h5>📈 Grafik Pendapatan Harian (30 Hari Terakhir)</h5>
<a href="{{ route('admin.laporan') }}" class="btn-lihat-detail">
<i class="fas fa-external-link-alt"></i> Lihat Detail
</a>
</div>
</div>
<div class="card-body">
<div class="chart-card-body">
@if(count($chartLabels) > 0)
<canvas id="salesChart" height="80"></canvas>
@else
@ -190,39 +594,46 @@
<i class="fas fa-chart-line"></i>
<h4>Belum Ada Data</h4>
<p>Grafik pendapatan akan ditampilkan setelah ada transaksi</p>
<a href="{{ route('admin.laporan') }}" class="btn btn-success">
<i class="fas fa-chart-bar me-2"></i>Buka Laporan
<a href="{{ route('admin.laporan') }}" class="btn-buka-laporan">
<i class="fas fa-chart-bar"></i>Buka Laporan
</a>
</div>
@endif
</div>
</div>
</div>
</div>
<!-- <div class="row">
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="mb-0"> Aktivitas Terbaru</h5>
</div>
<div class="card-body">
<div class="empty-state">
<i class="fas fa-clock"></i>
<h4>Belum Ada Aktivitas</h4>
<p>Aktivitas terbaru akan ditampilkan di sini</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div> -->
</div><!-- /content-area -->
</div><!-- /main-content -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
// Sidebar toggle
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.toggle('open');
overlay.classList.toggle('show');
document.body.style.overflow = sidebar.classList.contains('open') ? 'hidden' : '';
}
function closeSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.remove('open');
overlay.classList.remove('show');
document.body.style.overflow = '';
}
// Close sidebar on resize if desktop
window.addEventListener('resize', function() {
if (window.innerWidth > 900) {
closeSidebar();
}
});
// Chart
@if(count($chartLabels) > 0)
const ctx = document.getElementById('salesChart').getContext('2d');
new Chart(ctx, {
@ -232,11 +643,12 @@
datasets: [{
label: 'Penjualan (Rp)',
data: {!! json_encode($chartData) !!},
borderColor: '#28a745',
backgroundColor: 'rgba(40, 167, 69, 0.1)',
borderWidth: 2,
pointBackgroundColor: '#28a745',
borderColor: '#16a34a',
backgroundColor: 'rgba(22, 163, 74, 0.08)',
borderWidth: 2.5,
pointBackgroundColor: '#16a34a',
pointRadius: 4,
pointHoverRadius: 6,
tension: 0.4,
fill: true
}]

File diff suppressed because it is too large Load Diff

View File

@ -6,175 +6,330 @@
<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; }
:root {
--sidebar-width: 260px;
--green-dark: #15803d;
--green-main: #16a34a;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f8f9fa;
background: #f1f5f2;
overflow-x: hidden;
}
.admin-container {
display: flex;
min-height: 100vh;
}
/* Sidebar */
/* ===== SIDEBAR ===== */
.sidebar {
width: 280px;
background: linear-gradient(180deg, #28a745, #20c997);
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.sidebar-header {
padding: 25px 20px;
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.1);
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.sidebar-header h3 {
font-size: 1.8rem;
font-weight: bold;
margin-bottom: 5px;
.sidebar-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-header h3 { font-size: 1.35rem; font-weight: 700; margin-bottom: 2px; }
.sidebar-header .subtitle { opacity: 0.65; font-size: 0.78rem; margin-bottom: 6px; }
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-header p { opacity: 0.8; font-size: 0.9rem; }
.sidebar-menu { padding: 12px 0; flex: 1; }
.sidebar-menu { padding: 20px 0; }
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display: block;
padding: 15px 25px;
color: white;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.3s;
border-left: 4px solid transparent;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
}
.menu-item:hover, .menu-item.active {
background: rgba(255,255,255,0.2);
.menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
transform: translateX(5px);
font-weight: 600;
}
.menu-item i { width: 20px; margin-right: 10px; }
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
/* Main Content */
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: 280px;
flex: 1;
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
.admin-header {
/* ===== TOPBAR ===== */
.topbar {
background: white;
padding: 20px 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-bottom: 1px solid #dee2e6;
height: 64px;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.content-area { padding: 30px; }
.topbar-left { display: flex; align-items: center; gap: 14px; }
.page-header {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
margin-bottom: 30px;
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.breadcrumb { background: transparent; padding: 0; margin-bottom: 10px; }
.breadcrumb-item a { color: #28a745; text-decoration: none; }
.hamburger-btn:hover { background: #f3f4f6; }
/* Stats Cards */
.topbar-breadcrumb { font-size: 0.8rem; color: #6b7280; margin-bottom: 2px; }
.topbar-breadcrumb a { color: var(--green-main); text-decoration: none; }
.topbar-title h2 { font-size: 1.1rem; font-weight: 700; color: #111827; margin: 0; }
.topbar-title p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.btn-back-website {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
border: none;
padding: 9px 18px;
border-radius: 8px;
font-weight: 600;
font-size: 0.825rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 7px;
transition: all 0.2s;
white-space: nowrap;
}
.btn-back-website:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(220,38,38,0.35);
color: white;
text-decoration: none;
}
/* ===== CONTENT AREA ===== */
.content-area { padding: 20px 24px 24px; flex: 1; }
/* ===== STATS GRID ===== */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 30px;
gap: 16px;
margin-bottom: 20px;
}
.stat-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
padding: 18px 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
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-card.primary { border-left-color: #2563eb; }
.stat-card.warning { border-left-color: #d97706; }
.stat-card.info { border-left-color: #0891b2; }
.stat-card.success { border-left-color: var(--green-main); }
.stat-label {
font-size: 0.75rem;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
margin-bottom: 5px;
letter-spacing: 0.04em;
margin-bottom: 4px;
}
.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-card.primary .stat-label { color: #2563eb; }
.stat-card.warning .stat-label { color: #d97706; }
.stat-card.info .stat-label { color: #0891b2; }
.stat-card.success .stat-label { color: var(--green-main); }
.stat-value { font-size: 1.5rem; font-weight: 700; color: #333; }
.stat-icon { font-size: 2rem; color: #dee2e6; }
.stat-value { font-size: 1.5rem; font-weight: 800; color: #111827; }
.stat-icon { font-size: 1.75rem; color: #e5e7eb; }
/* ===== TABLE CARD ===== */
.table-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
overflow: hidden;
}
.table-card-header {
padding: 18px 20px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.section-title {
font-size: 1rem;
font-weight: 700;
color: #111827;
display: flex;
align-items: center;
gap: 8px;
}
.table-controls {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
/* Table */
.data-table {
width: 100%;
border-collapse: collapse;
background: white;
}
.data-table th, .data-table td {
padding: 12px 15px;
padding: 11px 14px;
text-align: left;
border-bottom: 1px solid #dee2e6;
border-bottom: 1px solid #f3f4f6;
font-size: 0.875rem;
}
.data-table th {
background: #f8f9fa;
background: #f9fafb;
font-weight: 600;
color: #495057;
font-size: 0.85rem;
color: #6b7280;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.04em;
white-space: nowrap;
}
.data-table tbody tr:hover { background-color: #f8f9fc; }
.data-table tbody tr:hover { background-color: #f9fafb; }
/* Badges */
.badge-status {
padding: 4px 10px;
border-radius: 12px;
font-size: 0.75rem;
font-size: 0.72rem;
font-weight: 600;
display: inline-block;
}
.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; }
.badge-pending { background: #fef3c7; color: #92400e; }
.badge-processing { background: #dbeafe; color: #1e40af; }
.badge-shipped { background: #e0f2fe; color: #075985; }
.badge-delivered { background: #dcfce7; color: #166534; }
.badge-cancelled { background: #fee2e2; color: #991b1b; }
.badge-paid { background: #dcfce7; color: #166534; }
.badge-failed { background: #fee2e2; color: #991b1b; }
/* Selects with badge style */
select.badge-status {
border: none;
cursor: pointer;
appearance: auto;
-webkit-appearance: auto;
}
/* Action Buttons */
.btn-action {
@ -183,7 +338,7 @@
border-radius: 6px;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.3s;
transition: all 0.2s;
text-decoration: none;
display: inline-flex;
align-items: center;
@ -191,59 +346,22 @@
.btn-action:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
text-decoration: none;
color: inherit;
}
.btn-view { background: #17a2b8; color: white; }
.btn-edit { background: #ffc107; color: #212529; }
.btn-print { background: #6c757d; color: white; }
.btn-view { background: #0891b2; color: white; }
.btn-edit { background: #d97706; color: white; }
.btn-print { background: #6b7280; 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;
}
/* Cancelled row */
tr.row-cancelled { background-color: #f3f4f6 !important; opacity: 0.75; }
tr.row-cancelled td { color: #9ca3af !important; }
tr.row-cancelled strong, tr.row-cancelled .text-success { color: #9ca3af !important; }
tr.row-cancelled select, tr.row-cancelled .btn-action { pointer-events: none; opacity: 0.5; cursor: not-allowed; }
.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 */
.pagination {
display: flex;
gap: 4px;
@ -256,133 +374,169 @@
.pagination .page-item .page-link {
padding: 6px 12px;
border-radius: 6px;
border: 1px solid #dee2e6;
color: #28a745;
font-size: 0.85rem;
border: 1px solid #e5e7eb;
color: var(--green-main);
font-size: 0.83rem;
text-decoration: none;
background: white;
}
.pagination .page-item.active .page-link {
background: #28a745;
border-color: #28a745;
background: var(--green-main);
border-color: var(--green-main);
color: white;
}
.pagination .page-item.disabled .page-link {
color: #adb5bd;
pointer-events: none;
.pagination .page-item.disabled .page-link { color: #d1d5db; pointer-events: none; }
.pagination .page-item .page-link:hover { background: #f0fdf4; border-color: var(--green-main); }
.table-footer {
padding: 14px 20px;
border-top: 1px solid #f3f4f6;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.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;
/* Alert */
.alert-success-custom {
background: #f0fdf4;
border: 1px solid #bbf7d0;
color: #166534;
border-radius: 10px;
padding: 12px 16px;
font-size: 0.875rem;
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
}
tr.row-cancelled td {
color: #6c757d !important;
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
tr.row-cancelled strong,
tr.row-cancelled .text-success {
color: #6c757d !important;
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
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; }
/* ===== RESPONSIVE ===== */
@media (max-width: 1100px) {
.stats-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0,0,0,0.2); }
.main-content { margin-left: 0; }
.hamburger-btn { display: flex; align-items: center; justify-content: center; }
}
@media (max-width: 600px) {
.stats-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
.stat-card { padding: 14px 12px; }
.stat-value { font-size: 1.3rem; }
.stat-icon { font-size: 1.4rem; }
.content-area { padding: 14px 14px 20px; }
.topbar { padding: 0 14px; }
.btn-back-website span { display: none; }
.btn-back-website { padding: 9px 12px; }
.table-card-header { flex-direction: column; align-items: flex-start; }
.table-controls { width: 100%; }
.table-controls .form-select, .table-controls a { flex: 1; }
.table-footer { flex-direction: column; align-items: flex-start; gap: 10px; }
.data-table th, .data-table td { padding: 9px 10px; font-size: 0.8rem; }
}
@media (max-width: 380px) {
.stats-grid { grid-template-columns: 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">
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<!-- Sidebar -->
<nav class="sidebar">
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3>
<p>Admin Dashboard</p>
<small class="text-light">{{ Auth::user()->name ?? 'Admin User' }}</small>
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div>
<div class="sidebar-menu">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i>Dashboard
<i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item">
<i class="fas fa-seedling"></i>Kelola Produk
<i class="fas fa-seedling"></i><span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item active">
<i class="fas fa-shopping-cart"></i>Pesanan
<i class="fas fa-shopping-cart"></i><span>Pesanan</span>
</a>
{{-- TAMBAHKAN DI SINI --}}
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i>Pengajuan Batal
<i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item">
<i class="fas fa-users"></i>Pengguna
<i class="fas fa-users"></i><span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i>Laporan
<i class="fas fa-chart-line"></i><span>Laporan</span>
</a>
<!-- <a href="{{ route('admin.settings') }}" class="menu-item">
<i class="fas fa-cog"></i>Pengaturan
</a> -->
<div class="mt-4 px-3">
</div>
<div class="sidebar-footer">
<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 type="submit" class="btn-logout" onclick="return confirm('Apakah Anda yakin ingin logout?')">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</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>
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" id="hamburgerBtn" onclick="toggleSidebar()" aria-label="Toggle menu">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<div class="topbar-breadcrumb">
<a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
&nbsp;/&nbsp;Pesanan
</div>
<h2>Orders Management</h2>
</div>
</div>
<a href="{{ route('home') }}" class="btn-back-website">
<i class="fas fa-arrow-left"></i>
<span>Kembali ke Website</span>
</a>
</header>
<!-- Content -->
<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 class="alert-success-custom">
<i class="fas fa-check-circle"></i>
<span>{{ session('success') }}</span>
</div>
@endif
<!-- Stats Cards -->
<!-- Stats Grid -->
<div class="stats-grid">
<div class="stat-card primary">
<div>
@ -393,36 +547,36 @@
</div>
<div class="stat-card warning">
<div>
<div class="stat-label">Pending Orders</div>
<div class="stat-label">Pending</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-label">Processing</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-label">Delivered</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>
<!-- Table Card -->
<div class="table-card">
<div class="table-card-header">
<div class="section-title">
<i class="fas fa-shopping-cart text-success"></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;">
</div>
<div class="table-controls">
<select class="form-select form-select-sm" id="filterStatus" onchange="filterOrders()" style="min-width:140px;">
<option value="">Semua Status</option>
<option value="pending">Pending</option>
<option value="processing">Processing</option>
@ -440,14 +594,14 @@
<table class="data-table" id="ordersTable">
<thead>
<tr>
<th>Invoice Number</th>
<th>Invoice</th>
<th>Customer</th>
<th>Phone</th>
<th>Order Status</th>
<th>Payment Status</th>
<th>Metode Pembayaran</th>
<th>Metode</th>
<th>Total</th>
<th>Order Date</th>
<th>Tanggal</th>
<th>Aksi</th>
</tr>
</thead>
@ -459,8 +613,7 @@ class="{{ $order->order_status === 'cancelled' ? 'row-cancelled' : '' }}">
<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;"
<select class="badge-status badge-{{ $order->order_status }} border-0"
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>
@ -469,14 +622,11 @@ class="{{ $order->order_status === 'cancelled' ? 'row-cancelled' : '' }}">
<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>
<br><span class="badge-status badge-warning mt-1" style="font-size:0.68rem;"> 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>
@ -484,38 +634,40 @@ class="{{ $order->order_status === 'cancelled' ? 'row-cancelled' : '' }}">
</select>
</td>
<td>
<span class="badge-status" style="background:#ced4da;color:#212529;">
<span class="badge-status" style="background:#f3f4f6;color:#374151;">
{{ 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><small style="color:#6b7280;">{{ $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">
<div class="d-flex gap-1 flex-nowrap">
<a href="{{ route('admin.transaksis.show', $order->id) }}" class="btn-action btn-view" title="Lihat Detail">
<i class="fas fa-eye"></i>
</a>
<a href="{{ route('admin.transaksis.edit', $order->id) }}"
class="btn-action btn-edit" title="Edit">
<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">
<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
<tr>
<td colspan="9" style="text-align:center; padding:40px; color:#9ca3af;">
<i class="fas fa-inbox" style="font-size:2rem; display:block; margin-bottom:10px; opacity:0.3;"></i>
Belum ada pesanan
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="d-flex justify-content-between align-items-center mt-4">
<small class="text-muted">
<div class="table-footer">
<small style="color:#6b7280;">
Menampilkan {{ $orders->firstItem() ?? 0 }} sampai {{ $orders->lastItem() ?? 0 }}
dari {{ $orders->total() }} pesanan
</small>
@ -525,11 +677,32 @@ class="btn-action btn-print" title="Print Invoice" target="_blank">
</div>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Sidebar toggle
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.toggle('open');
overlay.classList.toggle('show');
document.body.style.overflow = sidebar.classList.contains('open') ? 'hidden' : '';
}
function closeSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.remove('open');
overlay.classList.remove('show');
document.body.style.overflow = '';
}
window.addEventListener('resize', function() {
if (window.innerWidth > 900) closeSidebar();
});
// Filter orders
function filterOrders() {
const filterValue = document.getElementById('filterStatus').value.toLowerCase();
const rows = document.querySelectorAll('#ordersTable tbody tr[data-status]');
@ -539,8 +712,8 @@ function filterOrders() {
});
}
// Update status via AJAX
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');
@ -564,17 +737,13 @@ function updateStatus(orderId, field, value, selectEl) {
.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 {
@ -582,7 +751,6 @@ function updateStatus(orderId, field, value, selectEl) {
}
}
// Tampilkan notifikasi kecil
showToast('Status berhasil diupdate!');
} else {
alert('Gagal update: ' + data.message);
@ -594,7 +762,7 @@ function updateStatus(orderId, field, value, selectEl) {
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;';
toast.style.cssText = 'position:fixed;bottom:24px;right:24px;background:#16a34a;color:white;padding:10px 20px;border-radius:8px;z-index:9999;font-size:14px;font-weight:500;box-shadow:0 4px 12px rgba(0,0,0,0.15);';
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 2500);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,238 +1,814 @@
@extends('admin.layouts.app')
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Produk - Bibit Cabai</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
@section('title', 'Edit Produk')
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
@section('content')
<!-- Back Button -->
<a href="{{ route('admin.products.index') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Daftar Produk
</a>
<!-- 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">
<a href="{{ route('admin.products.index') }}">Kelola Produk</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Edit Produk</li>
</ol>
</nav>
<h2>Edit Produk</h2>
<p class="mb-0">Edit informasi produk {{ $product->name }}</p>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--sidebar-width: 260px;
--green-dark: #15803d;
--green-main: #16a34a;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f1f5f2;
overflow-x: hidden;
}
/* ===== SIDEBAR ===== */
.sidebar {
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0; left: 0;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.sidebar-header {
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.sidebar-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-header h3 { font-size: 1.35rem; font-weight: 700; margin-bottom: 2px; }
.sidebar-header .subtitle { opacity: 0.65; font-size: 0.78rem; margin-bottom: 6px; }
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-menu { padding: 12px 0; flex: 1; }
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
}
.menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
font-weight: 600;
}
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ===== TOPBAR ===== */
.topbar {
background: white;
height: 64px;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.topbar-left { display: flex; align-items: center; gap: 14px; }
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.hamburger-btn:hover { background: #f3f4f6; }
.topbar-breadcrumb { font-size: 0.8rem; color: #6b7280; margin-bottom: 2px; }
.topbar-breadcrumb a { color: var(--green-main); text-decoration: none; }
.topbar-breadcrumb a:hover { text-decoration: underline; }
.topbar-title h2 { font-size: 1.1rem; font-weight: 700; color: #111827; margin: 0; }
.topbar-title p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.btn-back-header {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
border: none;
padding: 9px 18px;
border-radius: 8px;
font-weight: 600;
font-size: 0.825rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 7px;
transition: all 0.2s;
white-space: nowrap;
}
.btn-back-header:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(220,38,38,0.35);
color: white;
text-decoration: none;
}
/* ===== CONTENT AREA ===== */
.content-area { padding: 20px 24px 32px; flex: 1; }
/* ===== FORM CARD ===== */
.form-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
overflow: hidden;
}
.form-card-header {
padding: 20px 24px 16px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
gap: 10px;
}
.form-card-header-icon {
width: 38px; height: 38px;
background: #fef9c3;
border-radius: 10px;
display: flex; align-items: center; justify-content: center;
color: #ca8a04;
font-size: 1rem;
flex-shrink: 0;
}
.form-card-header h3 { font-size: 1rem; font-weight: 700; color: #111827; margin: 0; }
.form-card-header p { font-size: 0.78rem; color: #6b7280; margin: 0; }
.form-card-body { padding: 24px; }
/* ===== FORM ELEMENTS ===== */
.form-label {
font-size: 0.82rem;
font-weight: 600;
color: #374151;
margin-bottom: 6px;
}
.req { color: #dc2626; }
.form-control, .form-select {
border: 1.5px solid #e5e7eb;
border-radius: 8px;
font-size: 0.875rem;
padding: 9px 12px;
color: #111827;
transition: border-color 0.2s, box-shadow 0.2s;
}
.form-control:focus, .form-select:focus {
border-color: var(--green-main);
box-shadow: 0 0 0 3px rgba(22,163,74,0.12);
outline: none;
}
.form-control.is-invalid, .form-select.is-invalid { border-color: #dc2626; }
.input-group-text {
background: #f9fafb;
border: 1.5px solid #e5e7eb;
border-radius: 8px 0 0 8px;
color: #6b7280;
font-size: 0.875rem;
}
.input-group .form-control,
.input-group .form-select { border-radius: 0 8px 8px 0; }
.form-text { font-size: 0.75rem; color: #9ca3af; margin-top: 4px; }
textarea.form-control { resize: vertical; min-height: 100px; }
/* ===== CURRENT IMAGE ===== */
.current-image-box {
border: 1.5px solid #e5e7eb;
border-radius: 10px;
padding: 14px;
background: #f9fafb;
display: flex;
align-items: center;
gap: 14px;
}
.current-image-box img {
width: 80px; height: 80px;
object-fit: cover;
border-radius: 8px;
border: 1px solid #e5e7eb;
flex-shrink: 0;
}
.current-image-info { font-size: 0.8rem; color: #6b7280; }
.current-image-info strong { display: block; color: #374151; margin-bottom: 3px; font-size: 0.82rem; }
/* ===== IMAGE UPLOAD ===== */
.image-upload-area {
border: 2px dashed #d1d5db;
border-radius: 10px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
background: #fafafa;
position: relative;
}
.image-upload-area:hover { border-color: var(--green-main); background: #f0fdf4; }
.image-upload-area input[type=file] {
position: absolute; inset: 0; width: 100%; height: 100%;
opacity: 0; cursor: pointer;
}
.upload-icon { font-size: 2rem; color: #d1d5db; margin-bottom: 8px; }
.upload-text { font-size: 0.82rem; color: #6b7280; }
.upload-hint { font-size: 0.72rem; color: #9ca3af; margin-top: 4px; }
.image-preview-box { display: none; margin-top: 14px; }
.image-preview-box img {
max-width: 150px; max-height: 150px;
border-radius: 10px; object-fit: cover;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
/* ===== INFO BANNER ===== */
.info-banner {
background: #fffbeb;
border: 1px solid #fde68a;
border-radius: 10px;
padding: 12px 16px;
display: flex;
align-items: flex-start;
gap: 10px;
font-size: 0.82rem;
color: #92400e;
margin-bottom: 20px;
}
.info-banner i { margin-top: 1px; flex-shrink: 0; }
/* ===== SECTION DIVIDER ===== */
.section-divider {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #9ca3af;
padding-bottom: 10px;
border-bottom: 1px solid #f3f4f6;
margin-bottom: 18px;
}
/* ===== ACTION BUTTONS ===== */
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
flex-wrap: wrap;
padding-top: 20px;
border-top: 1px solid #f3f4f6;
margin-top: 24px;
}
.btn-cancel {
background: #f9fafb;
color: #374151;
border: 1.5px solid #e5e7eb;
border-radius: 8px;
padding: 10px 20px;
font-size: 0.875rem;
font-weight: 600;
text-decoration: none;
display: inline-flex; align-items: center; gap: 7px;
transition: all 0.2s;
}
.btn-cancel:hover { background: #f3f4f6; color: #111827; text-decoration: none; }
.btn-reset-form {
background: #fff7ed;
color: #c2410c;
border: 1.5px solid #fed7aa;
border-radius: 8px;
padding: 10px 20px;
font-size: 0.875rem;
font-weight: 600;
display: inline-flex; align-items: center; gap: 7px;
transition: all 0.2s;
cursor: pointer;
}
.btn-reset-form:hover { background: #ffedd5; }
.btn-save {
background: linear-gradient(135deg, #ca8a04, #a16207);
color: white;
border: none;
border-radius: 8px;
padding: 10px 24px;
font-size: 0.875rem;
font-weight: 700;
display: inline-flex; align-items: center; gap: 7px;
transition: all 0.2s;
cursor: pointer;
}
.btn-save:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(202,138,4,0.35);
color: white;
}
.fade-in { animation: fadeIn 0.3s ease; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0,0,0,0.2); }
.main-content { margin-left: 0; }
.hamburger-btn { display: flex; align-items: center; justify-content: center; }
}
@media (max-width: 767px) {
.content-area { padding: 14px 14px 24px; }
.topbar { padding: 0 14px; height: 58px; }
.btn-back-header span { display: none; }
.btn-back-header { padding: 9px 12px; }
.form-card-body { padding: 16px; }
.form-actions { justify-content: stretch; }
.form-actions > * { flex: 1; justify-content: center; }
}
</style>
</head>
<body>
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<!-- ===== SIDEBAR ===== -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div>
<div class="sidebar-menu">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item active">
<i class="fas fa-seedling"></i><span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item">
<i class="fas fa-shopping-cart"></i><span>Pesanan</span>
</a>
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item">
<i class="fas fa-users"></i><span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i><span>Laporan</span>
</a>
</div>
<div class="sidebar-footer">
<form method="POST" action="{{ route('admin.logout') }}">
@csrf
<button type="submit" class="btn-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</button>
</form>
</div>
</nav>
<!-- ===== MAIN ===== -->
<div class="main-content">
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" onclick="toggleSidebar()">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<div class="topbar-breadcrumb">
<a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
&nbsp;/&nbsp;<a href="{{ route('admin.products.index') }}">Kelola Produk</a>
&nbsp;/&nbsp;Edit Produk
</div>
<h2>Edit Produk</h2>
<p>Edit informasi produk {{ $product->name }}</p>
</div>
</div>
<a href="{{ route('admin.products.index') }}" class="btn-back-header">
<i class="fas fa-arrow-left"></i>
<span>Kembali ke Produk</span>
</a>
</header>
<!-- Content -->
<div class="content-area fade-in">
<!-- Display Validation Errors -->
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
<i class="fas fa-exclamation-circle me-2"></i>
<strong>Terdapat kesalahan:</strong>
<ul class="mb-0 mt-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
<div class="card">
<div class="card-header">
<h4 class="mb-0">
<i class="fas fa-edit text-warning me-2"></i>
Form Edit Produk
</h4>
@if (session('success'))
<div class="alert alert-success alert-dismissible fade show mb-4" 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>
<div class="card-body">
<form action="{{ route('admin.products.update', $product) }}" method="POST" enctype="multipart/form-data">
@endif
<div class="form-card">
<div class="form-card-header">
<div class="form-card-header-icon">
<i class="fas fa-edit"></i>
</div>
<div>
<h3>Form Edit Produk</h3>
<p>Ubah data produk sesuai kebutuhan</p>
</div>
</div>
<div class="form-card-body">
<form action="{{ route('admin.products.update', $product) }}" method="POST" enctype="multipart/form-data" id="productForm">
@csrf
@method('PUT')
<div class="row">
<!-- Nama Produk -->
<div class="col-md-6 mb-3">
<label for="name" class="form-label">Nama Produk <span class="text-danger">*</span></label>
<input type="text" class="form-control @error('name') is-invalid @enderror"
id="name" name="name" value="{{ old('name', $product->name) }}" required>
<!-- INFORMASI DASAR -->
<div class="section-divider">Informasi Dasar</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label">Nama Produk <span class="req">*</span></label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-seedling"></i></span>
<input type="text"
class="form-control @error('name') is-invalid @enderror"
name="name" id="name"
value="{{ old('name', $product->name) }}"
placeholder="Contoh: Bibit Cabai Rawit Genni"
required>
</div>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<!-- Kategori -->
<div class="col-md-6 mb-3">
<label for="category" class="form-label">Kategori <span class="text-danger">*</span></label>
<select class="form-select @error('category') is-invalid @enderror" id="category" name="category" required>
<div class="col-md-6">
<label class="form-label">Kategori <span class="req">*</span></label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-list"></i></span>
<select class="form-select @error('category') is-invalid @enderror"
name="category" id="category" required>
<option value="">Pilih Kategori</option>
<option value="Sayuran" {{ old('category', $product->category) === 'Sayuran' ? 'selected' : '' }}>Sayuran</option>
<option value="Buah" {{ old('category', $product->category) === 'Buah' ? 'selected' : '' }}>Buah</option>
<option value="Herbal" {{ old('category', $product->category) === 'Herbal' ? 'selected' : '' }}>Herbal</option>
<option value="Hias" {{ old('category', $product->category) === 'Hias' ? 'selected' : '' }}>Hias</option>
</select>
</div>
@error('category')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
</div>
<!-- Harga -->
<div class="col-md-6 mb-3">
<label for="price" class="form-label">Harga <span class="text-danger">*</span></label>
<!-- HARGA, STOK, STATUS, LABEL -->
<div class="row g-3 mb-3">
<div class="col-sm-6 col-md-3">
<label class="form-label">Harga <span class="req">*</span></label>
<div class="input-group">
<span class="input-group-text">Rp</span>
<input type="number" class="form-control @error('price') is-invalid @enderror"
id="price" name="price" value="{{ old('price', $product->price) }}"
min="0" step="0.01" required>
<input type="number"
class="form-control @error('price') is-invalid @enderror"
name="price" id="price"
value="{{ old('price', $product->price) }}"
min="0" step="1000" placeholder="0" required>
</div>
@error('price')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<!-- Stok -->
<div class="col-md-6 mb-3">
<label for="stock" class="form-label">Stok <span class="text-danger">*</span></label>
<input type="number" class="form-control @error('stock') is-invalid @enderror"
id="stock" name="stock" value="{{ old('stock', $product->stock) }}"
min="0" required>
<div class="col-sm-6 col-md-3">
<label class="form-label">Stok <span class="req">*</span></label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-boxes"></i></span>
<input type="number"
class="form-control @error('stock') is-invalid @enderror"
name="stock" id="stock"
value="{{ old('stock', $product->stock) }}"
min="0" placeholder="0" required>
</div>
@error('stock')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<!-- Status -->
<div class="col-md-6 mb-3">
<label for="status" class="form-label">Status <span class="text-danger">*</span></label>
<select class="form-select @error('status') is-invalid @enderror" id="status" name="status" required>
<div class="col-sm-6 col-md-3">
<label class="form-label">Status <span class="req">*</span></label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-toggle-on"></i></span>
<select class="form-select @error('status') is-invalid @enderror"
name="status" id="status" required>
<option value="aktif" {{ old('status', $product->status) === 'aktif' ? 'selected' : '' }}>Aktif</option>
<option value="nonaktif" {{ old('status', $product->status) === 'nonaktif' ? 'selected' : '' }}>Non-aktif</option>
</select>
</div>
@error('status')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<!-- Label -->
<div class="col-md-6 mb-3">
<label for="label" class="form-label">Label</label>
<select class="form-select @error('label') is-invalid @enderror" id="label" name="label">
<option value="">Tidak Ada Label</option>
<div class="col-sm-6 col-md-3">
<label class="form-label">Label</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-tag"></i></span>
<select class="form-select @error('label') is-invalid @enderror"
name="label" id="label">
<option value="">Tidak Ada</option>
<option value="baru" {{ old('label', $product->label) === 'baru' ? 'selected' : '' }}>Baru</option>
<option value="populer" {{ old('label', $product->label) === 'populer' ? 'selected' : '' }}>Populer</option>
<option value="diskon" {{ old('label', $product->label) === 'diskon' ? 'selected' : '' }}>Diskon</option>
<option value="terbatas" {{ old('label', $product->label) === 'terbatas' ? 'selected' : '' }}>Terbatas</option>
</select>
</div>
@error('label')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<!-- Deskripsi -->
<div class="col-12 mb-3">
<label for="description" class="form-label">Deskripsi <span class="text-danger">*</span></label>
<textarea class="form-control @error('description') is-invalid @enderror"
id="description" name="description" rows="4" required>{{ old('description', $product->description) }}</textarea>
@error('description')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<!-- Gambar Saat Ini -->
@if($product->image_url)
<div class="col-12 mb-3">
<label class="form-label">Gambar Saat Ini</label>
<!-- DESKRIPSI -->
<div class="mb-3">
<img src="{{ $product->image_url }}" alt="{{ $product->name }}"
class="img-thumbnail" style="max-width: 200px;">
<label class="form-label">Deskripsi Produk <span class="req">*</span></label>
<textarea class="form-control @error('description') is-invalid @enderror"
name="description" id="description" rows="4"
placeholder="Jelaskan keunggulan dan cara perawatan bibit..." required>{{ old('description', $product->description) }}</textarea>
<div class="form-text">Deskripsi yang detail akan membantu pelanggan memahami produk Anda.</div>
@error('description')
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<!-- GAMBAR -->
<div class="section-divider" style="margin-top:24px">Gambar Produk</div>
@if($product->image_url)
<div class="mb-3">
<label class="form-label">Gambar Saat Ini</label>
<div class="current-image-box">
<img src="{{ $product->image_url }}" alt="{{ $product->name }}">
<div class="current-image-info">
<strong>{{ $product->name }}</strong>
Gambar produk yang sedang aktif. Upload gambar baru di bawah untuk menggantinya.
</div>
</div>
</div>
@endif
<!-- Upload Gambar Baru -->
<div class="col-12 mb-3">
<label for="image" class="form-label">Gambar Produk (Opsional)</label>
<input type="file" class="form-control @error('image') is-invalid @enderror"
id="image" name="image" accept="image/*">
<div class="form-text">Format: JPG, PNG, JPEG. Maksimal 2MB. Kosongkan jika tidak ingin mengubah gambar.</div>
<div class="mb-3">
<label class="form-label">
Upload Gambar Baru
<span style="font-weight:400;color:#9ca3af">(Opsional kosongkan jika tidak ingin mengubah)</span>
</label>
<div class="image-upload-area" id="uploadArea">
<input type="file" name="image" id="image" accept="image/*"
onchange="previewImage(this)">
<div id="uploadPlaceholder">
<div class="upload-icon"><i class="fas fa-cloud-upload-alt"></i></div>
<div class="upload-text">Klik atau seret gambar ke sini</div>
<div class="upload-hint">JPG, PNG, GIF Maks. 2MB Disarankan 500×500px</div>
</div>
</div>
@error('image')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
<div class="image-preview-box" id="imagePreview">
<img id="previewImg" src="" alt="Preview">
<div class="form-text mt-1">
<span id="previewName"></span>
<a href="#" onclick="clearImage(); return false;" style="color:#dc2626;margin-left:8px">
<i class="fas fa-times"></i> Hapus
</a>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="d-flex justify-content-between mt-4">
<a href="{{ route('admin.products.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-2"></i>Kembali
</a>
<div>
<button type="reset" class="btn btn-outline-warning me-2">
<i class="fas fa-undo me-2"></i>Reset
</button>
<button type="submit" class="btn btn-success">
<i class="fas fa-save me-2"></i>Update Produk
</button>
<!-- INFO BANNER -->
<div class="info-banner">
<i class="fas fa-exclamation-triangle"></i>
<div>Pastikan semua perubahan sudah benar sebelum menyimpan. Perubahan akan langsung tampil di website.</div>
</div>
<!-- TOMBOL AKSI -->
<div class="form-actions">
<a href="{{ route('admin.products.index') }}" class="btn-cancel">
<i class="fas fa-times"></i> Batal
</a>
<button type="reset" class="btn-reset-form" onclick="clearImage()">
<i class="fas fa-undo"></i> Reset
</button>
<button type="submit" class="btn-save">
<i class="fas fa-save"></i> Update Produk
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// ===== SIDEBAR =====
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('open');
document.getElementById('sidebarOverlay').classList.toggle('show');
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebarOverlay').classList.remove('show');
}
window.addEventListener('resize', function() {
if (window.innerWidth > 900) closeSidebar();
});
<style>
.back-button {
position: fixed;
top: 20px;
right: 20px;
z-index: 1100;
background: linear-gradient(45deg, #ffc107, #e0a800);
color: #212529;
border: none;
padding: 12px 20px;
border-radius: 25px;
font-weight: 600;
transition: all 0.3s ease;
text-decoration: none;
// ===== IMAGE PREVIEW =====
function previewImage(input) {
if (input.files && input.files[0]) {
if (input.files[0].size > 2 * 1024 * 1024) {
alert('Ukuran file terlalu besar! Maksimal 2MB.');
input.value = '';
return;
}
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('previewImg').src = e.target.result;
document.getElementById('previewName').textContent = input.files[0].name;
document.getElementById('imagePreview').style.display = 'block';
document.getElementById('uploadPlaceholder').style.opacity = '0.4';
};
reader.readAsDataURL(input.files[0]);
}
}
.back-button:hover {
background: linear-gradient(45deg, #e0a800, #c69500);
transform: translateY(-2px);
color: #212529;
text-decoration: none;
function clearImage() {
document.getElementById('image').value = '';
document.getElementById('imagePreview').style.display = 'none';
document.getElementById('uploadPlaceholder').style.opacity = '1';
}
.card {
border: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
border-radius: 12px;
}
// ===== PRICE: digits only =====
document.getElementById('price').addEventListener('input', function() {
this.value = this.value.replace(/[^0-9]/g, '');
});
.card-header {
background: linear-gradient(45deg, #f8f9fa, #e9ecef);
border-bottom: 1px solid #dee2e6;
border-radius: 12px 12px 0 0 !important;
}
// ===== DRAG & DROP =====
const uploadArea = document.getElementById('uploadArea');
['dragover','dragenter'].forEach(evt => {
uploadArea.addEventListener(evt, function(e) {
e.preventDefault();
this.style.borderColor = 'var(--green-main)';
this.style.background = '#f0fdf4';
});
});
['dragleave','drop'].forEach(evt => {
uploadArea.addEventListener(evt, function(e) {
e.preventDefault();
this.style.borderColor = '#d1d5db';
this.style.background = '#fafafa';
});
});
.form-label {
font-weight: 600;
color: #495057;
}
.text-danger {
color: #dc3545 !important;
}
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
@endsection
// Auto-dismiss alerts
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.alert').forEach(function(alert) {
setTimeout(function() {
const a = bootstrap.Alert.getOrCreateInstance(alert);
if (a) a.close();
}, 5000);
});
document.getElementById('name').focus();
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -9,137 +9,442 @@
<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; }
:root {
--sidebar-width: 260px;
--green-dark: #15803d;
--green-main: #16a34a;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f1f5f2;
overflow-x: hidden;
}
/* ===== SIDEBAR ===== */
.sidebar {
width: 280px; background: linear-gradient(180deg, #28a745, #20c997);
color: white; min-height: 100vh; position: fixed; top: 0; left: 0; z-index: 1000;
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0; left: 0;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.sidebar-header {
padding: 25px 20px; text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.1);
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.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; }
.sidebar-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-header h3 { font-size: 1.35rem; font-weight: 700; margin-bottom: 2px; }
.sidebar-header .subtitle { opacity: 0.65; font-size: 0.78rem; margin-bottom: 6px; }
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-menu { padding: 12px 0; flex: 1; }
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display: block; padding: 15px 25px; color: white;
text-decoration: none; transition: all 0.3s; border-left: 4px solid transparent;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
}
.menu-item:hover, .menu-item.active {
background: rgba(255,255,255,0.2); color: white;
border-left-color: #fff; transform: translateX(5px); text-decoration: none;
}
.menu-item i { width: 20px; margin-right: 10px; }
.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; }
.breadcrumb { background: transparent; padding: 0; margin-bottom: 10px; }
.breadcrumb-item a { color: #28a745; text-decoration: none; }
.menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.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; text-decoration: none; transition: all 0.3s;
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.back-button:hover { color: white; transform: translateY(-2px); }
.fade-in { animation: fadeIn 0.3s ease-in-out; }
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
font-weight: 600;
}
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ===== TOPBAR ===== */
.topbar {
background: white;
height: 64px;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.topbar-left { display: flex; align-items: center; gap: 14px; }
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.hamburger-btn:hover { background: #f3f4f6; }
.topbar-breadcrumb { font-size: 0.8rem; color: #6b7280; margin-bottom: 2px; }
.topbar-breadcrumb a { color: var(--green-main); text-decoration: none; }
.topbar-breadcrumb a:hover { text-decoration: underline; }
.topbar-title h2 { font-size: 1.1rem; font-weight: 700; color: #111827; margin: 0; }
.topbar-title p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.topbar-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.btn-topbar {
border: none;
padding: 8px 14px;
border-radius: 8px;
font-weight: 600;
font-size: 0.8rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
white-space: nowrap;
cursor: pointer;
}
.btn-topbar:hover { transform: translateY(-1px); text-decoration: none; }
.btn-topbar-back {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
}
.btn-topbar-back:hover { box-shadow: 0 4px 12px rgba(220,38,38,0.35); color: white; }
.btn-topbar-save {
background: linear-gradient(135deg, var(--green-main), var(--green-dark));
color: white;
}
.btn-topbar-save:hover { box-shadow: 0 4px 12px rgba(22,163,74,0.35); color: white; }
/* ===== CONTENT AREA ===== */
.content-area { padding: 20px 24px 32px; flex: 1; }
/* ===== INFO CARD ===== */
.info-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
overflow: hidden;
margin-bottom: 16px;
}
.info-card-header {
padding: 16px 20px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
gap: 10px;
}
.info-card-icon {
width: 34px; height: 34px;
border-radius: 9px;
display: flex; align-items: center; justify-content: center;
font-size: 0.9rem;
flex-shrink: 0;
}
.icon-blue { background: #eff6ff; color: #2563eb; }
.icon-green { background: #f0fdf4; color: var(--green-main); }
.icon-orange { background: #fff7ed; color: #ea580c; }
.icon-purple { background: #faf5ff; color: #7c3aed; }
.icon-gray { background: #f3f4f6; color: #6b7280; }
.info-card-header h3 { font-size: 0.9rem; font-weight: 700; color: #111827; margin: 0; }
.info-card-header p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.info-card-body { padding: 20px; }
/* ===== SECTION DIVIDER ===== */
.section-divider {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #9ca3af;
padding-bottom: 10px;
border-bottom: 1px solid #f3f4f6;
margin-bottom: 16px;
}
/* ===== FORM STYLES ===== */
.form-label-custom {
font-size: 0.78rem;
font-weight: 600;
color: #374151;
margin-bottom: 5px;
display: block;
}
.form-control-custom,
.form-select-custom {
font-size: 0.84rem;
padding: 8px 12px;
border: 1.5px solid #e5e7eb;
border-radius: 8px;
width: 100%;
color: #111827;
background: white;
transition: border-color 0.2s, box-shadow 0.2s;
outline: none;
}
.form-control-custom:focus,
.form-select-custom:focus {
border-color: var(--green-main);
box-shadow: 0 0 0 3px rgba(22,163,74,0.12);
}
.form-control-custom.readonly,
.form-control-custom[readonly] {
background: #f9fafb;
color: #6b7280;
cursor: not-allowed;
}
.form-control-custom.is-invalid,
.form-select-custom.is-invalid {
border-color: #dc2626;
}
.invalid-feedback-custom {
font-size: 0.75rem;
color: #dc2626;
margin-top: 4px;
}
textarea.form-control-custom { resize: vertical; min-height: 80px; }
.lock-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.7rem;
color: #9ca3af;
background: #f3f4f6;
padding: 2px 8px;
border-radius: 10px;
margin-left: 8px;
font-weight: 500;
}
/* ===== ORDER SUMMARY TABLE ===== */
.summary-table { width: 100%; border-collapse: collapse; font-size: 0.83rem; }
.summary-table td { padding: 7px 0; color: #374151; border-bottom: 1px solid #f9fafb; }
.summary-table td:last-child { text-align: right; font-weight: 600; }
.summary-table tfoot td {
border-top: 2px solid #f3f4f6;
border-bottom: none;
font-weight: 700;
color: var(--green-main);
font-size: 0.9rem;
padding-top: 10px;
}
/* ===== FADE IN ===== */
.fade-in { animation: fadeIn 0.3s ease; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.sidebar { width: 100%; position: relative; }
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0,0,0,0.2); }
.main-content { margin-left: 0; }
.hamburger-btn { display: flex; align-items: center; justify-content: center; }
}
@media (max-width: 767px) {
.content-area { padding: 14px 14px 24px; }
.topbar { padding: 0 14px; height: auto; min-height: 58px; flex-wrap: wrap; gap: 8px; padding-top: 10px; padding-bottom: 10px; }
.topbar-actions .btn-topbar span { display: none; }
.topbar-actions .btn-topbar { padding: 8px 10px; }
}
</style>
</head>
<body>
<a href="{{ route('home') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<div class="admin-container">
{{-- Sidebar --}}
<nav class="sidebar">
<!-- ===== SIDEBAR ===== -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3>
<p>Admin Dashboard</p>
<small class="text-light">{{ Auth::user()->name ?? 'Admin User' }}</small>
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div>
<div class="sidebar-menu">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i>Dashboard
<i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item">
<i class="fas fa-seedling"></i>Kelola Produk
<i class="fas fa-seedling"></i><span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item active">
<i class="fas fa-shopping-cart"></i>Pesanan
<i class="fas fa-shopping-cart"></i><span>Pesanan</span>
</a>
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i>Pengajuan Batal
<i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item">
<i class="fas fa-users"></i>Pengguna
<i class="fas fa-users"></i><span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i>Laporan
<i class="fas fa-chart-line"></i><span>Laporan</span>
</a>
<!-- <a href="{{ route('admin.settings') }}" class="menu-item">
<i class="fas fa-cog"></i>Pengaturan -->
</a>
<div class="mt-4 px-3">
</div>
<div class="sidebar-footer">
<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 type="submit" class="btn-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</button>
</form>
</div>
</div>
</nav>
{{-- Main Content --}}
<!-- ===== MAIN ===== -->
<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"><a href="{{ route('admin.orders') }}">Pesanan</a></li>
<li class="breadcrumb-item active">Edit {{ $transaksi->invoice_number }}</li>
</ol>
</nav>
<div class="d-flex justify-content-between align-items-center">
<div>
<h2>Edit Order</h2>
<p class="mb-0">{{ $transaksi->invoice_number }}</p>
</div>
<a href="{{ route('admin.transaksis.show', $transaksi->id) }}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i> Back
</a>
</div>
</div>
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" onclick="toggleSidebar()">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<div class="topbar-breadcrumb">
<a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
&nbsp;/&nbsp;<a href="{{ route('admin.orders') }}">Pesanan</a>
&nbsp;/&nbsp;<a href="{{ route('admin.transaksis.show', $transaksi->id) }}">{{ $transaksi->invoice_number }}</a>
&nbsp;/&nbsp;Edit
</div>
<h2>Edit Order</h2>
<p>{{ $transaksi->invoice_number }}</p>
</div>
</div>
<div class="topbar-actions">
<a href="{{ route('admin.transaksis.show', $transaksi->id) }}" class="btn-topbar btn-topbar-back">
<i class="fas fa-arrow-left"></i>
<span>Kembali</span>
</a>
<button type="submit" form="editOrderForm" class="btn-topbar btn-topbar-save">
<i class="fas fa-save"></i>
<span>Simpan</span>
</button>
</div>
</header>
<!-- Content -->
<div class="content-area fade-in">
@if ($errors->any())
<div class="alert alert-danger alert-dismissible fade show">
<strong>Error!</strong>
<ul class="mb-0 mt-2">
<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
<i class="fas fa-exclamation-circle me-2"></i><strong>Error!</strong>
<ul class="mb-0 mt-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
<li style="font-size:0.85rem;">{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
@ -147,216 +452,235 @@
@endif
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show">
<div class="alert alert-success alert-dismissible fade show mb-4" 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
<form action="{{ route('admin.transaksis.update', $transaksi->id) }}" method="POST">
<form id="editOrderForm" action="{{ route('admin.transaksis.update', $transaksi->id) }}" method="POST">
@csrf
@method('PUT')
<div class="row">
<div class="row g-3">
{{-- Kolom Kiri --}}
<!-- ===== KOLOM KIRI (8/12) ===== -->
<div class="col-lg-8">
{{-- Order Details --}}
<div class="card shadow mb-4" style="border-radius:12px;">
<div class="card-header py-3" style="background:#f8f9fa; border-radius:12px 12px 0 0;">
<h6 class="m-0 fw-bold text-primary">Order Details</h6>
<!-- Order Details (editable) -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-blue">
<i class="fas fa-file-alt"></i>
</div>
<div class="card-body">
<div>
<h3>Order Details</h3>
<p>Status & informasi pesanan yang dapat diubah</p>
</div>
</div>
<div class="info-card-body">
<div class="row g-4">
<div class="col-md-6">
<div class="section-divider">Invoice</div>
<div class="mb-3">
<label class="form-label fw-semibold">Invoice Number</label>
<input type="text" class="form-control bg-light"
<label class="form-label-custom">Invoice Number</label>
<input type="text" class="form-control-custom readonly"
value="{{ $transaksi->invoice_number }}" readonly>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label fw-semibold">Order Status <span class="text-danger">*</span></label>
<select class="form-select @error('order_status') is-invalid @enderror"
name="order_status" required>
<label class="form-label-custom">Tanggal Order</label>
<input type="text" class="form-control-custom readonly"
value="{{ $transaksi->created_at->format('d M Y, H:i') }}" readonly>
</div>
</div>
<div class="col-md-6">
<div class="section-divider">Update Status</div>
<div class="mb-3">
<label class="form-label-custom">Order Status <span style="color:#dc2626;">*</span></label>
<select name="order_status" class="form-select-custom @error('order_status') is-invalid @enderror" required>
@foreach(['pending','processing','shipped','delivered','cancelled'] as $s)
<option value="{{ $s }}"
{{ old('order_status', $transaksi->order_status) == $s ? 'selected' : '' }}>
<option value="{{ $s }}" {{ old('order_status', $transaksi->order_status) == $s ? 'selected' : '' }}>
{{ ucfirst($s) }}
</option>
@endforeach
</select>
@error('order_status')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback-custom">{{ $message }}</div>
@enderror
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label fw-semibold">Payment Status <span class="text-danger">*</span></label>
<select class="form-select @error('payment_status') is-invalid @enderror"
name="payment_status" required>
<label class="form-label-custom">Payment Status <span style="color:#dc2626;">*</span></label>
<select name="payment_status" class="form-select-custom @error('payment_status') is-invalid @enderror" required>
@foreach(['pending','paid','failed'] as $p)
<option value="{{ $p }}"
{{ old('payment_status', $transaksi->payment_status) == $p ? 'selected' : '' }}>
<option value="{{ $p }}" {{ old('payment_status', $transaksi->payment_status) == $p ? 'selected' : '' }}>
{{ ucfirst($p) }}
</option>
@endforeach
</select>
@error('payment_status')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback-custom">{{ $message }}</div>
@enderror
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Tracking Number</label>
<input type="text" class="form-control @error('tracking_number') is-invalid @enderror"
name="tracking_number"
<div class="row g-3 mt-1">
<div class="col-md-6">
<label class="form-label-custom">Tracking Number</label>
<input type="text" name="tracking_number"
class="form-control-custom @error('tracking_number') is-invalid @enderror"
value="{{ old('tracking_number', $transaksi->tracking_number) }}"
placeholder="Enter tracking number">
placeholder="Masukkan nomor resi...">
@error('tracking_number')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback-custom">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Notes</label>
<textarea class="form-control @error('notes') is-invalid @enderror"
name="notes" rows="3"
placeholder="Enter any notes...">{{ old('notes', $transaksi->notes) }}</textarea>
<div class="col-md-6">
<label class="form-label-custom">Catatan / Notes</label>
<textarea name="notes"
class="form-control-custom @error('notes') is-invalid @enderror"
placeholder="Catatan tambahan...">{{ old('notes', $transaksi->notes) }}</textarea>
@error('notes')
<div class="invalid-feedback">{{ $message }}</div>
<div class="invalid-feedback-custom">{{ $message }}</div>
@enderror
</div>
</div>
</div>
</div>
{{-- Customer Information (readonly) --}}
<div class="card shadow mb-4" style="border-radius:12px;">
<div class="card-header py-3" style="background:#f8f9fa; border-radius:12px 12px 0 0;">
<h6 class="m-0 fw-bold text-primary">
Customer Information
<small class="text-muted fw-normal ms-2" style="font-size:0.75rem;">
<i class="fas fa-lock"></i> Tidak dapat diubah
</small>
</h6>
<!-- Customer Information (readonly) -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-purple">
<i class="fas fa-user"></i>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label fw-semibold">Customer Name</label>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->customer_name }}" readonly>
<div>
<h3>Customer Information</h3>
<p>Informasi pembeli <span class="lock-badge"><i class="fas fa-lock"></i> Tidak dapat diubah</span></p>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label fw-semibold">Phone</label>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->customer_phone }}" readonly>
</div>
<div class="info-card-body">
<div class="row g-3">
<div class="col-md-12">
<label class="form-label-custom">Nama Customer</label>
<input type="text" class="form-control-custom" value="{{ $transaksi->customer_name }}" readonly>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label fw-semibold">Email</label>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->customer_email ?? '-' }}" readonly>
<label class="form-label-custom">Telepon</label>
<input type="text" class="form-control-custom" value="{{ $transaksi->customer_phone }}" readonly>
</div>
<div class="col-md-6">
<label class="form-label-custom">Email</label>
<input type="text" class="form-control-custom" value="{{ $transaksi->customer_email ?? '-' }}" readonly>
</div>
</div>
</div>
</div>
{{-- Shipping Information (readonly) --}}
<div class="card shadow mb-4" style="border-radius:12px;">
<div class="card-header py-3" style="background:#f8f9fa; border-radius:12px 12px 0 0;">
<h6 class="m-0 fw-bold text-primary">
Shipping Information
<small class="text-muted fw-normal ms-2" style="font-size:0.75rem;">
<i class="fas fa-lock"></i> Tidak dapat diubah
</small>
</h6>
<!-- Shipping Information (readonly) -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-orange">
<i class="fas fa-truck"></i>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label fw-semibold">Shipping Address</label>
<textarea class="form-control bg-light" rows="3"
readonly>{{ $transaksi->shipping_address }}</textarea>
<div>
<h3>Pengiriman</h3>
<p>Alamat & info pengiriman <span class="lock-badge"><i class="fas fa-lock"></i> Tidak dapat diubah</span></p>
</div>
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label class="form-label fw-semibold">City</label>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->city }}" readonly>
</div>
<div class="info-card-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label-custom">Alamat Pengiriman</label>
<textarea class="form-control-custom" rows="3" readonly>{{ $transaksi->shipping_address }}</textarea>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label fw-semibold">Province</label>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->province }}" readonly>
</div>
<label class="form-label-custom">Kota</label>
<input type="text" class="form-control-custom" value="{{ $transaksi->city }}" readonly>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label fw-semibold">Postal Code</label>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->postal_code }}" readonly>
<label class="form-label-custom">Provinsi</label>
<input type="text" class="form-control-custom" value="{{ $transaksi->province }}" readonly>
</div>
<div class="col-md-4">
<label class="form-label-custom">Kode Pos</label>
<input type="text" class="form-control-custom" value="{{ $transaksi->postal_code }}" readonly>
</div>
</div>
</div>
</div>
</div>
{{-- Akhir col-lg-8 --}}
<!-- Akhir col-lg-8 -->
{{-- Kolom Kanan --}}
<!-- ===== KOLOM KANAN (4/12) ===== -->
<div class="col-lg-4">
{{-- Order Summary --}}
<div class="card shadow mb-4" style="border-radius:12px;">
<div class="card-header py-3" style="background:#f8f9fa; border-radius:12px 12px 0 0;">
<h6 class="m-0 fw-bold text-primary">Order Summary</h6>
<!-- Order Summary -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-green">
<i class="fas fa-box"></i>
</div>
<div class="card-body">
<h5 class="text-primary">{{ $transaksi->invoice_number }}</h5>
<p class="text-muted mb-3">{{ $transaksi->created_at->format('d M Y, H:i') }}</p>
<table class="table table-sm table-borderless">
<div>
<h3>Order Summary</h3>
<p>Ringkasan produk pesanan</p>
</div>
</div>
<div class="info-card-body">
<table class="summary-table">
<tbody>
@foreach($transaksi->details as $detail)
<tr>
<td>{{ $detail->product->name ?? 'N/A' }}</td>
<td class="text-end">x{{ $detail->quantity }}</td>
<td class="text-end">Rp {{ number_format($detail->subtotal, 0, ',', '.') }}</td>
<td style="text-align:center;color:#9ca3af;">x{{ $detail->quantity }}</td>
<td>Rp {{ number_format($detail->subtotal, 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
<tfoot class="border-top">
<tfoot>
<tr>
<th colspan="2">Total:</th>
<th class="text-end">Rp {{ number_format($transaksi->total_amount, 0, ',', '.') }}</th>
<td colspan="2" style="font-size:0.8rem;color:#6b7280;">Total Amount</td>
<td style="color:var(--green-main);font-size:0.95rem;">Rp {{ number_format($transaksi->total_amount, 0, ',', '.') }}</td>
</tr>
</tfoot>
</table>
</div>
</div>
{{-- Action Buttons --}}
<div class="card shadow" style="border-radius:12px;">
<div class="card-body">
<button type="submit" class="btn btn-primary w-100 mb-2">
<i class="fas fa-save me-1"></i> Update Order
<!-- Action Buttons -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-gray">
<i class="fas fa-cog"></i>
</div>
<div>
<h3>Aksi</h3>
<p>Simpan atau batalkan perubahan</p>
</div>
</div>
<div class="info-card-body" style="display:flex;flex-direction:column;gap:10px;">
<button type="submit" form="editOrderForm"
style="width:100%;padding:10px 16px;border-radius:9px;border:none;
background:linear-gradient(135deg,var(--green-main),var(--green-dark));
color:white;font-weight:700;font-size:0.875rem;cursor:pointer;
display:flex;align-items:center;justify-content:center;gap:8px;
transition:all 0.2s;">
<i class="fas fa-save"></i> Update Order
</button>
<a href="{{ route('admin.transaksis.show', $transaksi->id) }}"
class="btn btn-secondary w-100">
<i class="fas fa-times me-1"></i> Cancel
style="width:100%;padding:10px 16px;border-radius:9px;
border:1.5px solid #e5e7eb;background:#f9fafb;
color:#374151;font-weight:600;font-size:0.875rem;cursor:pointer;
display:flex;align-items:center;justify-content:center;gap:8px;
text-decoration:none;transition:all 0.2s;">
<i class="fas fa-times"></i> Batal
</a>
</div>
</div>
</div>
{{-- Akhir col-lg-4 --}}
<!-- Akhir col-lg-4 -->
</div>
</form>
@ -364,8 +688,28 @@ class="btn btn-secondary w-100">
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('open');
document.getElementById('sidebarOverlay').classList.toggle('show');
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebarOverlay').classList.remove('show');
}
window.addEventListener('resize', function() {
if (window.innerWidth > 900) closeSidebar();
});
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.alert').forEach(function(alert) {
setTimeout(function() {
const a = bootstrap.Alert.getOrCreateInstance(alert);
if (a) a.close();
}, 5000);
});
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -6,331 +6,530 @@
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Kelola Pengguna - Bibit Cabai Admin</title>
<!-- 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; }
:root {
--sidebar-width: 260px;
--green-dark: #15803d;
--green-main: #16a34a;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f8f9fa;
background: #f1f5f2;
overflow-x: hidden;
}
.admin-container {
display: flex;
min-height: 100vh;
}
/* Sidebar */
/* ===== SIDEBAR ===== */
.sidebar {
width: 280px;
background: linear-gradient(180deg, #28a745, #20c997);
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
top: 0; left: 0;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.sidebar-header {
padding: 25px 20px;
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.1);
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.sidebar-header h3 {
font-size: 1.8rem;
font-weight: bold;
margin-bottom: 5px;
.sidebar-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-header h3 { font-size: 1.35rem; font-weight: 700; margin-bottom: 2px; }
.sidebar-header .subtitle { opacity: 0.65; font-size: 0.78rem; margin-bottom: 6px; }
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-header p { opacity: 0.8; font-size: 0.9rem; }
.sidebar-menu { padding: 12px 0; flex: 1; }
.sidebar-menu { padding: 20px 0; }
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display: block;
padding: 15px 25px;
color: white;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.3s;
border-left: 4px solid transparent;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
}
.menu-item:hover, .menu-item.active {
background: rgba(255,255,255,0.2);
.menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
transform: translateX(5px);
font-weight: 600;
}
.menu-item i { width: 20px; margin-right: 10px; }
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
/* Main Content */
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: 280px;
flex: 1;
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
.admin-header {
/* ===== TOPBAR ===== */
.topbar {
background: white;
padding: 20px 30px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border-bottom: 1px solid #dee2e6;
height: 64px;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.content-area { padding: 30px; }
.topbar-left { display: flex; align-items: center; gap: 14px; }
.page-header {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
margin-bottom: 30px;
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.breadcrumb { background: transparent; padding: 0; margin-bottom: 10px; }
.breadcrumb-item a { color: #28a745; text-decoration: none; }
.hamburger-btn:hover { background: #f3f4f6; }
/* Stats Cards */
.topbar-breadcrumb { font-size: 0.8rem; color: #6b7280; margin-bottom: 2px; }
.topbar-breadcrumb a { color: var(--green-main); text-decoration: none; }
.topbar-title h2 { font-size: 1.1rem; font-weight: 700; color: #111827; margin: 0; }
.topbar-title p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.btn-back-website {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
border: none;
padding: 9px 18px;
border-radius: 8px;
font-weight: 600;
font-size: 0.825rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 7px;
transition: all 0.2s;
white-space: nowrap;
}
.btn-back-website:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(220,38,38,0.35);
color: white;
text-decoration: none;
}
/* ===== CONTENT AREA ===== */
.content-area { padding: 20px 24px 24px; flex: 1; }
/* ===== STATS GRID ===== */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 30px;
gap: 16px;
margin-bottom: 20px;
}
.stat-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
padding: 18px 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
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-card.primary { border-left-color: #2563eb; }
.stat-card.warning { border-left-color: #d97706; }
.stat-card.success { border-left-color: var(--green-main); }
.stat-card.info { border-left-color: #0891b2; }
.stat-label {
font-size: 0.75rem;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
margin-bottom: 5px;
letter-spacing: 0.04em;
margin-bottom: 4px;
}
.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-card.primary .stat-label { color: #2563eb; }
.stat-card.warning .stat-label { color: #d97706; }
.stat-card.success .stat-label { color: var(--green-main); }
.stat-card.info .stat-label { color: #0891b2; }
.stat-value { font-size: 1.5rem; font-weight: 700; color: #333; }
.stat-icon { font-size: 2rem; color: #dee2e6; }
.stat-value { font-size: 1.5rem; font-weight: 800; color: #111827; }
.stat-icon { font-size: 1.75rem; color: #e5e7eb; }
/* 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; }
/* Table */
.data-table {
width: 100%;
border-collapse: collapse;
/* ===== TABLE CARD ===== */
.table-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
overflow: hidden;
}
.table-card-header {
padding: 18px 20px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.section-title {
font-size: 1rem;
font-weight: 700;
color: #111827;
display: flex;
align-items: center;
gap: 8px;
}
/* Search */
.search-bar {
padding: 14px 20px;
border-bottom: 1px solid #f3f4f6;
}
/* ===== TABLE ===== */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th, .data-table td {
padding: 12px 15px;
padding: 11px 14px;
text-align: left;
border-bottom: 1px solid #dee2e6;
border-bottom: 1px solid #f3f4f6;
font-size: 0.875rem;
vertical-align: middle;
}
.data-table th {
background: #f8f9fa;
background: #f9fafb;
font-weight: 600;
color: #495057;
font-size: 0.85rem;
color: #6b7280;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.04em;
white-space: nowrap;
}
.data-table tbody tr:hover { background-color: #f8f9fc; }
.data-table tbody tr:hover { background: #f9fafb; }
/* Badges */
/* ===== BADGES ===== */
.badge-role {
padding: 4px 10px;
border-radius: 12px;
font-size: 0.75rem;
font-size: 0.72rem;
font-weight: 600;
display: inline-block;
}
.badge-admin { background: #cce5ff; color: #004085; }
.badge-user { background: #d4edda; color: #155724; }
.badge-verified { background: #d4edda; color: #155724; }
.badge-unverified { background: #e2e3e5; color: #383d41; }
.badge-admin { background: #dbeafe; color: #1e40af; }
.badge-user { background: #dcfce7; color: #166534; }
.badge-verified { background: #dcfce7; color: #166534; }
.badge-unverified { background: #f3f4f6; color: #6b7280; }
/* Action Buttons */
/* ===== ACTION BUTTONS ===== */
.btn-action {
padding: 6px 10px;
padding: 6px 9px;
border: none;
border-radius: 6px;
font-size: 0.8rem;
cursor: pointer;
transition: all 0.3s;
transition: all 0.2s;
text-decoration: none;
display: inline-flex;
align-items: center;
justify-content: center;
}
.btn-action:hover {
transform: translateY(-1px);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
text-decoration: none;
color: inherit;
}
.btn-view { background: #17a2b8; color: white; }
.btn-edit { background: #ffc107; color: #212529; }
.btn-delete { background: #dc3545; color: white; }
.btn-view { background: #0891b2; color: white; }
.btn-edit { background: #d97706; color: white; }
.btn-delete { background: #dc2626; color: white; }
/* Back Button */
.back-button {
position: fixed;
top: 20px;
right: 20px;
z-index: 1100;
background: linear-gradient(45deg, #dc3545, #c82333);
/* ===== TABLE FOOTER ===== */
.table-footer {
padding: 14px 20px;
border-top: 1px solid #f3f4f6;
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
/* ===== MOBILE CARDS ===== */
.mobile-card-list { display: none; padding: 14px; }
.user-card {
background: white;
border-radius: 12px;
padding: 16px;
margin-bottom: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
}
.user-card-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 12px;
gap: 10px;
}
.user-card-avatar {
width: 42px; height: 42px;
background: linear-gradient(135deg, var(--green-dark), var(--green-main));
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
border: none;
padding: 12px 20px;
border-radius: 25px;
font-weight: 600;
transition: all 0.3s;
text-decoration: none;
font-weight: 700;
font-size: 1rem;
flex-shrink: 0;
}
.back-button:hover {
background: linear-gradient(45deg, #c82333, #a71e2a);
transform: translateY(-2px);
color: white;
text-decoration: none;
.user-card-name { font-weight: 700; font-size: 0.95rem; color: #111827; }
.user-card-email { font-size: 0.78rem; color: #9ca3af; margin-top: 2px; }
.user-card-badges { display: flex; gap: 6px; flex-wrap: wrap; }
.user-card-meta {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 12px;
font-size: 0.8rem;
}
.fade-in {
animation: fadeIn 0.3s ease-in-out;
}
.user-card-meta-item .label { color: #9ca3af; font-size: 0.7rem; text-transform: uppercase; font-weight: 600; margin-bottom: 2px; }
.user-card-meta-item .value { color: #374151; }
.user-card-actions { display: flex; gap: 8px; }
.user-card-actions .btn-action { flex: 1; justify-content: center; padding: 8px; }
/* ===== FADE IN ===== */
.fade-in { animation: fadeIn 0.3s ease; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.sidebar { width: 100%; position: relative; }
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0,0,0,0.2); }
.main-content { margin-left: 0; }
.hamburger-btn { display: flex; align-items: center; justify-content: center; }
}
@media (max-width: 1100px) {
.stats-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 767px) {
.desktop-table-wrap { display: none; }
.mobile-card-list { display: block; }
.stats-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
.content-area { padding: 14px 14px 20px; }
.topbar { padding: 0 14px; }
.btn-back-website span { display: none; }
.btn-back-website { padding: 9px 12px; }
.table-card-header { flex-direction: column; align-items: stretch; }
.table-card-header .btn { width: 100%; }
.table-footer { flex-direction: column; align-items: flex-start; }
}
@media (max-width: 400px) {
.stats-grid { grid-template-columns: 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">
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<!-- Sidebar -->
<nav class="sidebar">
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3>
<p>Admin Dashboard</p>
<small class="text-light">{{ Auth::user()->name ?? 'Admin User' }}</small>
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div>
<div class="sidebar-menu">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i>Dashboard
<i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item">
<i class="fas fa-seedling"></i>Kelola Produk
<i class="fas fa-seedling"></i><span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item">
<i class="fas fa-shopping-cart"></i>Pesanan
<i class="fas fa-shopping-cart"></i><span>Pesanan</span>
</a>
{{-- TAMBAHKAN DI SINI --}}
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i>Pengajuan Batal
<i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item active">
<i class="fas fa-users"></i>Pengguna
<i class="fas fa-users"></i><span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i>Laporan
<i class="fas fa-chart-line"></i><span>Laporan</span>
</a>
<!-- <a href="{{ route('admin.settings') }}" class="menu-item">
<i class="fas fa-cog"></i>Pengaturan
</a> -->
<div class="mt-4 px-3">
</div>
<div class="sidebar-footer">
<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 type="submit" class="btn-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</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">Pengguna</li>
</ol>
</nav>
<h2>Kelola Pengguna</h2>
<p class="mb-0">Kelola semua pengguna terdaftar di sini!</p>
</div>
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" onclick="toggleSidebar()" aria-label="Toggle menu">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<div class="topbar-breadcrumb">
<a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
&nbsp;/&nbsp;Pengguna
</div>
<h2>Kelola Pengguna</h2>
<p>Kelola semua pengguna terdaftar di sini!</p>
</div>
</div>
<a href="{{ route('home') }}" class="btn-back-website">
<i class="fas fa-arrow-left"></i>
<span>Kembali ke Website</span>
</a>
</header>
<!-- Content -->
<div class="content-area fade-in">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
<div class="alert alert-success alert-dismissible fade show mb-4" 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
@if(session('error'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
<i class="fas fa-exclamation-circle me-2"></i>{{ session('error') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
<!-- Stats Cards -->
<!-- Stats -->
<div class="stats-grid">
<div class="stat-card primary">
<div>
@ -362,20 +561,20 @@
</div>
</div>
<!-- Users Table -->
<div class="page-header">
<div class="section-header">
<h3 class="section-title">
<i class="fas fa-users text-success me-2"></i>
<!-- Table Card -->
<div class="table-card">
<div class="table-card-header">
<div class="section-title">
<i class="fas fa-users text-success"></i>
Daftar Pengguna ({{ $totalUsers ?? 0 }})
</h3>
</div>
<a href="{{ route('admin.users.create') }}" class="btn btn-primary btn-sm">
<i class="fas fa-plus me-1"></i>Tambah Pengguna
</a>
</div>
<!-- Search -->
<div class="mb-3">
<div class="search-bar">
<div class="input-group" style="max-width:400px;">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" id="searchInput"
@ -384,11 +583,13 @@
</div>
@if(isset($users) && $users->count() > 0)
<div class="table-responsive">
<!-- DESKTOP TABLE -->
<div class="desktop-table-wrap table-responsive">
<table class="data-table" id="usersTable">
<thead>
<tr>
<th>ID</th>
<th style="padding-left:20px;">ID</th>
<th>Nama</th>
<th>Email</th>
<th>Telepon</th>
@ -401,11 +602,13 @@
<tbody>
@foreach($users as $user)
<tr>
<td>{{ ($users->currentPage() - 1) * $users->perPage() + $loop->iteration }}</td>
<td><strong>{{ $user->name }}</strong></td>
<td>{{ $user->email }}</td>
<td>{{ $user->phone ?? '-' }}</td>
<td><small>{{ \Illuminate\Support\Str::limit($user->address ?? '-', 30) }}</small></td>
<td style="padding-left:20px; color:#6b7280;">
{{ ($users->currentPage() - 1) * $users->perPage() + $loop->iteration }}
</td>
<td><strong style="color:#111827;">{{ $user->name }}</strong></td>
<td style="color:#374151;">{{ $user->email }}</td>
<td style="color:#374151;">{{ $user->phone ?? '-' }}</td>
<td><small style="color:#6b7280;">{{ \Illuminate\Support\Str::limit($user->address ?? '-', 30) }}</small></td>
<td>
@if($user->is_admin == 1)
<span class="badge-role badge-admin">Admin</span>
@ -449,38 +652,134 @@ class="btn-action btn-edit" title="Edit">
</table>
</div>
<!-- MOBILE CARD LIST -->
<div class="mobile-card-list" id="mobileUserList">
@foreach($users as $user)
<div class="user-card" data-search="{{ strtolower($user->name . ' ' . $user->email . ' ' . ($user->phone ?? '')) }}">
<div class="user-card-top">
<div class="d-flex align-items-center gap-3">
<div class="user-card-avatar">
{{ strtoupper(substr($user->name, 0, 1)) }}
</div>
<div>
<div class="user-card-name">{{ $user->name }}</div>
<div class="user-card-email">{{ $user->email }}</div>
</div>
</div>
<div class="user-card-badges">
@if($user->is_admin == 1)
<span class="badge-role badge-admin">Admin</span>
@else
<span class="badge-role badge-user">User</span>
@endif
</div>
</div>
<div class="user-card-meta">
<div class="user-card-meta-item">
<div class="label">Telepon</div>
<div class="value">{{ $user->phone ?? '-' }}</div>
</div>
<div class="user-card-meta-item">
<div class="label">Status Email</div>
<div class="value">
@if($user->email_verified_at)
<span class="badge-role badge-verified" style="font-size:0.68rem;">
<i class="fas fa-check me-1"></i>Terverifikasi
</span>
@else
<span class="badge-role badge-unverified" style="font-size:0.68rem;">Belum Verifikasi</span>
@endif
</div>
</div>
@if($user->address)
<div class="user-card-meta-item" style="grid-column:span 2;">
<div class="label">Alamat</div>
<div class="value">{{ \Illuminate\Support\Str::limit($user->address, 60) }}</div>
</div>
@endif
</div>
<div class="user-card-actions">
<a href="{{ route('admin.users.show', $user) }}" class="btn-action btn-view" title="Detail">
<i class="fas fa-eye"></i>
</a>
<a href="{{ route('admin.users.edit', $user) }}" class="btn-action btn-edit" title="Edit">
<i class="fas fa-edit"></i> Edit
</a>
<form method="POST" action="{{ route('admin.users.destroy', $user) }}"
style="flex:0;"
onsubmit="return confirm('Yakin ingin menghapus pengguna ini?')">
@csrf
@method('DELETE')
<button type="submit" class="btn-action btn-delete" title="Hapus">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
</div>
@endforeach
</div>
<!-- Pagination -->
<div class="d-flex justify-content-between align-items-center mt-4">
<small class="text-muted">
Menampilkan {{ $users->firstItem() }} sampai {{ $users->lastItem() }}
<div class="table-footer">
<small style="color:#6b7280;">
Menampilkan {{ $users->firstItem() }}{{ $users->lastItem() }}
dari {{ $users->total() }} pengguna
</small>
<div>{{ $users->links() }}</div>
</div>
@else
<div class="text-center py-5">
<i class="fas fa-users fa-3x text-muted mb-3 d-block"></i>
<h5 class="text-muted">Belum ada pengguna terdaftar</h5>
<p class="text-muted">Mulai dengan menambahkan pengguna baru.</p>
<div style="text-align:center; padding:50px 20px; color:#9ca3af;">
<i class="fas fa-users" style="font-size:2.5rem; display:block; margin-bottom:12px; opacity:0.3;"></i>
<h5 style="color:#6b7280;">Belum ada pengguna terdaftar</h5>
<p style="font-size:0.875rem;">Mulai dengan menambahkan pengguna baru.</p>
</div>
@endif
</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>
// Live search
// ===== SIDEBAR =====
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.toggle('open');
overlay.classList.toggle('show');
document.body.style.overflow = sidebar.classList.contains('open') ? 'hidden' : '';
}
function closeSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
sidebar.classList.remove('open');
overlay.classList.remove('show');
document.body.style.overflow = '';
}
window.addEventListener('resize', function() {
if (window.innerWidth > 900) closeSidebar();
});
// ===== LIVE SEARCH (desktop table + mobile cards) =====
document.getElementById('searchInput').addEventListener('keyup', function() {
const filter = this.value.toLowerCase();
// Desktop table rows
const rows = document.querySelectorAll('#usersTable tbody tr');
rows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(filter) ? '' : 'none';
row.style.display = row.textContent.toLowerCase().includes(filter) ? '' : 'none';
});
// Mobile cards
const cards = document.querySelectorAll('.user-card');
cards.forEach(card => {
const text = card.getAttribute('data-search') || card.textContent.toLowerCase();
card.style.display = text.includes(filter) ? '' : 'none';
});
});
</script>

View File

@ -4,222 +4,764 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Pengguna - Bibit Cabai Admin</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<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',sans-serif; background:#f8f9fa; }
.admin-container { display:flex; min-height:100vh; }
:root {
--sidebar-width: 260px;
--green-dark: #15803d;
--green-main: #16a34a;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f1f5f2;
overflow-x: hidden;
}
/* ===== SIDEBAR ===== */
.sidebar {
width:280px; background:linear-gradient(180deg,#28a745,#20c997);
color:white; min-height:100vh; position:fixed; top:0; left:0; z-index:1000;
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0; left: 0;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.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-menu { padding:20px 0; }
.sidebar-header {
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.sidebar-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-header h3 { font-size: 1.35rem; font-weight: 700; margin-bottom: 2px; }
.sidebar-header .subtitle { opacity: 0.65; font-size: 0.78rem; margin-bottom: 6px; }
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-menu { padding: 12px 0; flex: 1; }
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display:block; padding:15px 25px; color:white; text-decoration:none;
transition:all 0.3s; border-left:4px solid transparent;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
}
.menu-item:hover, .menu-item.active {
background:rgba(255,255,255,0.2); color:white;
border-left-color:#fff; transform:translateX(5px);
.menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.menu-item i { width:20px; margin-right:10px; }
.main-content { margin-left:280px; flex:1; }
.admin-header { background:white; padding:20px 30px; box-shadow:0 2px 4px rgba(0,0,0,0.1); }
.content-area { padding:30px; }
.form-card { background:white; border-radius:12px; padding:35px; box-shadow:0 4px 6px rgba(0,0,0,0.05); max-width:700px; }
.form-label { font-weight:600; color:#495057; }
.form-control:focus { border-color:#28a745; box-shadow:0 0 0 0.2rem rgba(40,167,69,0.25); }
.btn-save { background:linear-gradient(45deg,#28a745,#20c997); border:none; color:white; padding:12px 30px; border-radius:8px; font-weight:600; }
.btn-save:hover { opacity:0.9; color:white; }
.breadcrumb { background:transparent; padding:0; margin-bottom:10px; }
.breadcrumb-item a { color:#28a745; text-decoration:none; }
.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; text-decoration:none; transition:all 0.3s;
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
font-weight: 600;
}
.back-button:hover { transform:translateY(-2px); color:white; }
.section-divider { border-top:2px solid #f8f9fa; margin:25px 0; }
.user-info-badge {
background:#f8f9fa; border-radius:8px; padding:12px 15px;
margin-bottom:20px; font-size:0.85rem; color:#6c757d;
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ===== TOPBAR ===== */
.topbar {
background: white;
height: 64px;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.topbar-left { display: flex; align-items: center; gap: 14px; }
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.hamburger-btn:hover { background: #f3f4f6; }
.topbar-breadcrumb { font-size: 0.8rem; color: #6b7280; margin-bottom: 2px; }
.topbar-breadcrumb a { color: var(--green-main); text-decoration: none; }
.topbar-breadcrumb a:hover { text-decoration: underline; }
.topbar-title h2 { font-size: 1.1rem; font-weight: 700; color: #111827; margin: 0; }
.topbar-title p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.topbar-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.btn-topbar {
border: none;
padding: 8px 14px;
border-radius: 8px;
font-weight: 600;
font-size: 0.8rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
white-space: nowrap;
cursor: pointer;
}
.btn-topbar:hover { transform: translateY(-1px); text-decoration: none; }
.btn-topbar-back {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
}
.btn-topbar-back:hover { box-shadow: 0 4px 12px rgba(220,38,38,0.35); color: white; }
.btn-topbar-save {
background: linear-gradient(135deg, var(--green-main), var(--green-dark));
color: white;
}
.btn-topbar-save:hover { box-shadow: 0 4px 12px rgba(22,163,74,0.35); color: white; }
.btn-topbar-view {
background: linear-gradient(135deg, #0891b2, #0e7490);
color: white;
}
.btn-topbar-view:hover { box-shadow: 0 4px 12px rgba(8,145,178,0.35); color: white; }
/* ===== CONTENT AREA ===== */
.content-area { padding: 20px 24px 32px; flex: 1; }
/* ===== INFO CARD ===== */
.info-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
overflow: hidden;
margin-bottom: 16px;
}
.info-card-header {
padding: 16px 20px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
gap: 10px;
}
.info-card-icon {
width: 34px; height: 34px;
border-radius: 9px;
display: flex; align-items: center; justify-content: center;
font-size: 0.9rem;
flex-shrink: 0;
}
.icon-blue { background: #eff6ff; color: #2563eb; }
.icon-green { background: #f0fdf4; color: var(--green-main); }
.icon-orange { background: #fff7ed; color: #ea580c; }
.icon-purple { background: #faf5ff; color: #7c3aed; }
.icon-yellow { background: #fefce8; color: #ca8a04; }
.icon-gray { background: #f3f4f6; color: #6b7280; }
.icon-red { background: #fef2f2; color: #dc2626; }
.info-card-header h3 { font-size: 0.9rem; font-weight: 700; color: #111827; margin: 0; }
.info-card-header p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.info-card-body { padding: 20px; }
/* ===== SECTION DIVIDER ===== */
.section-divider {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #9ca3af;
padding-bottom: 10px;
border-bottom: 1px solid #f3f4f6;
margin-bottom: 16px;
}
/* ===== FORM STYLES ===== */
.form-label-custom {
font-size: 0.78rem;
font-weight: 600;
color: #374151;
margin-bottom: 5px;
display: block;
}
.form-control-custom,
.form-select-custom {
font-size: 0.84rem;
padding: 8px 12px;
border: 1.5px solid #e5e7eb;
border-radius: 8px;
width: 100%;
color: #111827;
background: white;
transition: border-color 0.2s, box-shadow 0.2s;
outline: none;
}
.form-control-custom:focus,
.form-select-custom:focus {
border-color: var(--green-main);
box-shadow: 0 0 0 3px rgba(22,163,74,0.12);
}
.form-control-custom.is-invalid,
.form-select-custom.is-invalid {
border-color: #dc2626;
}
textarea.form-control-custom { resize: vertical; min-height: 90px; }
.invalid-feedback-custom {
font-size: 0.75rem;
color: #dc2626;
margin-top: 4px;
}
/* Password toggle */
.input-pw-wrap {
position: relative;
}
.input-pw-wrap .form-control-custom {
padding-right: 40px;
}
.btn-pw-toggle {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: #9ca3af;
cursor: pointer;
font-size: 0.85rem;
padding: 0;
line-height: 1;
}
.btn-pw-toggle:hover { color: #374151; }
/* User info badge */
.user-meta-badge {
display: flex;
flex-wrap: wrap;
gap: 10px;
background: #f9fafb;
border: 1px solid #f3f4f6;
border-radius: 10px;
padding: 12px 16px;
margin-bottom: 20px;
font-size: 0.8rem;
color: #6b7280;
}
.user-meta-badge span { display: flex; align-items: center; gap: 5px; }
.user-meta-badge strong { color: #111827; }
/* Checkbox custom */
.check-card {
border: 1.5px solid #e5e7eb;
border-radius: 10px;
padding: 14px 16px;
display: flex;
align-items: flex-start;
gap: 12px;
cursor: pointer;
transition: border-color 0.2s, background 0.2s;
}
.check-card:has(input:checked) {
border-color: var(--green-main);
background: #f0fdf4;
}
.check-card input[type="checkbox"] {
width: 17px; height: 17px;
accent-color: var(--green-main);
margin-top: 2px;
flex-shrink: 0;
cursor: pointer;
}
.check-card-label { font-size: 0.84rem; font-weight: 600; color: #111827; }
.check-card-desc { font-size: 0.75rem; color: #6b7280; margin-top: 2px; }
.check-card-desc.text-danger { color: #dc2626 !important; }
/* ===== AVATAR MINI ===== */
.avatar-mini {
width: 44px; height: 44px;
border-radius: 50%;
background: linear-gradient(135deg, var(--green-main), var(--green-dark));
display: flex; align-items: center; justify-content: center;
font-size: 1.1rem; color: white; font-weight: 700;
flex-shrink: 0;
}
/* ===== FADE IN ===== */
.fade-in { animation: fadeIn 0.3s ease; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0,0,0,0.2); }
.main-content { margin-left: 0; }
.hamburger-btn { display: flex; align-items: center; justify-content: center; }
}
@media (max-width: 767px) {
.content-area { padding: 14px 14px 24px; }
.topbar { padding: 0 14px; height: auto; min-height: 58px; flex-wrap: wrap; gap: 8px; padding-top: 10px; padding-bottom: 10px; }
.topbar-actions .btn-topbar span { display: none; }
.topbar-actions .btn-topbar { padding: 8px 10px; }
}
</style>
</head>
<body>
<a href="{{ route('home') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<div class="admin-container">
<nav class="sidebar">
<!-- ===== SIDEBAR ===== -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3>
<p>Admin Dashboard</p>
<small class="text-light">{{ Auth::user()->name ?? 'Admin' }}</small>
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</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"><i class="fas fa-shopping-cart"></i>Pesanan</a>
<a href="{{ route('admin.users') }}" class="menu-item active"><i class="fas fa-users"></i>Pengguna</a>
<a href="{{ route('admin.reports') }}" 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">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item">
<i class="fas fa-seedling"></i><span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item">
<i class="fas fa-shopping-cart"></i><span>Pesanan</span>
</a>
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item active">
<i class="fas fa-users"></i><span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i><span>Laporan</span>
</a>
</div>
<div class="sidebar-footer">
<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 type="submit" class="btn-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</button>
</form>
</div>
</div>
</nav>
<!-- ===== MAIN ===== -->
<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"><a href="{{ route('admin.users') }}">Pengguna</a></li>
<li class="breadcrumb-item active">Edit Pengguna</li>
</ol>
</nav>
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" onclick="toggleSidebar()">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<div class="topbar-breadcrumb">
<a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
&nbsp;/&nbsp;<a href="{{ route('admin.users') }}">Pengguna</a>
&nbsp;/&nbsp;<a href="{{ route('admin.users.show', $user) }}">{{ $user->name }}</a>
&nbsp;/&nbsp;Edit
</div>
<h2>Edit Pengguna</h2>
<p class="mb-0">Perbarui informasi pengguna <strong>{{ $user->name }}</strong>.</p>
<p>{{ $user->name }}</p>
</div>
<div class="content-area">
<div class="form-card">
<div class="user-info-badge">
<i class="fas fa-info-circle me-2 text-info"></i>
ID Pengguna: <strong>#{{ $user->id }}</strong> &nbsp;|&nbsp;
Terdaftar: <strong>{{ $user->created_at->format('d M Y') }}</strong>
@if($user->email_verified_at)
&nbsp;|&nbsp; <span class="text-success"><i class="fas fa-check-circle"></i> Email Terverifikasi</span>
@else
&nbsp;|&nbsp; <span class="text-muted"><i class="fas fa-clock"></i> Belum Verifikasi</span>
@endif
</div>
<div class="topbar-actions">
<a href="{{ route('admin.users') }}" class="btn-topbar btn-topbar-back">
<i class="fas fa-arrow-left"></i>
<span>Kembali</span>
</a>
<a href="{{ route('admin.users.show', $user) }}" class="btn-topbar btn-topbar-view">
<i class="fas fa-eye"></i>
<span>Lihat Detail</span>
</a>
<button type="submit" form="editUserForm" class="btn-topbar btn-topbar-save">
<i class="fas fa-save"></i>
<span>Simpan</span>
</button>
</div>
</header>
<!-- Content -->
<div class="content-area fade-in">
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
<i class="fas fa-exclamation-circle me-2"></i><strong>Error!</strong>
<ul class="mb-0 mt-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
<li style="font-size:0.85rem;">{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
<form method="POST" action="{{ route('admin.users.update', $user) }}">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show mb-4" 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
<form id="editUserForm" method="POST" action="{{ route('admin.users.update', $user) }}">
@csrf
@method('PUT')
<h5 class="mb-3 text-success"><i class="fas fa-user me-2"></i>Informasi Dasar</h5>
<div class="row g-3">
<div class="mb-3">
<label class="form-label">Nama Lengkap <span class="text-danger">*</span></label>
<input type="text" name="name" class="form-control @error('name') is-invalid @enderror"
<!-- ===== KOLOM KIRI (8/12) ===== -->
<div class="col-lg-8">
<!-- Informasi Dasar -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-blue">
<i class="fas fa-user"></i>
</div>
<div>
<h3>Informasi Dasar</h3>
<p>Nama, email, dan password pengguna</p>
</div>
</div>
<div class="info-card-body">
<!-- User meta badge -->
<div class="user-meta-badge">
<span><i class="fas fa-hashtag" style="color:var(--green-main)"></i> ID: <strong>#{{ $user->id }}</strong></span>
<span><i class="fas fa-calendar" style="color:var(--green-main)"></i> Terdaftar: <strong>{{ $user->created_at->format('d M Y') }}</strong></span>
<span>
@if($user->email_verified_at)
<i class="fas fa-check-circle" style="color:#16a34a"></i> <strong style="color:#16a34a">Email Terverifikasi</strong>
@else
<i class="fas fa-clock" style="color:#9ca3af"></i> <span style="color:#9ca3af">Belum Verifikasi</span>
@endif
</span>
</div>
<div class="row g-3">
<div class="col-12">
<label class="form-label-custom">Nama Lengkap <span style="color:#dc2626">*</span></label>
<input type="text" name="name"
class="form-control-custom @error('name') is-invalid @enderror"
value="{{ old('name', $user->name) }}" required>
@error('name')<div class="invalid-feedback">{{ $message }}</div>@enderror
@error('name')<div class="invalid-feedback-custom">{{ $message }}</div>@enderror
</div>
<div class="mb-3">
<label class="form-label">Email <span class="text-danger">*</span></label>
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror"
<div class="col-12">
<label class="form-label-custom">Email <span style="color:#dc2626">*</span></label>
<input type="email" name="email"
class="form-control-custom @error('email') is-invalid @enderror"
value="{{ old('email', $user->email) }}" required>
@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror
@error('email')<div class="invalid-feedback-custom">{{ $message }}</div>@enderror
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Password Baru</label>
<div class="input-group">
<div class="section-divider mt-4">Ganti Password</div>
<p style="font-size:0.78rem;color:#9ca3af;margin-bottom:14px;">Kosongkan jika tidak ingin mengganti password.</p>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label-custom">Password Baru</label>
<div class="input-pw-wrap">
<input type="password" name="password" id="password"
class="form-control @error('password') is-invalid @enderror"
class="form-control-custom @error('password') is-invalid @enderror"
placeholder="Minimal 8 karakter">
<button type="button" class="btn btn-outline-secondary" onclick="togglePassword('password', 'eye1')">
<button type="button" class="btn-pw-toggle" onclick="togglePassword('password','eye1')">
<i class="fas fa-eye" id="eye1"></i>
</button>
</div>
@error('password')<div class="text-danger small mt-1">{{ $message }}</div>@enderror
@error('password')<div class="invalid-feedback-custom">{{ $message }}</div>@enderror
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Konfirmasi Password Baru</label>
<div class="input-group">
<div class="col-md-6">
<label class="form-label-custom">Konfirmasi Password Baru</label>
<div class="input-pw-wrap">
<input type="password" name="password_confirmation" id="password_confirmation"
class="form-control"
class="form-control-custom"
placeholder="Ulangi password baru">
<button type="button" class="btn btn-outline-secondary" onclick="togglePassword('password_confirmation', 'eye2')">
<button type="button" class="btn-pw-toggle" onclick="togglePassword('password_confirmation','eye2')">
<i class="fas fa-eye" id="eye2"></i>
</button>
</div>
</div>
</div>
<div class="section-divider"></div>
<h5 class="mb-3 text-success"><i class="fas fa-address-card me-2"></i>Informasi Tambahan</h5>
</div>
</div>
<div class="mb-3">
<label class="form-label">Nomor Telepon</label>
<!-- Informasi Tambahan -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-orange">
<i class="fas fa-address-card"></i>
</div>
<div>
<h3>Informasi Tambahan</h3>
<p>Telepon dan alamat pengguna</p>
</div>
</div>
<div class="info-card-body">
<div class="row g-3">
<div class="col-12">
<label class="form-label-custom">Nomor Telepon</label>
<input type="text" name="phone"
value="{{ old('phone', '+62') }}"
class="form-control-custom @error('phone') is-invalid @enderror"
value="{{ old('phone', $user->phone ?? '+62') }}"
placeholder="+62812xxxxxxxx">
@error('phone')<div class="invalid-feedback">{{ $message }}</div>@enderror
@error('phone')<div class="invalid-feedback-custom">{{ $message }}</div>@enderror
</div>
<div class="col-12">
<label class="form-label-custom">Alamat</label>
<textarea name="address"
class="form-control-custom @error('address') is-invalid @enderror"
rows="4">{{ old('address', $user->address) }}</textarea>
@error('address')<div class="invalid-feedback-custom">{{ $message }}</div>@enderror
</div>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Alamat</label>
<textarea name="address" class="form-control @error('address') is-invalid @enderror"
rows="4" style="min-height: 100px; resize: vertical;">{{ old('address', $user->address) }}</textarea>
@error('address')<div class="invalid-feedback">{{ $message }}</div>@enderror
<!-- Hak Akses -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-purple">
<i class="fas fa-shield-alt"></i>
</div>
<div class="section-divider"></div>
<h5 class="mb-3 text-success"><i class="fas fa-shield-alt me-2"></i>Hak Akses</h5>
<div class="mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="is_admin" id="is_admin"
<div>
<h3>Hak Akses</h3>
<p>Role dan izin pengguna</p>
</div>
</div>
<div class="info-card-body">
<label class="check-card" for="is_admin">
<input class="form-check-input" type="checkbox"
name="is_admin" id="is_admin"
{{ old('is_admin', $user->is_admin) ? 'checked' : '' }}
{{ $user->id === auth()->id() ? 'disabled' : '' }}>
<label class="form-check-label" for="is_admin">
<strong>Jadikan Admin</strong>
<div>
<div class="check-card-label"><i class="fas fa-shield-alt me-1" style="color:var(--green-main)"></i> Jadikan Admin</div>
@if($user->id === auth()->id())
<small class="text-danger d-block">Anda tidak dapat mengubah role akun sendiri.</small>
<div class="check-card-desc text-danger">Anda tidak dapat mengubah role akun sendiri.</div>
@else
<small class="text-muted d-block">Admin memiliki akses penuh ke dashboard.</small>
<div class="check-card-desc">Admin memiliki akses penuh ke seluruh dashboard.</div>
@endif
</div>
</label>
</div>
</div>
<div class="d-flex gap-2">
<button type="submit" class="btn btn-save">
<i class="fas fa-save me-2"></i>Simpan Perubahan
</div>
<!-- Akhir col-lg-8 -->
<!-- ===== KOLOM KANAN (4/12) ===== -->
<div class="col-lg-4">
<!-- User Profile Card -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-green">
<i class="fas fa-id-card"></i>
</div>
<div>
<h3>Profil Pengguna</h3>
<p>Ringkasan akun</p>
</div>
</div>
<div class="info-card-body" style="text-align:center;">
<div style="width:64px;height:64px;border-radius:50%;
background:linear-gradient(135deg,var(--green-main),var(--green-dark));
display:flex;align-items:center;justify-content:center;
font-size:1.5rem;color:white;font-weight:700;
margin:0 auto 12px;">
{{ strtoupper(substr($user->name, 0, 1)) }}
</div>
<div style="font-weight:700;color:#111827;font-size:0.95rem;margin-bottom:4px;">{{ $user->name }}</div>
<div style="font-size:0.78rem;color:#6b7280;margin-bottom:12px;">{{ $user->email }}</div>
<div style="display:inline-flex;align-items:center;gap:6px;
padding:4px 12px;border-radius:20px;font-size:0.75rem;font-weight:700;
{{ $user->is_admin ? 'background:#dbeafe;color:#1e40af;' : 'background:#dcfce7;color:#14532d;' }}">
<i class="fas {{ $user->is_admin ? 'fa-shield-alt' : 'fa-user' }}"></i>
{{ $user->is_admin ? 'Admin' : 'User' }}
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-gray">
<i class="fas fa-cog"></i>
</div>
<div>
<h3>Aksi</h3>
<p>Simpan atau batalkan perubahan</p>
</div>
</div>
<div class="info-card-body" style="display:flex;flex-direction:column;gap:10px;">
<button type="submit" form="editUserForm"
style="width:100%;padding:10px 16px;border-radius:9px;border:none;
background:linear-gradient(135deg,var(--green-main),var(--green-dark));
color:white;font-weight:700;font-size:0.875rem;cursor:pointer;
display:flex;align-items:center;justify-content:center;gap:8px;
transition:all 0.2s;">
<i class="fas fa-save"></i> Simpan Perubahan
</button>
<a href="{{ route('admin.users') }}" class="btn btn-outline-secondary">
<i class="fas fa-times me-2"></i>Batal
<a href="{{ route('admin.users.show', $user) }}"
style="width:100%;padding:10px 16px;border-radius:9px;
border:1.5px solid #bfdbfe;background:#eff6ff;
color:#2563eb;font-weight:600;font-size:0.875rem;
display:flex;align-items:center;justify-content:center;gap:8px;
text-decoration:none;transition:all 0.2s;">
<i class="fas fa-eye"></i> Lihat Detail
</a>
<a href="{{ route('admin.users.show', $user) }}" class="btn btn-outline-info ms-auto">
<i class="fas fa-eye me-2"></i>Lihat Detail
<a href="{{ route('admin.users') }}"
style="width:100%;padding:10px 16px;border-radius:9px;
border:1.5px solid #e5e7eb;background:#f9fafb;
color:#374151;font-weight:600;font-size:0.875rem;
display:flex;align-items:center;justify-content:center;gap:8px;
text-decoration:none;transition:all 0.2s;">
<i class="fas fa-times"></i> Batal
</a>
</div>
</div>
</div>
<!-- Akhir col-lg-4 -->
</div>
</form>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('open');
document.getElementById('sidebarOverlay').classList.toggle('show');
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebarOverlay').classList.remove('show');
}
window.addEventListener('resize', function() {
if (window.innerWidth > 900) closeSidebar();
});
function togglePassword(fieldId, iconId) {
const field = document.getElementById(fieldId);
const icon = document.getElementById(iconId);
@ -231,6 +773,15 @@ function togglePassword(fieldId, iconId) {
icon.classList.replace('fa-eye-slash', 'fa-eye');
}
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.alert').forEach(function(alert) {
setTimeout(function() {
const a = bootstrap.Alert.getOrCreateInstance(alert);
if (a) a.close();
}, 5000);
});
});
</script>
</body>
</html>

View File

@ -4,180 +4,679 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Detail Pengguna - Bibit Cabai Admin</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<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',sans-serif; background:#f8f9fa; }
.admin-container { display:flex; min-height:100vh; }
:root {
--sidebar-width: 260px;
--green-dark: #15803d;
--green-main: #16a34a;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f1f5f2;
overflow-x: hidden;
}
/* ===== SIDEBAR ===== */
.sidebar {
width:280px; background:linear-gradient(180deg,#28a745,#20c997);
color:white; min-height:100vh; position:fixed; top:0; left:0; z-index:1000;
width: var(--sidebar-width);
background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
color: white;
min-height: 100vh;
position: fixed;
top: 0; left: 0;
z-index: 1050;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
.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-menu { padding:20px 0; }
.sidebar-header {
padding: 24px 20px 20px;
text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.sidebar-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-header h3 { font-size: 1.35rem; font-weight: 700; margin-bottom: 2px; }
.sidebar-header .subtitle { opacity: 0.65; font-size: 0.78rem; margin-bottom: 6px; }
.sidebar-header .admin-name {
display: inline-block;
background: rgba(255,255,255,0.15);
padding: 3px 12px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 500;
}
.sidebar-menu { padding: 12px 0; flex: 1; }
.menu-section-label {
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.45;
padding: 12px 20px 6px;
}
.menu-item {
display:block; padding:15px 25px; color:white; text-decoration:none;
transition:all 0.3s; border-left:4px solid transparent;
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: rgba(255,255,255,0.82);
text-decoration: none;
transition: all 0.2s;
border-left: 3px solid transparent;
font-size: 0.9rem;
}
.menu-item:hover, .menu-item.active {
background:rgba(255,255,255,0.2); color:white;
border-left-color:#fff; transform:translateX(5px);
.menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.menu-item:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
border-left-color: rgba(255,255,255,0.5);
}
.menu-item i { width:20px; margin-right:10px; }
.main-content { margin-left:280px; flex:1; }
.admin-header { background:white; padding:20px 30px; box-shadow:0 2px 4px rgba(0,0,0,0.1); }
.content-area { padding:30px; }
.detail-card { background:white; border-radius:12px; padding:35px; box-shadow:0 4px 6px rgba(0,0,0,0.05); max-width:700px; }
.avatar-circle {
width:80px; height:80px; border-radius:50%;
background:linear-gradient(135deg,#28a745,#20c997);
.menu-item.active {
background: rgba(255,255,255,0.15);
color: white;
border-left-color: #fff;
font-weight: 600;
}
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
.btn-logout {
width: 100%;
background: rgba(255,255,255,0.12);
color: white;
border: 1.5px solid rgba(255,255,255,0.3);
padding: 9px 16px;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn-logout:hover {
background: rgba(255,255,255,0.22);
border-color: rgba(255,255,255,0.5);
color: white;
}
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(0,0,0,0.45);
z-index: 1040;
backdrop-filter: blur(2px);
}
.sidebar-overlay.show { display: block; }
/* ===== MAIN CONTENT ===== */
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ===== TOPBAR ===== */
.topbar {
background: white;
height: 64px;
padding: 0 24px;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 0 #e5e7eb;
position: sticky;
top: 0;
z-index: 100;
}
.topbar-left { display: flex; align-items: center; gap: 14px; }
.hamburger-btn {
display: none;
background: none;
border: none;
color: #374151;
font-size: 1.25rem;
padding: 6px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
}
.hamburger-btn:hover { background: #f3f4f6; }
.topbar-breadcrumb { font-size: 0.8rem; color: #6b7280; margin-bottom: 2px; }
.topbar-breadcrumb a { color: var(--green-main); text-decoration: none; }
.topbar-breadcrumb a:hover { text-decoration: underline; }
.topbar-title h2 { font-size: 1.1rem; font-weight: 700; color: #111827; margin: 0; }
.topbar-title p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.topbar-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.btn-topbar {
border: none;
padding: 8px 14px;
border-radius: 8px;
font-weight: 600;
font-size: 0.8rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
white-space: nowrap;
cursor: pointer;
}
.btn-topbar:hover { transform: translateY(-1px); text-decoration: none; }
.btn-topbar-back {
background: linear-gradient(135deg, #dc2626, #b91c1c);
color: white;
}
.btn-topbar-back:hover { box-shadow: 0 4px 12px rgba(220,38,38,0.35); color: white; }
.btn-topbar-edit {
background: linear-gradient(135deg, #ca8a04, #a16207);
color: white;
}
.btn-topbar-edit:hover { box-shadow: 0 4px 12px rgba(202,138,4,0.35); color: white; }
.btn-topbar-delete {
background: #fef2f2;
color: #dc2626;
border: 1.5px solid #fecaca;
}
.btn-topbar-delete:hover { background: #fee2e2; color: #dc2626; }
/* ===== CONTENT AREA ===== */
.content-area { padding: 20px 24px 32px; flex: 1; }
/* ===== INFO CARD ===== */
.info-card {
background: white;
border-radius: 14px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 10px rgba(0,0,0,0.04);
overflow: hidden;
margin-bottom: 16px;
}
.info-card-header {
padding: 16px 20px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
gap: 10px;
}
.info-card-icon {
width: 34px; height: 34px;
border-radius: 9px;
display: flex; align-items: center; justify-content: center;
font-size:2rem; color:white; font-weight:bold; margin-bottom:15px;
font-size: 0.9rem;
flex-shrink: 0;
}
.info-row { display:flex; padding:14px 0; border-bottom:1px solid #f8f9fa; }
.info-row:last-child { border-bottom:none; }
.info-label { width:160px; font-weight:600; color:#6c757d; font-size:0.9rem; flex-shrink:0; }
.info-value { color:#333; flex:1; }
.badge-role { padding:5px 12px; border-radius:12px; font-size:0.8rem; font-weight:600; }
.badge-admin { background:#cce5ff; color:#004085; }
.badge-user { background:#d4edda; color:#155724; }
.badge-verified { background:#d4edda; color:#155724; }
.badge-unverified { background:#e2e3e5; color:#383d41; }
.breadcrumb { background:transparent; padding:0; margin-bottom:10px; }
.breadcrumb-item a { color:#28a745; text-decoration:none; }
.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; text-decoration:none; transition:all 0.3s;
.icon-blue { background: #eff6ff; color: #2563eb; }
.icon-green { background: #f0fdf4; color: var(--green-main); }
.icon-orange { background: #fff7ed; color: #ea580c; }
.icon-purple { background: #faf5ff; color: #7c3aed; }
.icon-yellow { background: #fefce8; color: #ca8a04; }
.icon-gray { background: #f3f4f6; color: #6b7280; }
.info-card-header h3 { font-size: 0.9rem; font-weight: 700; color: #111827; margin: 0; }
.info-card-header p { font-size: 0.75rem; color: #6b7280; margin: 0; }
.info-card-body { padding: 20px; }
/* ===== DETAIL ROW ===== */
.detail-row {
display: flex;
padding: 8px 0;
border-bottom: 1px solid #f9fafb;
font-size: 0.84rem;
gap: 12px;
}
.detail-row:last-child { border-bottom: none; }
.detail-label {
width: 140px;
flex-shrink: 0;
color: #6b7280;
font-weight: 500;
}
.detail-value { color: #111827; flex: 1; }
/* ===== BADGES ===== */
.badge-status {
padding: 4px 10px;
border-radius: 20px;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.02em;
}
.badge-admin { background: #dbeafe; color: #1e40af; }
.badge-user { background: #dcfce7; color: #14532d; }
.badge-verified { background: #dcfce7; color: #14532d; }
.badge-unverified { background: #f3f4f6; color: #6b7280; }
/* ===== AVATAR ===== */
.avatar-lg {
width: 72px; height: 72px;
border-radius: 50%;
background: linear-gradient(135deg, var(--green-main), var(--green-dark));
display: flex; align-items: center; justify-content: center;
font-size: 1.75rem; color: white; font-weight: 700;
flex-shrink: 0;
}
/* ===== FADE IN ===== */
.fade-in { animation: fadeIn 0.3s ease; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
/* ===== RESPONSIVE ===== */
@media (max-width: 900px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); box-shadow: 4px 0 24px rgba(0,0,0,0.2); }
.main-content { margin-left: 0; }
.hamburger-btn { display: flex; align-items: center; justify-content: center; }
}
@media (max-width: 767px) {
.content-area { padding: 14px 14px 24px; }
.topbar { padding: 0 14px; height: auto; min-height: 58px; flex-wrap: wrap; gap: 8px; padding-top: 10px; padding-bottom: 10px; }
.topbar-actions .btn-topbar span { display: none; }
.topbar-actions .btn-topbar { padding: 8px 10px; }
.detail-label { width: 110px; }
}
.back-button:hover { transform:translateY(-2px); color:white; }
</style>
</head>
<body>
<a href="{{ route('home') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<div class="admin-container">
<nav class="sidebar">
<!-- ===== SIDEBAR ===== -->
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3>
<p>Admin Dashboard</p>
<small class="text-light">{{ Auth::user()->name ?? 'Admin' }}</small>
<span class="brand-icon">🌱</span>
<h3>Bibit Cabai</h3>
<p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</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"><i class="fas fa-shopping-cart"></i>Pesanan</a>
<a href="{{ route('admin.users') }}" class="menu-item active"><i class="fas fa-users"></i>Pengguna</a>
<a href="{{ route('admin.reports') }}" 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">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item">
<i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
</a>
<a href="{{ route('admin.products.index') }}" class="menu-item">
<i class="fas fa-seedling"></i><span>Kelola Produk</span>
</a>
<a href="{{ route('admin.orders') }}" class="menu-item">
<i class="fas fa-shopping-cart"></i><span>Pesanan</span>
</a>
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</a>
<a href="{{ route('admin.users') }}" class="menu-item active">
<i class="fas fa-users"></i><span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item">
<i class="fas fa-chart-line"></i><span>Laporan</span>
</a>
</div>
<div class="sidebar-footer">
<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 type="submit" class="btn-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</button>
</form>
</div>
</div>
</nav>
<!-- ===== MAIN ===== -->
<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"><a href="{{ route('admin.users') }}">Pengguna</a></li>
<li class="breadcrumb-item active">Detail Pengguna</li>
</ol>
</nav>
<!-- Topbar -->
<header class="topbar">
<div class="topbar-left">
<button class="hamburger-btn" onclick="toggleSidebar()">
<i class="fas fa-bars"></i>
</button>
<div class="topbar-title">
<div class="topbar-breadcrumb">
<a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
&nbsp;/&nbsp;<a href="{{ route('admin.users') }}">Pengguna</a>
&nbsp;/&nbsp;{{ $user->name }}
</div>
<h2>Detail Pengguna</h2>
<p class="mb-0">Informasi lengkap pengguna.</p>
</div>
<div class="content-area">
<div class="detail-card">
{{-- Avatar & Nama --}}
<div class="text-center mb-4">
<div class="avatar-circle mx-auto">
{{ strtoupper(substr($user->name, 0, 1)) }}
</div>
<h4 class="mb-1">{{ $user->name }}</h4>
@if($user->is_admin)
<span class="badge-role badge-admin"><i class="fas fa-shield-alt me-1"></i>Admin</span>
@else
<span class="badge-role badge-user"><i class="fas fa-user me-1"></i>User</span>
@endif
</div>
{{-- Info Detail --}}
<div class="info-row">
<div class="info-label"><i class="fas fa-hashtag me-2 text-muted"></i>ID</div>
<div class="info-value">#{{ $user->id }}</div>
</div>
<div class="info-row">
<div class="info-label"><i class="fas fa-envelope me-2 text-muted"></i>Email</div>
<div class="info-value">{{ $user->email }}</div>
</div>
<div class="info-row">
<div class="info-label"><i class="fas fa-check-circle me-2 text-muted"></i>Verifikasi</div>
<div class="info-value">
@if($user->email_verified_at)
<span class="badge-role badge-verified">
<i class="fas fa-check me-1"></i>Terverifikasi
</span>
<small class="text-muted ms-2">{{ $user->email_verified_at->format('d M Y') }}</small>
@else
<span class="badge-role badge-unverified">Belum Verifikasi</span>
@endif
<p>{{ $user->name }}</p>
</div>
</div>
<div class="info-row">
<div class="info-label"><i class="fas fa-phone me-2 text-muted"></i>Telepon</div>
<div class="info-value">{{ $user->phone ?? '-' }}</div>
</div>
<div class="info-row">
<div class="info-label"><i class="fas fa-map-marker-alt me-2 text-muted"></i>Alamat</div>
<div class="info-value">{{ $user->address ?? '-' }}</div>
</div>
<div class="info-row">
<div class="info-label"><i class="fas fa-calendar me-2 text-muted"></i>Bergabung</div>
<div class="info-value">{{ $user->created_at->format('d M Y, H:i') }}</div>
</div>
<div class="info-row">
<div class="info-label"><i class="fas fa-sync me-2 text-muted"></i>Diperbarui</div>
<div class="info-value">{{ $user->updated_at->format('d M Y, H:i') }}</div>
</div>
{{-- Action Buttons --}}
<div class="d-flex gap-2 mt-4">
<a href="{{ route('admin.users.edit', $user) }}" class="btn btn-warning">
<i class="fas fa-edit me-2"></i>Edit Pengguna
<div class="topbar-actions">
<a href="{{ route('admin.users') }}" class="btn-topbar btn-topbar-back">
<i class="fas fa-arrow-left"></i>
<span>Kembali</span>
</a>
<a href="{{ route('admin.users') }}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Kembali
<a href="{{ route('admin.users.edit', $user) }}" class="btn-topbar btn-topbar-edit">
<i class="fas fa-edit"></i>
<span>Edit</span>
</a>
@if($user->id !== auth()->id())
<form method="POST" action="{{ route('admin.users.destroy', $user) }}"
class="ms-auto" onsubmit="return confirm('Yakin ingin menghapus pengguna ini?')">
@csrf @method('DELETE')
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Hapus
<button type="button" class="btn-topbar btn-topbar-delete"
data-bs-toggle="modal" data-bs-target="#deleteModal">
<i class="fas fa-trash"></i>
<span>Hapus</span>
</button>
</form>
@endif
</div>
</header>
<!-- Content -->
<div class="content-area fade-in">
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show mb-4" 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
<div class="row g-3">
<!-- ===== KOLOM KIRI (8/12) ===== -->
<div class="col-lg-8">
<!-- Profil & Info Dasar -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-blue">
<i class="fas fa-user"></i>
</div>
<div style="flex:1;">
<h3>Informasi Pengguna</h3>
<p>Detail akun & status</p>
</div>
<span class="badge-status {{ $user->is_admin ? 'badge-admin' : 'badge-user' }}">
<i class="fas {{ $user->is_admin ? 'fa-shield-alt' : 'fa-user' }} me-1"></i>
{{ $user->is_admin ? 'Admin' : 'User' }}
</span>
</div>
<div class="info-card-body">
<!-- Avatar + nama -->
<div style="display:flex;align-items:center;gap:16px;margin-bottom:20px;
padding-bottom:16px;border-bottom:1px solid #f3f4f6;">
<div class="avatar-lg">{{ strtoupper(substr($user->name, 0, 1)) }}</div>
<div>
<div style="font-size:1.05rem;font-weight:700;color:#111827;">{{ $user->name }}</div>
<div style="font-size:0.82rem;color:#6b7280;margin-top:2px;">{{ $user->email }}</div>
<div style="margin-top:8px;">
@if($user->email_verified_at)
<span class="badge-status badge-verified">
<i class="fas fa-check me-1"></i>Email Terverifikasi
</span>
@else
<span class="badge-status badge-unverified">
<i class="fas fa-clock me-1"></i>Belum Verifikasi
</span>
@endif
</div>
</div>
</div>
<div class="row g-4">
<div class="col-md-6">
<div style="font-size:0.72rem;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#9ca3af;padding-bottom:10px;border-bottom:1px solid #f3f4f6;margin-bottom:16px;">Akun</div>
<div class="detail-row">
<span class="detail-label"><i class="fas fa-hashtag me-1" style="font-size:0.7rem;"></i> ID</span>
<span class="detail-value"><strong>#{{ $user->id }}</strong></span>
</div>
<div class="detail-row">
<span class="detail-label"><i class="fas fa-envelope me-1" style="font-size:0.7rem;"></i> Email</span>
<span class="detail-value">{{ $user->email }}</span>
</div>
<div class="detail-row">
<span class="detail-label"><i class="fas fa-check-circle me-1" style="font-size:0.7rem;"></i> Verifikasi</span>
<span class="detail-value">
@if($user->email_verified_at)
<span style="color:#16a34a;font-size:0.8rem;">{{ $user->email_verified_at->format('d M Y') }}</span>
@else
<span style="color:#9ca3af;font-size:0.8rem;">Belum verifikasi</span>
@endif
</span>
</div>
<div class="detail-row">
<span class="detail-label"><i class="fas fa-shield-alt me-1" style="font-size:0.7rem;"></i> Role</span>
<span class="detail-value">
<span class="badge-status {{ $user->is_admin ? 'badge-admin' : 'badge-user' }}">
{{ $user->is_admin ? 'Admin' : 'User' }}
</span>
</span>
</div>
</div>
<div class="col-md-6">
<div style="font-size:0.72rem;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#9ca3af;padding-bottom:10px;border-bottom:1px solid #f3f4f6;margin-bottom:16px;">Waktu</div>
<div class="detail-row">
<span class="detail-label"><i class="fas fa-calendar me-1" style="font-size:0.7rem;"></i> Bergabung</span>
<span class="detail-value">{{ $user->created_at->format('d M Y, H:i') }}</span>
</div>
<div class="detail-row">
<span class="detail-label"><i class="fas fa-sync me-1" style="font-size:0.7rem;"></i> Diperbarui</span>
<span class="detail-value">{{ $user->updated_at->format('d M Y, H:i') }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- Informasi Kontak -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-orange">
<i class="fas fa-address-card"></i>
</div>
<div>
<h3>Kontak & Alamat</h3>
<p>Telepon dan alamat pengguna</p>
</div>
</div>
<div class="info-card-body">
<div class="detail-row">
<span class="detail-label"><i class="fas fa-phone me-1" style="font-size:0.7rem;color:var(--green-main)"></i> Telepon</span>
<span class="detail-value">{{ $user->phone ?? '-' }}</span>
</div>
<div class="detail-row">
<span class="detail-label"><i class="fas fa-map-marker-alt me-1" style="font-size:0.7rem;color:var(--green-main)"></i> Alamat</span>
<span class="detail-value" style="line-height:1.6;">{{ $user->address ?? '-' }}</span>
</div>
</div>
</div>
</div>
<!-- Akhir col-lg-8 -->
<!-- ===== KOLOM KANAN (4/12) ===== -->
<div class="col-lg-4">
<!-- Quick Actions -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-yellow">
<i class="fas fa-bolt"></i>
</div>
<div>
<h3>Aksi Cepat</h3>
<p>Kelola pengguna ini</p>
</div>
</div>
<div class="info-card-body" style="display:flex;flex-direction:column;gap:10px;">
<a href="{{ route('admin.users.edit', $user) }}"
style="width:100%;padding:10px 16px;border-radius:9px;border:none;
background:linear-gradient(135deg,#ca8a04,#a16207);
color:white;font-weight:700;font-size:0.875rem;
display:flex;align-items:center;justify-content:center;gap:8px;
text-decoration:none;transition:all 0.2s;">
<i class="fas fa-edit"></i> Edit Pengguna
</a>
<a href="{{ route('admin.users') }}"
style="width:100%;padding:10px 16px;border-radius:9px;
border:1.5px solid #e5e7eb;background:#f9fafb;
color:#374151;font-weight:600;font-size:0.875rem;
display:flex;align-items:center;justify-content:center;gap:8px;
text-decoration:none;transition:all 0.2s;">
<i class="fas fa-arrow-left"></i> Kembali ke Daftar
</a>
@if($user->id !== auth()->id())
<button type="button"
data-bs-toggle="modal" data-bs-target="#deleteModal"
style="width:100%;padding:10px 16px;border-radius:9px;
border:1.5px solid #fecaca;background:#fef2f2;
color:#dc2626;font-weight:600;font-size:0.875rem;
display:flex;align-items:center;justify-content:center;gap:8px;
cursor:pointer;transition:all 0.2s;">
<i class="fas fa-trash"></i> Hapus Pengguna
</button>
@endif
</div>
</div>
<!-- Status Summary -->
<div class="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-purple">
<i class="fas fa-info-circle"></i>
</div>
<div>
<h3>Ringkasan Status</h3>
<p>Status akun pengguna</p>
</div>
</div>
<div class="info-card-body">
<div class="detail-row">
<span class="detail-label">Role</span>
<span class="detail-value">
<span class="badge-status {{ $user->is_admin ? 'badge-admin' : 'badge-user' }}">
{{ $user->is_admin ? 'Admin' : 'User' }}
</span>
</span>
</div>
<div class="detail-row">
<span class="detail-label">Email</span>
<span class="detail-value">
@if($user->email_verified_at)
<span class="badge-status badge-verified">Terverifikasi</span>
@else
<span class="badge-status badge-unverified">Belum Verifikasi</span>
@endif
</span>
</div>
<div class="detail-row">
<span class="detail-label">ID Pengguna</span>
<span class="detail-value"><code style="background:#f3f4f6;padding:2px 6px;border-radius:4px;font-size:0.8rem;">#{{ $user->id }}</code></span>
</div>
</div>
</div>
</div>
<!-- Akhir col-lg-4 -->
</div>
</div>
</div>
<!-- ===== DELETE MODAL ===== -->
@if($user->id !== auth()->id())
<div class="modal fade" id="deleteModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" style="border:none;border-radius:14px;overflow:hidden;">
<div class="modal-header" style="background:#dc2626;color:white;border:none;">
<h5 class="modal-title">
<i class="fas fa-exclamation-triangle me-2"></i>Konfirmasi Hapus
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" style="padding:24px;">
<p style="color:#374151;margin-bottom:8px;">Apakah Anda yakin ingin menghapus pengguna ini?</p>
<p style="font-weight:700;color:#111827;margin-bottom:12px;">{{ $user->name }} ({{ $user->email }})</p>
<p style="color:#dc2626;font-size:0.84rem;margin:0;">
<i class="fas fa-exclamation-triangle me-1"></i> Tindakan ini tidak dapat dibatalkan!
</p>
</div>
<div class="modal-footer" style="border:none;padding:16px 24px;">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<form action="{{ route('admin.users.destroy', $user) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-1"></i> Hapus Pengguna
</button>
</form>
</div>
</div>
</div>
</div>
@endif
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('open');
document.getElementById('sidebarOverlay').classList.toggle('show');
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('sidebarOverlay').classList.remove('show');
}
window.addEventListener('resize', function() {
if (window.innerWidth > 900) closeSidebar();
});
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.alert').forEach(function(alert) {
setTimeout(function() {
const a = bootstrap.Alert.getOrCreateInstance(alert);
if (a) a.close();
}, 5000);
});
});
</script>
</body>
</html>

View File

@ -40,7 +40,6 @@
border-color: #11998e;
box-shadow: 0 0 0 0.2rem rgba(17, 153, 142, 0.25);
}
.step-wrapper {
display: flex;
align-items: center;
@ -64,8 +63,6 @@
.step-line { height: 2px; width: 36px; margin: 0 6px 18px; }
.step-line.done { background: #11998e; }
.step-line.idle { background: #dee2e6; }
/* OTP Boxes */
.otp-box {
width: 46px !important;
height: 54px !important;
@ -86,7 +83,13 @@
border-color: #11998e !important;
background-color: #f0fff4 !important;
}
/* Kotak OTP saat expired */
.otp-box:disabled {
background-color: #f8f9fa !important;
border-color: #dee2e6 !important;
color: #adb5bd !important;
cursor: not-allowed !important;
}
.email-badge {
background: #f0fff4;
border: 1px solid #11998e33;
@ -94,7 +97,6 @@
border-radius: 8px;
padding: 10px 14px;
}
.icon-box {
width: 64px; height: 64px;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
@ -160,6 +162,9 @@
</div>
</div>
{{-- =============================================
FORM OTP disabled seluruhnya saat expired
============================================= --}}
<form method="POST" action="{{ route('admin.password.verify-otp') }}" id="otpForm" novalidate>
@csrf
@ -170,12 +175,18 @@
{{-- OTP Boxes --}}
<div class="d-flex justify-content-center gap-2 mb-2" id="otpBoxes">
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off">
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off">
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off">
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off">
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off">
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off">
{{-- disabled langsung dari PHP jika backend bilang expired --}}
@php $isExpiredFromServer = session('otp_expired', false); @endphp
@for ($i = 0; $i < 6; $i++)
<input type="text"
class="otp-box form-control"
maxlength="1"
inputmode="numeric"
pattern="[0-9]"
autocomplete="off"
{{ $isExpiredFromServer ? 'disabled' : '' }}>
@endfor
</div>
<input type="hidden" name="otp" id="otpHidden">
@ -186,32 +197,44 @@
{{-- Countdown Timer --}}
<div class="text-center mb-4">
<div id="timerWrapper">
<div id="timerWrapper" {{ $isExpiredFromServer ? 'style=display:none;' : '' }}>
<p class="text-muted small mb-1">Kode berlaku selama:</p>
<span id="countdown" class="badge fs-6 px-3 py-2" style="background:#11998e;">10:00</span>
<span id="countdown" class="badge fs-6 px-3 py-2" style="background:#11998e;">03:00</span>
</div>
<div id="expiredWrapper" style="display:none;">
<div id="expiredWrapper" {{ $isExpiredFromServer ? '' : 'style=display:none;' }}>
<span class="badge bg-danger fs-6 px-3 py-2">
Kode sudah kadaluarsa
</span>
</div>
</div>
{{-- Tombol Verifikasi --}}
{{-- disabled dari server jika expired, dari JS jika OTP belum 6 digit --}}
<div class="d-grid mb-3">
<button type="submit" class="btn btn-primary-custom py-2 fw-bold" id="submitBtn" disabled>
<button type="submit"
class="btn btn-primary-custom py-2 fw-bold"
id="submitBtn"
{{ $isExpiredFromServer ? 'disabled' : 'disabled' }}>
@if($isExpiredFromServer)
Waktu Habis
@else
Verifikasi Kode
@endif
</button>
</div>
</form>
<hr class="my-3">
{{-- Resend --}}
{{-- Resend hanya aktif saat expired --}}
<div class="text-center">
<p class="text-muted small mb-2">Tidak menerima kode?</p>
<form method="POST" action="{{ route('admin.password.resend-otp') }}">
<form method="POST" action="{{ route('admin.password.resend-otp') }}" id="resendForm">
@csrf
<button type="submit" class="btn btn-outline-secondary btn-sm" id="resendBtn" disabled>
<button type="submit"
class="btn btn-outline-secondary btn-sm"
id="resendBtn"
{{ $isExpiredFromServer ? '' : 'disabled' }}>
🔄 Kirim Ulang Kode
</button>
</form>
@ -231,15 +254,29 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
// ── Variabel elemen ──────────────────────────────────────
const boxes = document.querySelectorAll('.otp-box');
const otpHidden = document.getElementById('otpHidden');
const submitBtn = document.getElementById('submitBtn');
const resendBtn = document.getElementById('resendBtn');
const resendForm = document.getElementById('resendForm');
const countdown = document.getElementById('countdown');
const timerWrap = document.getElementById('timerWrapper');
const expiredWrap = document.getElementById('expiredWrapper');
// ---- OTP input logic ----
// ── Flag dari backend (PHP → JS) ─────────────────────────
// true = backend sudah konfirmasi OTP expired (record dihapus)
// false = backend belum, timer frontend yang jalan
const isExpiredFromServer = {{ $isExpiredFromServer ? 'true' : 'false' }};
// ── Jika sudah expired dari server → langsung lock ───────
if (isExpiredFromServer) {
lockExpired();
return; // stop, tidak perlu jalankan timer
}
// ── OTP input logic ──────────────────────────────────────
boxes.forEach((box, i) => {
box.addEventListener('input', function () {
this.value = this.value.replace(/[^0-9]/g, '');
@ -263,9 +300,13 @@
box.addEventListener('paste', function (e) {
e.preventDefault();
const pasted = (e.clipboardData || window.clipboardData).getData('text').replace(/\D/g, '');
const pasted = (e.clipboardData || window.clipboardData)
.getData('text').replace(/\D/g, '');
pasted.split('').slice(0, 6).forEach((ch, idx) => {
if (boxes[idx]) { boxes[idx].value = ch; boxes[idx].classList.add('filled'); }
if (boxes[idx]) {
boxes[idx].value = ch;
boxes[idx].classList.add('filled');
}
});
updateHidden();
boxes[Math.min(pasted.length, 5)].focus();
@ -275,29 +316,91 @@
function updateHidden() {
const otp = Array.from(boxes).map(b => b.value).join('');
otpHidden.value = otp;
submitBtn.disabled = otp.length !== 6;
// Tombol verify aktif hanya jika 6 digit DAN belum expired
submitBtn.disabled = (otp.length !== 6);
}
// ---- Countdown 10 min ----
let secs = 10 * 60;
const timer = setInterval(() => {
secs--;
const m = String(Math.floor(secs / 60)).padStart(2, '0');
const s = String(secs % 60).padStart(2, '0');
countdown.textContent = `${m}:${s}`;
// ── Countdown — pakai sessionStorage agar refresh tidak reset ──
// Key unik per email agar tidak bentrok jika beda sesi
const STORAGE_KEY = 'otp_timer_{{ md5(session("admin_reset_email", "x")) }}';
const PENALTY_KEY = 'otp_penalty_{{ md5(session("admin_reset_email", "x")) }}';
const DURATION = 3 * 60; // 180 detik (3 menit)
const WRONG_PENALTY = 30; // pengurangan waktu saat salah OTP (detik)
if (secs <= 60) { countdown.style.background = '#ffc107'; countdown.style.color = '#000'; }
if (secs <= 0) {
clearInterval(timer);
timerWrap.style.display = 'none';
expiredWrap.style.display = 'block';
let startTime = parseInt(sessionStorage.getItem(STORAGE_KEY) || '0');
if (!startTime || isNaN(startTime)) {
startTime = Date.now();
sessionStorage.setItem(STORAGE_KEY, startTime);
}
// ── Terapkan penalty jika backend bilang OTP salah ────────
// Backend set session 'otp_wrong' = true saat Hash::check gagal
const wrongFromServer = {{ session('otp_wrong') ? 'true' : 'false' }};
if (wrongFromServer) {
// Tambah penalty ke startTime (maju ke masa lalu = sisa waktu berkurang)
startTime -= (WRONG_PENALTY * 1000);
sessionStorage.setItem(STORAGE_KEY, startTime);
sessionStorage.removeItem(PENALTY_KEY);
}
const timerInterval = setInterval(() => {
const elapsed = Math.floor((Date.now() - startTime) / 1000);
const remaining = DURATION - elapsed;
if (remaining <= 0) {
clearInterval(timerInterval);
sessionStorage.removeItem(STORAGE_KEY);
lockExpired();
return;
}
// Warna kuning saat sisa ≤ 30 detik
if (remaining <= 30) {
countdown.style.background = '#dc3545';
countdown.style.color = '#fff';
} else if (remaining <= 60) {
countdown.style.background = '#ffc107';
countdown.style.color = '#000';
}
const m = String(Math.floor(remaining / 60)).padStart(2, '0');
const s = String(remaining % 60).padStart(2, '0');
countdown.textContent = `${m}:${s}`;
}, 500);
// ── Fungsi lock saat expired ─────────────────────────────
function lockExpired() {
// Sembunyikan timer, tampilkan badge expired
if (timerWrap) timerWrap.style.display = 'none';
if (expiredWrap) expiredWrap.style.display = 'block';
// Disable & ubah teks tombol verifikasi
submitBtn.disabled = true;
submitBtn.textContent = '⛔ Waktu Habis';
submitBtn.style.background = '#adb5bd';
// Blokir submit form meski tombol diklik paksa (inject JS / inspect)
document.getElementById('otpForm').addEventListener('submit', function (e) {
e.preventDefault();
return false;
});
// Disable semua kotak OTP
boxes.forEach(b => {
b.disabled = true;
b.value = '';
b.classList.remove('filled');
});
// Aktifkan tombol kirim ulang
resendBtn.disabled = false;
}
}, 1000);
// Enable resend after 60s
setTimeout(() => { resendBtn.disabled = false; }, 60000);
// ── Bersihkan sessionStorage saat resend dikirim ─────────
resendForm.addEventListener('submit', function () {
sessionStorage.removeItem(STORAGE_KEY);
});
});
</script>
</body>

View File

@ -4,26 +4,73 @@
@section('styles')
<style>
/* ── Hero Section ───────────────────────────── */
.hero-image {
max-height: 400px;
object-fit: cover;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
width: 100%;
}
/* Mobile: gambar naik ke atas, teks di bawah */
@media (max-width: 767.98px) {
.hero-section {
text-align: center;
padding-top: 30px !important;
padding-bottom: 30px !important;
}
.hero-img-col {
order: -1;
margin-bottom: 20px;
}
.hero-image {
max-height: 200px;
border-radius: 10px;
}
.hero-section h1 {
font-size: 1.8rem !important;
}
.hero-section p.lead {
font-size: 0.95rem;
}
.hero-section .btn {
width: 100%;
max-width: 300px;
}
}
/* ── Product Card ───────────────────────────── */
.product-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 25px rgba(0,0,0,0.15);
}
.product-image {
height: 250px;
object-fit: cover;
transition: transform 0.3s ease;
width: 100%;
}
.product-card:hover .product-image {
transform: scale(1.05);
}
@media (max-width: 575.98px) {
.product-image {
height: 150px;
}
.card-body {
padding: 10px;
}
.card-title {
font-size: 0.85rem;
margin-bottom: 4px !important;
}
}
.badge-bestseller {
position: absolute;
@ -44,12 +91,22 @@
font-weight: bold;
color: #28a745;
}
.hero-image {
max-height: 400px;
object-fit: cover;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
@media (max-width: 575.98px) {
.price-text {
font-size: 0.95rem;
margin-bottom: 6px !important;
}
/* Sembunyikan teks "Terjual" di mobile agar tidak penuh */
.sold-label { display: none; }
/* Quantity input lebih kecil */
.quantity-input { width: 40px !important; font-size: 0.85rem; }
.input-group .btn { padding: 4px 8px; font-size: 0.8rem; }
.btn { font-size: 0.8rem; }
/* Section title */
.display-5 { font-size: 1.4rem !important; }
/* Feature icon lebih kecil */
.feature-icon-circle { width: 56px !important; height: 56px !important; }
.feature-icon-circle .fa-2x { font-size: 1.2rem !important; }
}
.quantity-input {
@ -62,17 +119,17 @@
@section('content')
<div class="container-fluid p-0">
<!-- Hero Section -->
<section class="bg-success text-white py-5">
<section class="bg-success text-white py-5 hero-section">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-6">
<div class="col-lg-6 col-md-6 col-12">
<h1 class="display-4 fw-bold mb-4">Bibit Cabai Berkualitas</h1>
<p class="lead mb-4">Dapatkan bibit cabai terbaik dari Bondowoso untuk hasil panen yang optimal.</p>
<a href="{{ route('products.best-selling') }}" class="btn btn-light btn-lg">
<i class="fas fa-fire me-2"></i>Lihat Produk Terlaris
</a>
</div>
<div class="col-lg-6 text-center">
<div class="col-lg-6 col-md-6 col-12 text-center hero-img-col">
<img src="{{ asset('images/bara f1.jpeg') }}"
alt="Bibit Cabai"
class="img-fluid hero-image"
@ -93,7 +150,7 @@ class="img-fluid hero-image"
@if($featuredProducts->count() > 0)
<div class="row g-4">
@foreach($featuredProducts as $product)
<div class="col-lg-4 col-md-6">
<div class="col-lg-4 col-md-6 col-6">
<div class="card product-card h-100 position-relative">
@if($product->total_sold > 0)
<div class="badge-bestseller">
@ -114,7 +171,7 @@ class="card-img-top product-image"
<div class="price-text mb-3">Rp {{ number_format($product->price, 0, ',', '.') }}</div>
@if($product->total_sold > 0)
<small class="text-muted mb-3">
<small class="text-muted mb-3 sold-label">
<i class="fas fa-chart-line me-1"></i>
Terjual: {{ number_format($product->total_sold) }} bibit
</small>
@ -198,6 +255,7 @@ class="card-img-top product-image"
@endif
</div>
</section>
<!-- Semua Produk Section -->
<section class="py-5 bg-white">
<div class="container">
@ -208,7 +266,7 @@ class="card-img-top product-image"
<div class="row g-4">
@foreach($allProducts as $product)
<div class="col-lg-4 col-md-6">
<div class="col-lg-4 col-md-6 col-6">
<div class="card product-card h-100 position-relative">
<div class="overflow-hidden">
<img src="{{ $product->image ? asset('storage/' . $product->image) : 'https://via.placeholder.com/300x250/28a745/ffffff?text=' . urlencode($product->name) }}"
@ -268,41 +326,31 @@ class="card-img-top product-image"
</div>
</section>
<!-- Features Section -->
<!-- Features Section -->
<section class="py-5 bg-white">
<div class="container">
<div class="row g-4">
<div class="col-lg-3 col-md-6">
<div class="col-lg-3 col-md-6 col-6">
<div class="text-center">
<div class="bg-success text-white rounded-circle mx-auto mb-3 d-flex align-items-center justify-content-center" style="width: 80px; height: 80px;">
<div class="bg-success text-white rounded-circle mx-auto mb-3 d-flex align-items-center justify-content-center feature-icon-circle" style="width: 80px; height: 80px;">
<i class="fas fa-seedling fa-2x"></i>
</div>
<h5>Bibit Berkualitas</h5>
<p class="text-muted">Bibit pilihan dengan kualitas terjamin dan hasil optimal.</p>
</div>
</div>
<div class="col-lg-3 col-md-6">
<div class="col-lg-3 col-md-6 col-6">
<div class="text-center">
<div class="bg-success text-white rounded-circle mx-auto mb-3 d-flex align-items-center justify-content-center" style="width: 80px; height: 80px;">
<div class="bg-success text-white rounded-circle mx-auto mb-3 d-flex align-items-center justify-content-center feature-icon-circle" style="width: 80px; height: 80px;">
<i class="fas fa-truck fa-2x"></i>
</div>
<h5>Pengiriman Cepat</h5>
<p class="text-muted">Pengiriman ke seluruh kabupaten Bondowoso dengan packaging aman.</p>
</div>
</div>
<!-- <div class="col-lg-3 col-md-6">
<div class="col-lg-3 col-md-6 col-6">
<div class="text-center">
<div class="bg-success text-white rounded-circle mx-auto mb-3 d-flex align-items-center justify-content-center" style="width: 80px; height: 80px;">
<i class="fas fa-medal fa-2x"></i>
</div>
<h5>Garansi Tumbuh</h5>
<p class="text-muted">Garansi bibit tumbuh atau uang kembali 100%.</p>
</div>
</div> -->
<div class="col-lg-3 col-md-6">
<div class="text-center">
<div class="bg-success text-white rounded-circle mx-auto mb-3 d-flex align-items-center justify-content-center" style="width: 80px; height: 80px;">
<div class="bg-success text-white rounded-circle mx-auto mb-3 d-flex align-items-center justify-content-center feature-icon-circle" style="width: 80px; height: 80px;">
<i class="fas fa-headset fa-2x"></i>
</div>
<h5>Support 24/7</h5>