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,48 +82,52 @@ public function showVerifyOtpForm(Request $request)
// ───────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────
// STEP 2 — Proses: verifikasi OTP // STEP 2 — Proses: verifikasi OTP
// ───────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────
public function verifyOtp(Request $request) // STEP 2 — Proses: verifikasi OTP
{ public function verifyOtp(Request $request)
$request->validate([ {
'otp' => ['required', 'digits:6'], $request->validate([
], [ 'otp' => ['required', 'digits:6'],
'otp.required' => 'Kode OTP wajib diisi.', ], [
'otp.digits' => 'Kode OTP harus 6 digit angka.', 'otp.required' => 'Kode OTP wajib diisi.',
]); 'otp.digits' => 'Kode OTP harus 6 digit angka.',
]);
$email = $request->session()->get('admin_reset_email'); $email = $request->session()->get('admin_reset_email');
if (!$email) { if (!$email) {
return redirect()->route('admin.password.request') return redirect()->route('admin.password.request')
->with('error', 'Sesi tidak valid. Silakan ulangi dari awal.'); ->with('error', 'Sesi tidak valid. Silakan ulangi dari awal.');
}
$record = DB::table('password_reset_tokens')->where('email', $email)->first();
if (!$record) {
return back()->with('error', 'Kode OTP tidak ditemukan. Silakan kirim ulang.');
}
if (Carbon::now()->diffInMinutes(Carbon::parse($record->created_at)) > 10) {
DB::table('password_reset_tokens')->where('email', $email)->delete();
return back()->with('error', 'Kode OTP sudah kadaluarsa. Silakan kirim ulang kode.');
}
if (!Hash::check($request->otp, $record->token)) {
return back()->with('error', 'Kode OTP salah. Periksa kembali kode yang Anda masukkan.');
}
$verifiedToken = Str::random(60);
DB::table('password_reset_tokens')
->where('email', $email)
->update(['token' => Hash::make($verifiedToken)]);
$request->session()->put('admin_reset_token', $verifiedToken);
return redirect()->route('admin.password.reset-form')
->with('success', 'Kode berhasil diverifikasi! Silakan buat password baru.');
} }
$record = DB::table('password_reset_tokens')->where('email', $email)->first();
if (!$record) {
return back()->with('error', 'Kode OTP tidak ditemukan. Silakan kirim ulang.');
}
// ✅ 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('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. Waktu berkurang 30 detik.')
->with('otp_wrong', true); // ← flag untuk kurangi timer di frontend
}
$verifiedToken = Str::random(60);
DB::table('password_reset_tokens')
->where('email', $email)
->update(['token' => Hash::make($verifiedToken)]);
$request->session()->put('admin_reset_token', $verifiedToken);
return redirect()->route('admin.password.reset-form')
->with('success', 'Kode berhasil diverifikasi! Silakan buat password baru.');
}
// ───────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────
// STEP 2 — Kirim ulang OTP // STEP 2 — Kirim ulang OTP
// ───────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────
@ -182,41 +186,53 @@ public function showResetForm(Request $request)
// ───────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────
// STEP 3 — Proses: simpan password baru // STEP 3 — Proses: simpan password baru
// ───────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────
public function resetPassword(Request $request) // ─────────────────────────────────────────────────────────
{ // STEP 3 — Proses: simpan password baru
$request->validate([ // ─────────────────────────────────────────────────────────
'password' => ['required', 'min:8', 'confirmed', 'regex:/^\S+$/'], public function resetPassword(Request $request)
'password_confirmation' => ['required'], {
], [ $request->validate([
'password.required' => 'Password baru wajib diisi.', 'password' => ['required', 'min:8', 'confirmed', 'regex:/^\S+$/'],
'password.min' => 'Password minimal 8 karakter.', 'password_confirmation' => ['required'],
'password.confirmed' => 'Konfirmasi password tidak cocok.', ], [
'password.regex' => 'Password tidak boleh mengandung spasi.', 'password.required' => 'Password baru wajib diisi.',
]); 'password.min' => 'Password minimal 8 karakter.',
'password.confirmed' => 'Konfirmasi password tidak cocok.',
'password.regex' => 'Password tidak boleh mengandung spasi.',
]);
$email = $request->session()->get('admin_reset_email'); $email = $request->session()->get('admin_reset_email');
$token = $request->session()->get('admin_reset_token'); $token = $request->session()->get('admin_reset_token');
if (!$email || !$token) { if (!$email || !$token) {
return redirect()->route('admin.password.request') return redirect()->route('admin.password.request')
->with('error', 'Sesi tidak valid. Silakan ulangi dari awal.'); ->with('error', 'Sesi tidak valid. Silakan ulangi dari awal.');
}
$record = DB::table('password_reset_tokens')->where('email', $email)->first();
if (!$record || !Hash::check($token, $record->token)) {
return redirect()->route('admin.password.request')
->with('error', 'Token tidak valid. Silakan ulangi dari awal.');
}
User::where('email', $email)
->where('is_admin', true)
->update(['password' => Hash::make($request->password)]);
DB::table('password_reset_tokens')->where('email', $email)->delete();
$request->session()->forget(['admin_reset_email', 'admin_reset_token']);
return redirect()->route('admin.login.form')
->with('success', 'Password admin berhasil diubah! Silakan login dengan password baru.');
} }
$record = DB::table('password_reset_tokens')->where('email', $email)->first();
if (!$record || !Hash::check($token, $record->token)) {
return redirect()->route('admin.password.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)]);
DB::table('password_reset_tokens')->where('email', $email)->delete();
$request->session()->forget(['admin_reset_email', 'admin_reset_token']);
return redirect()->route('admin.login.form')
->with('success', 'Password admin berhasil diubah! Silakan login dengan password baru.');
}
} }

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');
});
}
};

File diff suppressed because it is too large Load Diff

View File

@ -10,219 +10,630 @@
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { 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 */ :root {
.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-width: 260px;
.sidebar-header { padding: 25px 20px; text-align: center; border-bottom: 1px solid rgba(255,255,255,0.1); } --header-height: 64px;
.sidebar-header h3 { font-size: 1.8rem; font-weight: bold; margin-bottom: 5px; } --green-dark: #15803d;
.sidebar-header p { opacity: 0.8; font-size: 0.9rem; } --green-main: #16a34a;
.sidebar-menu { padding: 20px 0; } --green-light: #22c55e;
.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; }
/* Main Content */ body {
.main-content { margin-left: 280px; flex: 1; min-height: 100vh; } font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
.admin-header { background: white; padding: 20px 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-bottom: 1px solid #dee2e6; } background: #f1f5f2;
.content-area { padding: 30px; } overflow-x: hidden;
}
/* Stats Cards */ /* ===== SIDEBAR ===== */
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 25px; margin-bottom: 40px; } .sidebar {
.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; } width: var(--sidebar-width);
.stat-card:hover { transform: translateY(-5px); } background: linear-gradient(180deg, var(--green-dark) 0%, #14532d 100%);
.stat-card.products { border-left-color: #28a745; } color: white;
.stat-card.orders { border-left-color: #007bff; } min-height: 100vh;
.stat-card.revenue { border-left-color: #ffc107; } position: fixed;
.stat-card.users { border-left-color: #6f42c1; } top: 0;
.stat-number { font-size: 2.5rem; font-weight: bold; margin-bottom: 8px; } left: 0;
.stat-label { color: #6c757d; font-size: 1rem; font-weight: 500; } z-index: 1050;
.stat-icon { float: right; font-size: 2.5rem; opacity: 0.3; margin-top: -10px; } transition: transform 0.3s ease;
display: flex;
flex-direction: column;
overflow-y: auto;
}
/* Back Button */ .sidebar-header {
.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; } padding: 24px 20px 20px;
.back-button:hover { background: linear-gradient(45deg, #c82333, #a71e2a); transform: translateY(-2px); color: white; text-decoration: none; } text-align: center;
border-bottom: 1px solid rgba(255,255,255,0.12);
flex-shrink: 0;
}
/* Chart Card Header */ .sidebar-header .brand-icon {
.chart-card-header { 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; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap; box-shadow: 0 1px 0 #e5e7eb;
gap: 10px; 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 { .btn-lihat-detail {
background: rgba(255,255,255,0.2); background: rgba(255,255,255,0.2);
color: white; color: white;
border: 2px solid rgba(255,255,255,0.6); border: 1.5px solid rgba(255,255,255,0.55);
padding: 7px 18px; padding: 6px 16px;
border-radius: 20px; border-radius: 20px;
font-size: 0.85rem; font-size: 0.8rem;
font-weight: 600; font-weight: 600;
text-decoration: none; text-decoration: none;
transition: all 0.3s ease; transition: all 0.2s;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
white-space: nowrap; white-space: nowrap;
} }
.btn-lihat-detail:hover { .btn-lihat-detail:hover {
background: white; background: white;
color: #28a745; color: var(--green-main);
border-color: white; border-color: white;
text-decoration: none; text-decoration: none;
transform: translateY(-2px); transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15); box-shadow: 0 4px 10px rgba(0,0,0,0.12);
}
.chart-card-body {
padding: 20px;
} }
/* Empty State */ /* Empty State */
.empty-state { text-align: center; padding: 60px 20px; color: #6c757d; } .empty-state {
.empty-state i { font-size: 4rem; margin-bottom: 20px; opacity: 0.3; } text-align: center;
.empty-state h4 { font-size: 1.5rem; margin-bottom: 10px; } padding: 48px 20px;
.empty-state p { font-size: 1rem; margin-bottom: 20px; } color: #9ca3af;
}
@media (max-width: 768px) { .empty-state i { font-size: 3rem; margin-bottom: 16px; opacity: 0.35; display: block; }
.sidebar { width: 100%; position: relative; min-height: auto; } .empty-state h4 { font-size: 1.1rem; color: #6b7280; margin-bottom: 8px; }
.main-content { margin-left: 0; } .empty-state p { font-size: 0.875rem; margin-bottom: 16px; }
.stats-grid { grid-template-columns: 1fr; }
.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> </style>
</head> </head>
<body> <body>
<a href="{{ route('home') }}" class="back-button">
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="admin-container"> <!-- Sidebar Overlay (mobile) -->
<!-- Sidebar --> <div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<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
</a>
<a href="{{ route('admin.orders') }}" class="menu-item {{ request()->routeIs('admin.orders') ? 'active' : '' }}">
<i class="fas fa-shopping-cart"></i>Pesanan
</a>
{{-- TAMBAHKAN DI SINI --}}
<a href="{{ route('admin.cancellations') }}" class="menu-item">
<i class="fas fa-times-circle"></i>Pengajuan Batal
</a>
<a href="{{ route('admin.users') }}" class="menu-item {{ request()->routeIs('admin.users') ? 'active' : '' }}">
<i class="fas fa-users"></i>Pengguna
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item {{ request()->routeIs('admin.laporan') ? 'active' : '' }}">
<i class="fas fa-chart-line"></i>Laporan
</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">
<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>
</form>
</div>
</div>
</nav>
<!-- Main Content --> <!-- Sidebar -->
<div class="main-content"> <nav class="sidebar" id="sidebar">
<div class="admin-header"> <div class="sidebar-header">
<h2>Dashboard</h2> <span class="brand-icon">🌱</span>
<p class="mb-0">Selamat datang di dashboard admin!</p> <h3>Bibit Cabai</h3>
</div> <p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
<div class="content-area">
<div class="stats-grid">
<div class="stat-card products">
<div class="stat-number text-success">{{ $stats['total_products'] }}</div>
<div class="stat-label">Total Produk</div>
<i class="fas fa-seedling stat-icon text-success"></i>
</div>
<div class="stat-card orders">
<div class="stat-number text-primary">{{ $stats['active_products'] }}</div>
<div class="stat-label">Produk Aktif</div>
<i class="fas fa-check-circle stat-icon text-primary"></i>
</div>
<div class="stat-card revenue">
<div class="stat-number text-warning">{{ $stats['total_sold'] }}</div>
<div class="stat-label">Total Terjual</div>
<i class="fas fa-chart-line stat-icon text-warning"></i>
</div>
<div class="stat-card users">
<div class="stat-number text-info">{{ $stats['low_stock'] }}</div>
<div class="stat-label">Stok Menipis</div>
<i class="fas fa-exclamation-triangle stat-icon text-info"></i>
</div>
</div>
<div class="row">
<div class="col-12 mb-4">
<div class="card">
<div class="card-header bg-success text-white">
<div class="chart-card-header">
<h5 class="mb-0">📈 Grafik Pendapatan Harian (30 Hari Terakhir)</h5>
{{-- Tombol Lihat Detail mengarah ke halaman laporan --}}
<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">
@if(count($chartLabels) > 0)
<canvas id="salesChart" height="80"></canvas>
@else
<div class="empty-state">
<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>
</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> -->
<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>
<a href="{{ route('admin.products.index') }}" class="menu-item {{ request()->routeIs('admin.products.*') ? 'active' : '' }}">
<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>
<span>Pesanan</span>
</a>
<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>
<span>Pengguna</span>
</a>
<a href="{{ route('admin.laporan') }}" class="menu-item {{ request()->routeIs('admin.laporan') ? 'active' : '' }}">
<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" onclick="return confirm('Apakah Anda yakin ingin logout?')">
<i class="fas fa-sign-out-alt"></i>
<span>Logout</span>
</button>
</form>
</div>
</nav>
<!-- Main Content -->
<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>
<div class="stat-label">Total Produk</div>
<i class="fas fa-seedling stat-icon text-success"></i>
</div>
<div class="stat-card orders">
<div class="stat-number text-primary">{{ $stats['active_products'] }}</div>
<div class="stat-label">Produk Aktif</div>
<i class="fas fa-check-circle stat-icon text-primary"></i>
</div>
<div class="stat-card revenue">
<div class="stat-number text-warning">{{ $stats['total_sold'] }}</div>
<div class="stat-label">Total Terjual</div>
<i class="fas fa-chart-line stat-icon text-warning"></i>
</div>
<div class="stat-card users">
<div class="stat-number text-info">{{ $stats['low_stock'] }}</div>
<div class="stat-label">Stok Menipis</div>
<i class="fas fa-exclamation-triangle stat-icon text-info"></i>
</div>
</div>
<!-- Chart Card -->
<div class="chart-card">
<div class="chart-card-header">
<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 class="chart-card-body">
@if(count($chartLabels) > 0)
<canvas id="salesChart" height="80"></canvas>
@else
<div class="empty-state">
<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-buka-laporan">
<i class="fas fa-chart-bar"></i>Buka Laporan
</a>
</div>
@endif
</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/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 src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<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) @if(count($chartLabels) > 0)
const ctx = document.getElementById('salesChart').getContext('2d'); const ctx = document.getElementById('salesChart').getContext('2d');
new Chart(ctx, { new Chart(ctx, {
@ -232,11 +643,12 @@
datasets: [{ datasets: [{
label: 'Penjualan (Rp)', label: 'Penjualan (Rp)',
data: {!! json_encode($chartData) !!}, data: {!! json_encode($chartData) !!},
borderColor: '#28a745', borderColor: '#16a34a',
backgroundColor: 'rgba(40, 167, 69, 0.1)', backgroundColor: 'rgba(22, 163, 74, 0.08)',
borderWidth: 2, borderWidth: 2.5,
pointBackgroundColor: '#28a745', pointBackgroundColor: '#16a34a',
pointRadius: 4, pointRadius: 4,
pointHoverRadius: 6,
tension: 0.4, tension: 0.4,
fill: true fill: true
}] }]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { 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 { .sidebar {
width: 280px; background: linear-gradient(180deg, #28a745, #20c997); width: var(--sidebar-width);
color: white; min-height: 100vh; position: fixed; top: 0; left: 0; z-index: 1000; 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 { .sidebar-header {
padding: 25px 20px; text-align: center; padding: 24px 20px 20px;
border-bottom: 1px solid rgba(255,255,255,0.1); 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-header .brand-icon { font-size: 2rem; margin-bottom: 6px; display: block; }
.sidebar-menu { padding: 20px 0; } .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 { .menu-item {
display: block; padding: 15px 25px; color: white; display: flex;
text-decoration: none; transition: all 0.3s; border-left: 4px solid transparent; 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; } .menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
.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; }
.back-button { .menu-item:hover {
position: fixed; top: 20px; right: 20px; z-index: 1100; background: rgba(255,255,255,0.1);
background: linear-gradient(45deg, #dc3545, #c82333); color: white;
color: white; border: none; padding: 12px 20px; text-decoration: none;
border-radius: 25px; font-weight: 600; text-decoration: none; transition: all 0.3s; 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 { @keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); } from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); } to { opacity: 1; transform: translateY(0); }
} }
@media (max-width: 768px) { /* ===== RESPONSIVE ===== */
.sidebar { width: 100%; position: relative; } @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; } .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> </style>
</head> </head>
<body> <body>
<a href="{{ route('home') }}" class="back-button"> <div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="admin-container"> <!-- ===== SIDEBAR ===== -->
<nav class="sidebar" id="sidebar">
{{-- Sidebar --}}
<nav class="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3> <span class="brand-icon">🌱</span>
<p>Admin Dashboard</p> <h3>Bibit Cabai</h3>
<small class="text-light">{{ Auth::user()->name ?? 'Admin User' }}</small> <p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div> </div>
<div class="sidebar-menu"> <div class="sidebar-menu">
<div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.dashboard') }}" class="menu-item"> <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>
<a href="{{ route('admin.products.index') }}" class="menu-item"> <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>
<a href="{{ route('admin.orders') }}" class="menu-item active"> <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>
<a href="{{ route('admin.cancellations') }}" class="menu-item"> <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>
<a href="{{ route('admin.users') }}" class="menu-item"> <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>
<a href="{{ route('admin.laporan') }}" class="menu-item"> <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>
<!-- <a href="{{ route('admin.settings') }}" class="menu-item"> </div>
<i class="fas fa-cog"></i>Pengaturan --> <div class="sidebar-footer">
</a> <form method="POST" action="{{ route('admin.logout') }}">
<div class="mt-4 px-3"> @csrf
<form method="POST" action="{{ route('admin.logout') }}"> <button type="submit" class="btn-logout">
@csrf <i class="fas fa-sign-out-alt"></i><span>Logout</span>
<button type="submit" class="btn btn-outline-light btn-sm w-100"> </button>
<i class="fas fa-sign-out-alt me-2"></i>Logout </form>
</button>
</form>
</div>
</div> </div>
</nav> </nav>
{{-- Main Content --}} <!-- ===== MAIN ===== -->
<div class="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.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"> <div class="content-area fade-in">
@if ($errors->any()) @if ($errors->any())
<div class="alert alert-danger alert-dismissible fade show"> <div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
<strong>Error!</strong> <i class="fas fa-exclamation-circle me-2"></i><strong>Error!</strong>
<ul class="mb-0 mt-2"> <ul class="mb-0 mt-1">
@foreach ($errors->all() as $error) @foreach ($errors->all() as $error)
<li>{{ $error }}</li> <li style="font-size:0.85rem;">{{ $error }}</li>
@endforeach @endforeach
</ul> </ul>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
@ -147,216 +452,235 @@
@endif @endif
@if(session('success')) @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') }} <i class="fas fa-check-circle me-2"></i>{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div> </div>
@endif @endif
<form action="{{ route('admin.transaksis.update', $transaksi->id) }}" method="POST"> <form id="editOrderForm" action="{{ route('admin.transaksis.update', $transaksi->id) }}" method="POST">
@csrf @csrf
@method('PUT') @method('PUT')
<div class="row"> <div class="row g-3">
{{-- Kolom Kiri --}} <!-- ===== KOLOM KIRI (8/12) ===== -->
<div class="col-lg-8"> <div class="col-lg-8">
{{-- Order Details --}} <!-- Order Details (editable) -->
<div class="card shadow mb-4" style="border-radius:12px;"> <div class="info-card">
<div class="card-header py-3" style="background:#f8f9fa; border-radius:12px 12px 0 0;"> <div class="info-card-header">
<h6 class="m-0 fw-bold text-primary">Order Details</h6> <div class="info-card-icon icon-blue">
</div> <i class="fas fa-file-alt"></i>
<div class="card-body">
<div class="mb-3">
<label class="form-label fw-semibold">Invoice Number</label>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->invoice_number }}" readonly>
</div> </div>
<div class="row"> <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="col-md-6">
<div class="section-divider">Invoice</div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-semibold">Order Status <span class="text-danger">*</span></label> <label class="form-label-custom">Invoice Number</label>
<select class="form-select @error('order_status') is-invalid @enderror" <input type="text" class="form-control-custom readonly"
name="order_status" required> value="{{ $transaksi->invoice_number }}" readonly>
</div>
<div class="mb-3">
<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) @foreach(['pending','processing','shipped','delivered','cancelled'] as $s)
<option value="{{ $s }}" <option value="{{ $s }}" {{ old('order_status', $transaksi->order_status) == $s ? 'selected' : '' }}>
{{ old('order_status', $transaksi->order_status) == $s ? 'selected' : '' }}>
{{ ucfirst($s) }} {{ ucfirst($s) }}
</option> </option>
@endforeach @endforeach
</select> </select>
@error('order_status') @error('order_status')
<div class="invalid-feedback">{{ $message }}</div> <div class="invalid-feedback-custom">{{ $message }}</div>
@enderror @enderror
</div> </div>
</div>
<div class="col-md-6">
<div class="mb-3"> <div class="mb-3">
<label class="form-label fw-semibold">Payment Status <span class="text-danger">*</span></label> <label class="form-label-custom">Payment Status <span style="color:#dc2626;">*</span></label>
<select class="form-select @error('payment_status') is-invalid @enderror" <select name="payment_status" class="form-select-custom @error('payment_status') is-invalid @enderror" required>
name="payment_status" required>
@foreach(['pending','paid','failed'] as $p) @foreach(['pending','paid','failed'] as $p)
<option value="{{ $p }}" <option value="{{ $p }}" {{ old('payment_status', $transaksi->payment_status) == $p ? 'selected' : '' }}>
{{ old('payment_status', $transaksi->payment_status) == $p ? 'selected' : '' }}>
{{ ucfirst($p) }} {{ ucfirst($p) }}
</option> </option>
@endforeach @endforeach
</select> </select>
@error('payment_status') @error('payment_status')
<div class="invalid-feedback">{{ $message }}</div> <div class="invalid-feedback-custom">{{ $message }}</div>
@enderror @enderror
</div> </div>
</div> </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"
value="{{ old('tracking_number', $transaksi->tracking_number) }}"
placeholder="Enter tracking number">
@error('tracking_number')
<div class="invalid-feedback">{{ $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>
@error('notes')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
</div>
{{-- Customer Information (readonly) --}} <div class="row g-3 mt-1">
<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>
</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>
<div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="mb-3"> <label class="form-label-custom">Tracking Number</label>
<label class="form-label fw-semibold">Phone</label> <input type="text" name="tracking_number"
<input type="text" class="form-control bg-light" class="form-control-custom @error('tracking_number') is-invalid @enderror"
value="{{ $transaksi->customer_phone }}" readonly> value="{{ old('tracking_number', $transaksi->tracking_number) }}"
</div> placeholder="Masukkan nomor resi...">
@error('tracking_number')
<div class="invalid-feedback-custom">{{ $message }}</div>
@enderror
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="mb-3"> <label class="form-label-custom">Catatan / Notes</label>
<label class="form-label fw-semibold">Email</label> <textarea name="notes"
<input type="text" class="form-control bg-light" class="form-control-custom @error('notes') is-invalid @enderror"
value="{{ $transaksi->customer_email ?? '-' }}" readonly> placeholder="Catatan tambahan...">{{ old('notes', $transaksi->notes) }}</textarea>
</div> @error('notes')
<div class="invalid-feedback-custom">{{ $message }}</div>
@enderror
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{-- Shipping Information (readonly) --}} <!-- Customer Information (readonly) -->
<div class="card shadow mb-4" style="border-radius:12px;"> <div class="info-card">
<div class="card-header py-3" style="background:#f8f9fa; border-radius:12px 12px 0 0;"> <div class="info-card-header">
<h6 class="m-0 fw-bold text-primary"> <div class="info-card-icon icon-purple">
Shipping Information <i class="fas fa-user"></i>
<small class="text-muted fw-normal ms-2" style="font-size:0.75rem;">
<i class="fas fa-lock"></i> Tidak dapat diubah
</small>
</h6>
</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> </div>
<div class="row"> <div>
<div class="col-md-4"> <h3>Customer Information</h3>
<div class="mb-3"> <p>Informasi pembeli <span class="lock-badge"><i class="fas fa-lock"></i> Tidak dapat diubah</span></p>
<label class="form-label fw-semibold">City</label> </div>
<input type="text" class="form-control bg-light" </div>
value="{{ $transaksi->city }}" readonly> <div class="info-card-body">
</div> <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">
<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="info-card">
<div class="info-card-header">
<div class="info-card-icon icon-orange">
<i class="fas fa-truck"></i>
</div>
<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>
<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>
<div class="col-md-4"> <div class="col-md-4">
<div class="mb-3"> <label class="form-label-custom">Kota</label>
<label class="form-label fw-semibold">Province</label> <input type="text" class="form-control-custom" value="{{ $transaksi->city }}" readonly>
<input type="text" class="form-control bg-light"
value="{{ $transaksi->province }}" readonly>
</div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="mb-3"> <label class="form-label-custom">Provinsi</label>
<label class="form-label fw-semibold">Postal Code</label> <input type="text" class="form-control-custom" value="{{ $transaksi->province }}" readonly>
<input type="text" class="form-control bg-light" </div>
value="{{ $transaksi->postal_code }}" readonly> <div class="col-md-4">
</div> <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> </div>
</div> </div>
</div> </div>
{{-- Akhir col-lg-8 --}} <!-- Akhir col-lg-8 -->
{{-- Kolom Kanan --}} <!-- ===== KOLOM KANAN (4/12) ===== -->
<div class="col-lg-4"> <div class="col-lg-4">
{{-- Order Summary --}} <!-- Order Summary -->
<div class="card shadow mb-4" style="border-radius:12px;"> <div class="info-card">
<div class="card-header py-3" style="background:#f8f9fa; border-radius:12px 12px 0 0;"> <div class="info-card-header">
<h6 class="m-0 fw-bold text-primary">Order Summary</h6> <div class="info-card-icon icon-green">
<i class="fas fa-box"></i>
</div>
<div>
<h3>Order Summary</h3>
<p>Ringkasan produk pesanan</p>
</div>
</div> </div>
<div class="card-body"> <div class="info-card-body">
<h5 class="text-primary">{{ $transaksi->invoice_number }}</h5> <table class="summary-table">
<p class="text-muted mb-3">{{ $transaksi->created_at->format('d M Y, H:i') }}</p>
<table class="table table-sm table-borderless">
<tbody> <tbody>
@foreach($transaksi->details as $detail) @foreach($transaksi->details as $detail)
<tr> <tr>
<td>{{ $detail->product->name ?? 'N/A' }}</td> <td>{{ $detail->product->name ?? 'N/A' }}</td>
<td class="text-end">x{{ $detail->quantity }}</td> <td style="text-align:center;color:#9ca3af;">x{{ $detail->quantity }}</td>
<td class="text-end">Rp {{ number_format($detail->subtotal, 0, ',', '.') }}</td> <td>Rp {{ number_format($detail->subtotal, 0, ',', '.') }}</td>
</tr> </tr>
@endforeach @endforeach
</tbody> </tbody>
<tfoot class="border-top"> <tfoot>
<tr> <tr>
<th colspan="2">Total:</th> <td colspan="2" style="font-size:0.8rem;color:#6b7280;">Total Amount</td>
<th class="text-end">Rp {{ number_format($transaksi->total_amount, 0, ',', '.') }}</th> <td style="color:var(--green-main);font-size:0.95rem;">Rp {{ number_format($transaksi->total_amount, 0, ',', '.') }}</td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
</div> </div>
</div> </div>
{{-- Action Buttons --}} <!-- Action Buttons -->
<div class="card shadow" style="border-radius:12px;"> <div class="info-card">
<div class="card-body"> <div class="info-card-header">
<button type="submit" class="btn btn-primary w-100 mb-2"> <div class="info-card-icon icon-gray">
<i class="fas fa-save me-1"></i> Update Order <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> </button>
<a href="{{ route('admin.transaksis.show', $transaksi->id) }}" <a href="{{ route('admin.transaksis.show', $transaksi->id) }}"
class="btn btn-secondary w-100"> style="width:100%;padding:10px 16px;border-radius:9px;
<i class="fas fa-times me-1"></i> Cancel 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> </a>
</div> </div>
</div> </div>
</div> </div>
{{-- Akhir col-lg-4 --}} <!-- Akhir col-lg-4 -->
</div> </div>
</form> </form>
@ -364,8 +688,28 @@ class="btn btn-secondary w-100">
</div> </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();
});
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> 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> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,233 +4,784 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Pengguna - Bibit Cabai Admin</title> <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 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"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style> <style>
* { margin:0; padding:0; box-sizing:border-box; } * { 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 { .sidebar {
width:280px; background:linear-gradient(180deg,#28a745,#20c997); width: var(--sidebar-width);
color:white; min-height:100vh; position:fixed; top:0; left:0; z-index:1000; 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 {
.sidebar-menu { padding:20px 0; } 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 { .menu-item {
display:block; padding:15px 25px; color:white; text-decoration:none; display: flex;
transition:all 0.3s; border-left:4px solid transparent; 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; .menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
border-left-color:#fff; transform:translateX(5px);
.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; } .menu-item.active {
.admin-header { background:white; padding:20px 30px; box-shadow:0 2px 4px rgba(0,0,0,0.1); } background: rgba(255,255,255,0.15);
.content-area { padding:30px; } color: white;
.form-card { background:white; border-radius:12px; padding:35px; box-shadow:0 4px 6px rgba(0,0,0,0.05); max-width:700px; } border-left-color: #fff;
.form-label { font-weight:600; color:#495057; } font-weight: 600;
.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;
} }
.back-button:hover { transform:translateY(-2px); color:white; }
.section-divider { border-top:2px solid #f8f9fa; margin:25px 0; } .sidebar-footer {
.user-info-badge { padding: 16px 20px;
background:#f8f9fa; border-radius:8px; padding:12px 15px; border-top: 1px solid rgba(255,255,255,0.12);
margin-bottom:20px; font-size:0.85rem; color:#6c757d; 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> </style>
</head> </head>
<body> <body>
<a href="{{ route('home') }}" class="back-button"> <div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="admin-container"> <!-- ===== SIDEBAR ===== -->
<nav class="sidebar"> <nav class="sidebar" id="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3> <span class="brand-icon">🌱</span>
<p>Admin Dashboard</p> <h3>Bibit Cabai</h3>
<small class="text-light">{{ Auth::user()->name ?? 'Admin' }}</small> <p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div> </div>
<div class="sidebar-menu"> <div class="sidebar-menu">
<a href="{{ route('admin.dashboard') }}" class="menu-item"><i class="fas fa-tachometer-alt"></i>Dashboard</a> <div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.products.index') }}" class="menu-item"><i class="fas fa-seedling"></i>Kelola Produk</a> <a href="{{ route('admin.dashboard') }}" class="menu-item">
<a href="{{ route('admin.orders') }}" class="menu-item"><i class="fas fa-shopping-cart"></i>Pesanan</a> <i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
<a href="{{ route('admin.users') }}" class="menu-item active"><i class="fas fa-users"></i>Pengguna</a> </a>
<a href="{{ route('admin.reports') }}" class="menu-item"><i class="fas fa-chart-line"></i>Laporan</a> <a href="{{ route('admin.products.index') }}" class="menu-item">
<!-- <a href="{{ route('admin.settings') }}" class="menu-item"><i class="fas fa-cog"></i>Pengaturan</a> --> <i class="fas fa-seedling"></i><span>Kelola Produk</span>
<div class="mt-4 px-3"> </a>
<form method="POST" action="{{ route('admin.logout') }}"> <a href="{{ route('admin.orders') }}" class="menu-item">
@csrf <i class="fas fa-shopping-cart"></i><span>Pesanan</span>
<button type="submit" class="btn btn-outline-light btn-sm w-100"> </a>
<i class="fas fa-sign-out-alt me-2"></i>Logout <a href="{{ route('admin.cancellations') }}" class="menu-item">
</button> <i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</form> </a>
</div> <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-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</button>
</form>
</div> </div>
</nav> </nav>
<!-- ===== MAIN ===== -->
<div class="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.users') }}">Pengguna</a></li>
<li class="breadcrumb-item active">Edit Pengguna</li>
</ol>
</nav>
<h2>Edit Pengguna</h2>
<p class="mb-0">Perbarui informasi pengguna <strong>{{ $user->name }}</strong>.</p>
</div>
<div class="content-area"> <!-- Topbar -->
<div class="form-card"> <header class="topbar">
<div class="topbar-left">
<div class="user-info-badge"> <button class="hamburger-btn" onclick="toggleSidebar()">
<i class="fas fa-info-circle me-2 text-info"></i> <i class="fas fa-bars"></i>
ID Pengguna: <strong>#{{ $user->id }}</strong> &nbsp;|&nbsp; </button>
Terdaftar: <strong>{{ $user->created_at->format('d M Y') }}</strong> <div class="topbar-title">
@if($user->email_verified_at) <div class="topbar-breadcrumb">
&nbsp;|&nbsp; <span class="text-success"><i class="fas fa-check-circle"></i> Email Terverifikasi</span> <a href="{{ route('admin.dashboard') }}"><i class="fas fa-home"></i> Dashboard</a>
@else &nbsp;/&nbsp;<a href="{{ route('admin.users') }}">Pengguna</a>
&nbsp;|&nbsp; <span class="text-muted"><i class="fas fa-clock"></i> Belum Verifikasi</span> &nbsp;/&nbsp;<a href="{{ route('admin.users.show', $user) }}">{{ $user->name }}</a>
@endif &nbsp;/&nbsp;Edit
</div>
<h2>Edit Pengguna</h2>
<p>{{ $user->name }}</p>
</div> </div>
@if($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form 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="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"
value="{{ old('name', $user->name) }}" required>
@error('name')<div class="invalid-feedback">{{ $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"
value="{{ old('email', $user->email) }}" required>
@error('email')<div class="invalid-feedback">{{ $message }}</div>@enderror
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Password Baru</label>
<div class="input-group">
<input type="password" name="password" id="password"
class="form-control @error('password') is-invalid @enderror"
placeholder="Minimal 8 karakter">
<button type="button" class="btn btn-outline-secondary" 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
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Konfirmasi Password Baru</label>
<div class="input-group">
<input type="password" name="password_confirmation" id="password_confirmation"
class="form-control"
placeholder="Ulangi password baru">
<button type="button" class="btn btn-outline-secondary" 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 class="mb-3">
<label class="form-label">Nomor Telepon</label>
<input type="text" name="phone"
value="{{ old('phone', '+62') }}"
placeholder="+62812xxxxxxxx">
@error('phone')<div class="invalid-feedback">{{ $message }}</div>@enderror
</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
</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"
{{ old('is_admin', $user->is_admin) ? 'checked' : '' }}
{{ $user->id === auth()->id() ? 'disabled' : '' }}>
<label class="form-check-label" for="is_admin">
<strong>Jadikan Admin</strong>
@if($user->id === auth()->id())
<small class="text-danger d-block">Anda tidak dapat mengubah role akun sendiri.</small>
@else
<small class="text-muted d-block">Admin memiliki akses penuh ke dashboard.</small>
@endif
</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
</button>
<a href="{{ route('admin.users') }}" class="btn btn-outline-secondary">
<i class="fas fa-times me-2"></i>Batal
</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>
</div>
</form>
</div> </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 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 style="font-size:0.85rem;">{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@endif
@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')
<div class="row g-3">
<!-- ===== 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-custom">{{ $message }}</div>@enderror
</div>
<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-custom">{{ $message }}</div>@enderror
</div>
</div>
<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-custom @error('password') is-invalid @enderror"
placeholder="Minimal 8 karakter">
<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="invalid-feedback-custom">{{ $message }}</div>@enderror
</div>
<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-custom"
placeholder="Ulangi password baru">
<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>
</div>
<!-- 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"
class="form-control-custom @error('phone') is-invalid @enderror"
value="{{ old('phone', $user->phone ?? '+62') }}"
placeholder="+62812xxxxxxxx">
@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>
<!-- 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>
<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' : '' }}>
<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())
<div class="check-card-desc text-danger">Anda tidak dapat mengubah role akun sendiri.</div>
@else
<div class="check-card-desc">Admin memiliki akses penuh ke seluruh dashboard.</div>
@endif
</div>
</label>
</div>
</div>
</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.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') }}"
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>
</div>
<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/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script> <script>
function togglePassword(fieldId, iconId) { function toggleSidebar() {
const field = document.getElementById(fieldId); document.getElementById('sidebar').classList.toggle('open');
const icon = document.getElementById(iconId); document.getElementById('sidebarOverlay').classList.toggle('show');
if (field.type === 'password') { }
field.type = 'text'; function closeSidebar() {
icon.classList.replace('fa-eye', 'fa-eye-slash'); document.getElementById('sidebar').classList.remove('open');
} else { document.getElementById('sidebarOverlay').classList.remove('show');
field.type = 'password'; }
icon.classList.replace('fa-eye-slash', 'fa-eye'); window.addEventListener('resize', function() {
} if (window.innerWidth > 900) closeSidebar();
} });
</script>
function togglePassword(fieldId, iconId) {
const field = document.getElementById(fieldId);
const icon = document.getElementById(iconId);
if (field.type === 'password') {
field.type = 'text';
icon.classList.replace('fa-eye', 'fa-eye-slash');
} else {
field.type = 'password';
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> </body>
</html> </html>

View File

@ -4,180 +4,679 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Detail Pengguna - Bibit Cabai Admin</title> <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 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"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style> <style>
* { margin:0; padding:0; box-sizing:border-box; } * { 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 { .sidebar {
width:280px; background:linear-gradient(180deg,#28a745,#20c997); width: var(--sidebar-width);
color:white; min-height:100vh; position:fixed; top:0; left:0; z-index:1000; 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 {
.sidebar-menu { padding:20px 0; } 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 { .menu-item {
display:block; padding:15px 25px; color:white; text-decoration:none; display: flex;
transition:all 0.3s; border-left:4px solid transparent; 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; .menu-item i { width: 18px; font-size: 0.95rem; flex-shrink: 0; text-align: center; }
border-left-color:#fff; transform:translateX(5px);
.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; } .menu-item.active {
.admin-header { background:white; padding:20px 30px; box-shadow:0 2px 4px rgba(0,0,0,0.1); } background: rgba(255,255,255,0.15);
.content-area { padding:30px; } color: white;
.detail-card { background:white; border-radius:12px; padding:35px; box-shadow:0 4px 6px rgba(0,0,0,0.05); max-width:700px; } border-left-color: #fff;
.avatar-circle { font-weight: 600;
width:80px; height:80px; border-radius:50%;
background:linear-gradient(135deg,#28a745,#20c997);
display:flex; align-items:center; justify-content:center;
font-size:2rem; color:white; font-weight:bold; margin-bottom:15px;
} }
.info-row { display:flex; padding:14px 0; border-bottom:1px solid #f8f9fa; }
.info-row:last-child { border-bottom:none; } .sidebar-footer {
.info-label { width:160px; font-weight:600; color:#6c757d; font-size:0.9rem; flex-shrink:0; } padding: 16px 20px;
.info-value { color:#333; flex:1; } border-top: 1px solid rgba(255,255,255,0.12);
.badge-role { padding:5px 12px; border-radius:12px; font-size:0.8rem; font-weight:600; } flex-shrink: 0;
.badge-admin { background:#cce5ff; color:#004085; } }
.badge-user { background:#d4edda; color:#155724; }
.badge-verified { background:#d4edda; color:#155724; } .btn-logout {
.badge-unverified { background:#e2e3e5; color:#383d41; } width: 100%;
.breadcrumb { background:transparent; padding:0; margin-bottom:10px; } background: rgba(255,255,255,0.12);
.breadcrumb-item a { color:#28a745; text-decoration:none; } color: white;
.back-button { border: 1.5px solid rgba(255,255,255,0.3);
position:fixed; top:20px; right:20px; z-index:1100; padding: 9px 16px;
background:linear-gradient(45deg,#dc3545,#c82333); border-radius: 8px;
color:white; border:none; padding:12px 20px; font-size: 0.875rem;
border-radius:25px; font-weight:600; text-decoration:none; transition:all 0.3s; 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: 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; }
.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> </style>
</head> </head>
<body> <body>
<a href="{{ route('home') }}" class="back-button"> <div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
<i class="fas fa-arrow-left me-2"></i>Kembali ke Website
</a>
<div class="admin-container"> <!-- ===== SIDEBAR ===== -->
<nav class="sidebar"> <nav class="sidebar" id="sidebar">
<div class="sidebar-header"> <div class="sidebar-header">
<h3>🌱 Bibit Cabai</h3> <span class="brand-icon">🌱</span>
<p>Admin Dashboard</p> <h3>Bibit Cabai</h3>
<small class="text-light">{{ Auth::user()->name ?? 'Admin' }}</small> <p class="subtitle">Admin Dashboard</p>
<span class="admin-name">{{ Auth::user()->name ?? 'Admin User' }}</span>
</div> </div>
<div class="sidebar-menu"> <div class="sidebar-menu">
<a href="{{ route('admin.dashboard') }}" class="menu-item"><i class="fas fa-tachometer-alt"></i>Dashboard</a> <div class="menu-section-label">Menu Utama</div>
<a href="{{ route('admin.products.index') }}" class="menu-item"><i class="fas fa-seedling"></i>Kelola Produk</a> <a href="{{ route('admin.dashboard') }}" class="menu-item">
<a href="{{ route('admin.orders') }}" class="menu-item"><i class="fas fa-shopping-cart"></i>Pesanan</a> <i class="fas fa-tachometer-alt"></i><span>Dashboard</span>
<a href="{{ route('admin.users') }}" class="menu-item active"><i class="fas fa-users"></i>Pengguna</a> </a>
<a href="{{ route('admin.reports') }}" class="menu-item"><i class="fas fa-chart-line"></i>Laporan</a> <a href="{{ route('admin.products.index') }}" class="menu-item">
<!-- <a href="{{ route('admin.settings') }}" class="menu-item"><i class="fas fa-cog"></i>Pengaturan</a> --> <i class="fas fa-seedling"></i><span>Kelola Produk</span>
<div class="mt-4 px-3"> </a>
<form method="POST" action="{{ route('admin.logout') }}"> <a href="{{ route('admin.orders') }}" class="menu-item">
@csrf <i class="fas fa-shopping-cart"></i><span>Pesanan</span>
<button type="submit" class="btn btn-outline-light btn-sm w-100"> </a>
<i class="fas fa-sign-out-alt me-2"></i>Logout <a href="{{ route('admin.cancellations') }}" class="menu-item">
</button> <i class="fas fa-times-circle"></i><span>Pengajuan Batal</span>
</form> </a>
</div> <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-logout">
<i class="fas fa-sign-out-alt"></i><span>Logout</span>
</button>
</form>
</div> </div>
</nav> </nav>
<!-- ===== MAIN ===== -->
<div class="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.users') }}">Pengguna</a></li>
<li class="breadcrumb-item active">Detail Pengguna</li>
</ol>
</nav>
<h2>Detail Pengguna</h2>
<p class="mb-0">Informasi lengkap pengguna.</p>
</div>
<div class="content-area"> <!-- Topbar -->
<div class="detail-card"> <header class="topbar">
<div class="topbar-left">
{{-- Avatar & Nama --}} <button class="hamburger-btn" onclick="toggleSidebar()">
<div class="text-center mb-4"> <i class="fas fa-bars"></i>
<div class="avatar-circle mx-auto"> </button>
{{ strtoupper(substr($user->name, 0, 1)) }} <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> </div>
<h4 class="mb-1">{{ $user->name }}</h4> <h2>Detail Pengguna</h2>
@if($user->is_admin) <p>{{ $user->name }}</p>
<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> </div>
</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.edit', $user) }}" class="btn-topbar btn-topbar-edit">
<i class="fas fa-edit"></i>
<span>Edit</span>
</a>
@if($user->id !== auth()->id())
<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>
@endif
</div>
</header>
{{-- Info Detail --}} <!-- Content -->
<div class="info-row"> <div class="content-area fade-in">
<div class="info-label"><i class="fas fa-hashtag me-2 text-muted"></i>ID</div>
<div class="info-value">#{{ $user->id }}</div> @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>
<div class="info-row"> @endif
<div class="info-label"><i class="fas fa-envelope me-2 text-muted"></i>Email</div>
<div class="info-value">{{ $user->email }}</div> <div class="row g-3">
</div>
<div class="info-row"> <!-- ===== KOLOM KIRI (8/12) ===== -->
<div class="info-label"><i class="fas fa-check-circle me-2 text-muted"></i>Verifikasi</div> <div class="col-lg-8">
<div class="info-value">
@if($user->email_verified_at) <!-- Profil & Info Dasar -->
<span class="badge-role badge-verified"> <div class="info-card">
<i class="fas fa-check me-1"></i>Terverifikasi <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> </span>
<small class="text-muted ms-2">{{ $user->email_verified_at->format('d M Y') }}</small> </div>
@else <div class="info-card-body">
<span class="badge-role badge-unverified">Belum Verifikasi</span> <!-- Avatar + nama -->
@endif <div style="display:flex;align-items:center;gap:16px;margin-bottom:20px;
</div> padding-bottom:16px;border-bottom:1px solid #f3f4f6;">
</div> <div class="avatar-lg">{{ strtoupper(substr($user->name, 0, 1)) }}</div>
<div class="info-row"> <div>
<div class="info-label"><i class="fas fa-phone me-2 text-muted"></i>Telepon</div> <div style="font-size:1.05rem;font-weight:700;color:#111827;">{{ $user->name }}</div>
<div class="info-value">{{ $user->phone ?? '-' }}</div> <div style="font-size:0.82rem;color:#6b7280;margin-top:2px;">{{ $user->email }}</div>
</div> <div style="margin-top:8px;">
<div class="info-row"> @if($user->email_verified_at)
<div class="info-label"><i class="fas fa-map-marker-alt me-2 text-muted"></i>Alamat</div> <span class="badge-status badge-verified">
<div class="info-value">{{ $user->address ?? '-' }}</div> <i class="fas fa-check me-1"></i>Email Terverifikasi
</div> </span>
<div class="info-row"> @else
<div class="info-label"><i class="fas fa-calendar me-2 text-muted"></i>Bergabung</div> <span class="badge-status badge-unverified">
<div class="info-value">{{ $user->created_at->format('d M Y, H:i') }}</div> <i class="fas fa-clock me-1"></i>Belum Verifikasi
</div> </span>
<div class="info-row"> @endif
<div class="info-label"><i class="fas fa-sync me-2 text-muted"></i>Diperbarui</div> </div>
<div class="info-value">{{ $user->updated_at->format('d M Y, H:i') }}</div> </div>
</div> </div>
{{-- Action Buttons --}} <div class="row g-4">
<div class="d-flex gap-2 mt-4"> <div class="col-md-6">
<a href="{{ route('admin.users.edit', $user) }}" class="btn btn-warning"> <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>
<i class="fas fa-edit me-2"></i>Edit Pengguna <div class="detail-row">
</a> <span class="detail-label"><i class="fas fa-hashtag me-1" style="font-size:0.7rem;"></i> ID</span>
<a href="{{ route('admin.users') }}" class="btn btn-outline-secondary"> <span class="detail-value"><strong>#{{ $user->id }}</strong></span>
<i class="fas fa-arrow-left me-2"></i>Kembali </div>
</a> <div class="detail-row">
@if($user->id !== auth()->id()) <span class="detail-label"><i class="fas fa-envelope me-1" style="font-size:0.7rem;"></i> Email</span>
<form method="POST" action="{{ route('admin.users.destroy', $user) }}" <span class="detail-value">{{ $user->email }}</span>
class="ms-auto" onsubmit="return confirm('Yakin ingin menghapus pengguna ini?')"> </div>
@csrf @method('DELETE') <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"> <button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Hapus <i class="fas fa-trash me-1"></i> Hapus Pengguna
</button> </button>
</form> </form>
@endif
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> @endif
<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/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> </body>
</html> </html>

View File

@ -40,7 +40,6 @@
border-color: #11998e; border-color: #11998e;
box-shadow: 0 0 0 0.2rem rgba(17, 153, 142, 0.25); box-shadow: 0 0 0 0.2rem rgba(17, 153, 142, 0.25);
} }
.step-wrapper { .step-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
@ -64,8 +63,6 @@
.step-line { height: 2px; width: 36px; margin: 0 6px 18px; } .step-line { height: 2px; width: 36px; margin: 0 6px 18px; }
.step-line.done { background: #11998e; } .step-line.done { background: #11998e; }
.step-line.idle { background: #dee2e6; } .step-line.idle { background: #dee2e6; }
/* OTP Boxes */
.otp-box { .otp-box {
width: 46px !important; width: 46px !important;
height: 54px !important; height: 54px !important;
@ -86,7 +83,13 @@
border-color: #11998e !important; border-color: #11998e !important;
background-color: #f0fff4 !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 { .email-badge {
background: #f0fff4; background: #f0fff4;
border: 1px solid #11998e33; border: 1px solid #11998e33;
@ -94,7 +97,6 @@
border-radius: 8px; border-radius: 8px;
padding: 10px 14px; padding: 10px 14px;
} }
.icon-box { .icon-box {
width: 64px; height: 64px; width: 64px; height: 64px;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
@ -160,6 +162,9 @@
</div> </div>
</div> </div>
{{-- =============================================
FORM OTP disabled seluruhnya saat expired
============================================= --}}
<form method="POST" action="{{ route('admin.password.verify-otp') }}" id="otpForm" novalidate> <form method="POST" action="{{ route('admin.password.verify-otp') }}" id="otpForm" novalidate>
@csrf @csrf
@ -170,12 +175,18 @@
{{-- OTP Boxes --}} {{-- OTP Boxes --}}
<div class="d-flex justify-content-center gap-2 mb-2" id="otpBoxes"> <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"> {{-- disabled langsung dari PHP jika backend bilang expired --}}
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off"> @php $isExpiredFromServer = session('otp_expired', false); @endphp
<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"> @for ($i = 0; $i < 6; $i++)
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off"> <input type="text"
<input type="text" class="otp-box form-control" maxlength="1" inputmode="numeric" pattern="[0-9]" autocomplete="off"> class="otp-box form-control"
maxlength="1"
inputmode="numeric"
pattern="[0-9]"
autocomplete="off"
{{ $isExpiredFromServer ? 'disabled' : '' }}>
@endfor
</div> </div>
<input type="hidden" name="otp" id="otpHidden"> <input type="hidden" name="otp" id="otpHidden">
@ -186,32 +197,44 @@
{{-- Countdown Timer --}} {{-- Countdown Timer --}}
<div class="text-center mb-4"> <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> <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>
<div id="expiredWrapper" style="display:none;"> <div id="expiredWrapper" {{ $isExpiredFromServer ? '' : 'style=display:none;' }}>
<span class="badge bg-danger fs-6 px-3 py-2"> <span class="badge bg-danger fs-6 px-3 py-2">
Kode sudah kadaluarsa Kode sudah kadaluarsa
</span> </span>
</div> </div>
</div> </div>
{{-- Tombol Verifikasi --}}
{{-- disabled dari server jika expired, dari JS jika OTP belum 6 digit --}}
<div class="d-grid mb-3"> <div class="d-grid mb-3">
<button type="submit" class="btn btn-primary-custom py-2 fw-bold" id="submitBtn" disabled> <button type="submit"
Verifikasi Kode class="btn btn-primary-custom py-2 fw-bold"
id="submitBtn"
{{ $isExpiredFromServer ? 'disabled' : 'disabled' }}>
@if($isExpiredFromServer)
Waktu Habis
@else
Verifikasi Kode
@endif
</button> </button>
</div> </div>
</form> </form>
<hr class="my-3"> <hr class="my-3">
{{-- Resend --}} {{-- Resend hanya aktif saat expired --}}
<div class="text-center"> <div class="text-center">
<p class="text-muted small mb-2">Tidak menerima kode?</p> <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 @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 🔄 Kirim Ulang Kode
</button> </button>
</form> </form>
@ -231,15 +254,29 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
const boxes = document.querySelectorAll('.otp-box');
const otpHidden = document.getElementById('otpHidden');
const submitBtn = document.getElementById('submitBtn');
const resendBtn = document.getElementById('resendBtn');
const countdown = document.getElementById('countdown');
const timerWrap = document.getElementById('timerWrapper');
const expiredWrap= document.getElementById('expiredWrapper');
// ---- OTP input logic ---- // ── 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');
// ── 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) => { boxes.forEach((box, i) => {
box.addEventListener('input', function () { box.addEventListener('input', function () {
this.value = this.value.replace(/[^0-9]/g, ''); this.value = this.value.replace(/[^0-9]/g, '');
@ -263,9 +300,13 @@
box.addEventListener('paste', function (e) { box.addEventListener('paste', function (e) {
e.preventDefault(); 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) => { 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(); updateHidden();
boxes[Math.min(pasted.length, 5)].focus(); boxes[Math.min(pasted.length, 5)].focus();
@ -275,29 +316,91 @@
function updateHidden() { function updateHidden() {
const otp = Array.from(boxes).map(b => b.value).join(''); const otp = Array.from(boxes).map(b => b.value).join('');
otpHidden.value = otp; 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 ---- // ── Countdown — pakai sessionStorage agar refresh tidak reset ──
let secs = 10 * 60; // Key unik per email agar tidak bentrok jika beda sesi
const timer = setInterval(() => { const STORAGE_KEY = 'otp_timer_{{ md5(session("admin_reset_email", "x")) }}';
secs--; const PENALTY_KEY = 'otp_penalty_{{ md5(session("admin_reset_email", "x")) }}';
const m = String(Math.floor(secs / 60)).padStart(2, '0'); const DURATION = 3 * 60; // 180 detik (3 menit)
const s = String(secs % 60).padStart(2, '0'); const WRONG_PENALTY = 30; // pengurangan waktu saat salah OTP (detik)
countdown.textContent = `${m}:${s}`;
if (secs <= 60) { countdown.style.background = '#ffc107'; countdown.style.color = '#000'; } let startTime = parseInt(sessionStorage.getItem(STORAGE_KEY) || '0');
if (secs <= 0) { if (!startTime || isNaN(startTime)) {
clearInterval(timer); startTime = Date.now();
timerWrap.style.display = 'none'; sessionStorage.setItem(STORAGE_KEY, startTime);
expiredWrap.style.display = 'block'; }
submitBtn.disabled = true;
resendBtn.disabled = false; // ── 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;
} }
}, 1000);
// Enable resend after 60s // Warna kuning saat sisa ≤ 30 detik
setTimeout(() => { resendBtn.disabled = false; }, 60000); 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;
}
// ── Bersihkan sessionStorage saat resend dikirim ─────────
resendForm.addEventListener('submit', function () {
sessionStorage.removeItem(STORAGE_KEY);
});
}); });
</script> </script>
</body> </body>

View File

@ -4,26 +4,73 @@
@section('styles') @section('styles')
<style> <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 { .product-card {
transition: transform 0.3s ease, box-shadow 0.3s ease; transition: transform 0.3s ease, box-shadow 0.3s ease;
border: none; border: none;
box-shadow: 0 2px 10px rgba(0,0,0,0.1); box-shadow: 0 2px 10px rgba(0,0,0,0.1);
} }
.product-card:hover { .product-card:hover {
transform: translateY(-5px); transform: translateY(-5px);
box-shadow: 0 5px 25px rgba(0,0,0,0.15); box-shadow: 0 5px 25px rgba(0,0,0,0.15);
} }
.product-image { .product-image {
height: 250px; height: 250px;
object-fit: cover; object-fit: cover;
transition: transform 0.3s ease; transition: transform 0.3s ease;
width: 100%;
} }
.product-card:hover .product-image { .product-card:hover .product-image {
transform: scale(1.05); 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 { .badge-bestseller {
position: absolute; position: absolute;
@ -44,12 +91,22 @@
font-weight: bold; font-weight: bold;
color: #28a745; color: #28a745;
} }
@media (max-width: 575.98px) {
.hero-image { .price-text {
max-height: 400px; font-size: 0.95rem;
object-fit: cover; margin-bottom: 6px !important;
border-radius: 15px; }
box-shadow: 0 10px 30px rgba(0,0,0,0.2); /* 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 { .quantity-input {
@ -62,17 +119,17 @@
@section('content') @section('content')
<div class="container-fluid p-0"> <div class="container-fluid p-0">
<!-- Hero Section --> <!-- 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="container">
<div class="row align-items-center"> <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> <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> <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"> <a href="{{ route('products.best-selling') }}" class="btn btn-light btn-lg">
<i class="fas fa-fire me-2"></i>Lihat Produk Terlaris <i class="fas fa-fire me-2"></i>Lihat Produk Terlaris
</a> </a>
</div> </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') }}" <img src="{{ asset('images/bara f1.jpeg') }}"
alt="Bibit Cabai" alt="Bibit Cabai"
class="img-fluid hero-image" class="img-fluid hero-image"
@ -93,7 +150,7 @@ class="img-fluid hero-image"
@if($featuredProducts->count() > 0) @if($featuredProducts->count() > 0)
<div class="row g-4"> <div class="row g-4">
@foreach($featuredProducts as $product) @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"> <div class="card product-card h-100 position-relative">
@if($product->total_sold > 0) @if($product->total_sold > 0)
<div class="badge-bestseller"> <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> <div class="price-text mb-3">Rp {{ number_format($product->price, 0, ',', '.') }}</div>
@if($product->total_sold > 0) @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> <i class="fas fa-chart-line me-1"></i>
Terjual: {{ number_format($product->total_sold) }} bibit Terjual: {{ number_format($product->total_sold) }} bibit
</small> </small>
@ -198,6 +255,7 @@ class="card-img-top product-image"
@endif @endif
</div> </div>
</section> </section>
<!-- Semua Produk Section --> <!-- Semua Produk Section -->
<section class="py-5 bg-white"> <section class="py-5 bg-white">
<div class="container"> <div class="container">
@ -208,7 +266,7 @@ class="card-img-top product-image"
<div class="row g-4"> <div class="row g-4">
@foreach($allProducts as $product) @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="card product-card h-100 position-relative">
<div class="overflow-hidden"> <div class="overflow-hidden">
<img src="{{ $product->image ? asset('storage/' . $product->image) : 'https://via.placeholder.com/300x250/28a745/ffffff?text=' . urlencode($product->name) }}" <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> </div>
</section> </section>
<!-- Features Section -->
<!-- Features Section --> <!-- Features Section -->
<section class="py-5 bg-white"> <section class="py-5 bg-white">
<div class="container"> <div class="container">
<div class="row g-4"> <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="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> <i class="fas fa-seedling fa-2x"></i>
</div> </div>
<h5>Bibit Berkualitas</h5> <h5>Bibit Berkualitas</h5>
<p class="text-muted">Bibit pilihan dengan kualitas terjamin dan hasil optimal.</p> <p class="text-muted">Bibit pilihan dengan kualitas terjamin dan hasil optimal.</p>
</div> </div>
</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="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> <i class="fas fa-truck fa-2x"></i>
</div> </div>
<h5>Pengiriman Cepat</h5> <h5>Pengiriman Cepat</h5>
<p class="text-muted">Pengiriman ke seluruh kabupaten Bondowoso dengan packaging aman.</p> <p class="text-muted">Pengiriman ke seluruh kabupaten Bondowoso dengan packaging aman.</p>
</div> </div>
</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="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-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;">
<i class="fas fa-headset fa-2x"></i> <i class="fas fa-headset fa-2x"></i>
</div> </div>
<h5>Support 24/7</h5> <h5>Support 24/7</h5>