admin-penyakit done

This commit is contained in:
E31232303evimr 2026-02-24 22:11:58 +07:00
parent e1f990c895
commit 9d860df6e8
45 changed files with 3122 additions and 53 deletions

View File

@ -0,0 +1,80 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\MasterGejala;
use App\Models\MasterPenyakit;
use App\Models\RiwayatDiagnosis;
use App\Models\InformasiBudidaya;
use App\Models\InformasiHamaPenyakit;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class AdminDashboardController extends Controller
{
public function index()
{
// Total Statistics
$totalGejala = MasterGejala::count();
$totalPenyakit = MasterPenyakit::count();
$totalDiagnosa = RiwayatDiagnosis::count();
$totalArtikel = InformasiBudidaya::count() + InformasiHamaPenyakit::count();
// Chart: Penyakit Paling Banyak Didiagnosa (Top 5)
$penyakitTerbanyak = RiwayatDiagnosis::select('penyakit_final', DB::raw('count(*) as total'))
->whereNotNull('penyakit_final')
->groupBy('penyakit_final')
->orderBy('total', 'desc')
->limit(5)
->get();
// Join dengan MasterPenyakit untuk ambil nama lengkap
$chartPenyakit = [
'labels' => $penyakitTerbanyak->map(function($item) {
// Cari nama penyakit berdasarkan kode
$penyakit = MasterPenyakit::where('kode_penyakit', $item->penyakit_final)->first();
return $penyakit ? $penyakit->nama_penyakit : $item->penyakit_final;
})->toArray(),
'data' => $penyakitTerbanyak->pluck('total')->toArray(),
];
// Chart: Trend Diagnosa Per Bulan (6 bulan terakhir)
$sixMonthsAgo = Carbon::now()->subMonths(6);
$diagnosaTrend = RiwayatDiagnosis::where('created_at', '>=', $sixMonthsAgo)
->select(
DB::raw('DATE_FORMAT(created_at, "%Y-%m") as bulan'),
DB::raw('count(*) as total')
)
->groupBy('bulan')
->orderBy('bulan')
->get();
// Generate all months for the last 6 months
$months = collect();
for ($i = 5; $i >= 0; $i--) {
$months->push(Carbon::now()->subMonths($i));
}
$chartDiagnosa = [
'labels' => $months->map(function($month) {
return $month->translatedFormat('M Y');
})->toArray(),
'data' => $months->map(function($month) use ($diagnosaTrend) {
$bulan = $month->format('Y-m');
$found = $diagnosaTrend->firstWhere('bulan', $bulan);
return $found ? $found->total : 0;
})->toArray(),
];
return view('admin.dashboard', compact(
'totalGejala',
'totalPenyakit',
'totalDiagnosa',
'totalArtikel',
'chartPenyakit',
'chartDiagnosa'
));
}
}

View File

@ -0,0 +1,146 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\InformasiBudidaya;
use App\Models\InformasiHamaPenyakit;
use Illuminate\Http\Request;
class ArtikelController extends Controller
{
/**
* Display a listing of artikel.
* Gabungan dari InformasiBudidaya dan InformasiHamaPenyakit
*/
public function index()
{
// Ambil artikel dari kedua tabel
$budidaya = InformasiBudidaya::latest()->get()->map(function($item) {
$item->kategori = 'Budidaya';
return $item;
});
$hamaPenyakit = InformasiHamaPenyakit::latest()->get()->map(function($item) {
$item->kategori = 'Hama & Penyakit';
return $item;
});
// Gabungkan dan paginate manual
$artikels = $budidaya->merge($hamaPenyakit)->sortByDesc('created_at');
// Convert ke paginator
$page = request()->get('page', 1);
$perPage = 10;
$artikels = new \Illuminate\Pagination\LengthAwarePaginator(
$artikels->forPage($page, $perPage),
$artikels->count(),
$perPage,
$page,
['path' => request()->url(), 'query' => request()->query()]
);
return view('admin.artikel.index', compact('artikels'));
}
/**
* Show the form for creating a new artikel.
*/
public function create()
{
return view('admin.artikel.create');
}
/**
* Store a newly created artikel.
*/
public function store(Request $request)
{
$validated = $request->validate([
'kategori' => ['required', 'in:budidaya,hama_penyakit'],
'judul' => ['required', 'string', 'max:255'],
'konten' => ['required', 'string'],
'gambar' => ['nullable', 'image', 'max:2048'],
]);
// Upload gambar jika ada
if ($request->hasFile('gambar')) {
$validated['gambar'] = $request->file('gambar')->store('artikel', 'public');
}
$validated['created_by'] = auth()->id();
// Simpan ke tabel yang sesuai
if ($validated['kategori'] === 'budidaya') {
InformasiBudidaya::create($validated);
} else {
InformasiHamaPenyakit::create($validated);
}
return redirect()->route('admin.artikel.index')
->with('success', 'Artikel berhasil ditambahkan!');
}
/**
* Show the form for editing artikel.
*/
public function edit($id)
{
// Cari di kedua tabel
$artikel = InformasiBudidaya::find($id);
$kategori = 'budidaya';
if (!$artikel) {
$artikel = InformasiHamaPenyakit::findOrFail($id);
$kategori = 'hama_penyakit';
}
return view('admin.artikel.edit', compact('artikel', 'kategori'));
}
/**
* Update the specified artikel.
*/
public function update(Request $request, $id)
{
$validated = $request->validate([
'kategori' => ['required', 'in:budidaya,hama_penyakit'],
'judul' => ['required', 'string', 'max:255'],
'konten' => ['required', 'string'],
'gambar' => ['nullable', 'image', 'max:2048'],
]);
// Upload gambar baru jika ada
if ($request->hasFile('gambar')) {
$validated['gambar'] = $request->file('gambar')->store('artikel', 'public');
}
// Update di tabel yang sesuai
if ($validated['kategori'] === 'budidaya') {
$artikel = InformasiBudidaya::findOrFail($id);
} else {
$artikel = InformasiHamaPenyakit::findOrFail($id);
}
$artikel->update($validated);
return redirect()->route('admin.artikel.index')
->with('success', 'Artikel berhasil diupdate!');
}
/**
* Remove the specified artikel.
*/
public function destroy($id)
{
// Coba hapus dari kedua tabel
$deleted = InformasiBudidaya::where('id', $id)->delete();
if (!$deleted) {
InformasiHamaPenyakit::where('id', $id)->delete();
}
return redirect()->route('admin.artikel.index')
->with('success', 'Artikel berhasil dihapus!');
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\MasterGejala;
use Illuminate\Http\Request;
class GejalaController extends Controller
{
/**
* Display a listing of gejala.
*/
public function index()
{
$gejalas = MasterGejala::latest()->paginate(10);
return view('admin.gejala.index', compact('gejalas'));
}
/**
* Show the form for creating a new gejala.
*/
public function create()
{
return view('admin.gejala.create');
}
/**
* Store a newly created gejala.
*/
public function store(Request $request)
{
$validated = $request->validate([
'kode_gejala' => ['required', 'string', 'max:10', 'unique:gejala,kode_gejala'],
'nama_gejala' => ['required', 'string', 'max:255'],
], [
'kode_gejala.unique' => 'Kode gejala sudah digunakan',
'kode_gejala.required' => 'Kode gejala wajib diisi',
'nama_gejala.required' => 'Nama gejala wajib diisi',
]);
MasterGejala::create($validated);
return redirect()->route('admin.gejala.index')
->with('success', 'Gejala berhasil ditambahkan!');
}
/**
* Display the specified gejala.
*/
public function show(MasterGejala $gejala)
{
return view('admin.gejala.show', compact('gejala'));
}
/**
* Show the form for editing gejala.
*/
public function edit(MasterGejala $gejala)
{
return view('admin.gejala.edit', compact('gejala'));
}
/**
* Update the specified gejala.
*/
public function update(Request $request, MasterGejala $gejala)
{
$validated = $request->validate([
'kode_gejala' => ['required', 'string', 'max:10', 'unique:gejala,kode_gejala,' . $gejala->id],
'nama_gejala' => ['required', 'string', 'max:255'],
], [
'kode_gejala.unique' => 'Kode gejala sudah digunakan',
]);
$gejala->update($validated);
return redirect()->route('admin.gejala.index')
->with('success', 'Gejala berhasil diupdate!');
}
/**
* Remove the specified gejala.
*/
public function destroy(MasterGejala $gejala)
{
$gejala->delete();
return redirect()->route('admin.gejala.index')
->with('success', 'Gejala berhasil dihapus!');
}
}

View File

@ -0,0 +1,156 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\MasterPenyakit;
use Illuminate\Http\Request;
class PenyakitController extends Controller
{
/**
* Display a listing of penyakit.
*/
public function index()
{
$penyakits = MasterPenyakit::orderByRaw('LENGTH(id_penyakit) ASC, id_penyakit ASC')->paginate(10);
return view('admin.penyakit.index', compact('penyakits'));
}
/**
* Show the form for creating a new penyakit.
*/
public function create()
{
return view('admin.penyakit.create');
}
/**
* Store a newly created penyakit.
*/
public function store(Request $request)
{
$validated = $request->validate([
'id_penyakit' => ['required', 'string', 'max:10', 'unique:master_penyakit,id_penyakit'],
'nama_penyakit' => ['required', 'string', 'max:255'],
'nama_latin' => ['nullable', 'string', 'max:255'],
'kategori' => ['required', 'in:Hama,Penyakit'],
'deskripsi_singkat' => ['nullable', 'string'],
'deskripsi_lengkap' => ['nullable', 'string'],
'pengendalian_pencegahan' => ['nullable', 'string'],
'pengendalian_kimia' => ['nullable', 'string'],
'pengendalian_organik' => ['nullable', 'string'],
'pengendalian_budidaya' => ['nullable', 'string'],
'tingkat_bahaya' => ['nullable', 'in:Rendah,Sedang,Tinggi,Sangat Tinggi'],
'gambar' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
], [
'id_penyakit.required' => 'Kode penyakit wajib diisi',
'id_penyakit.unique' => 'Kode penyakit sudah digunakan',
'nama_penyakit.required' => 'Nama penyakit wajib diisi',
'kategori.required' => 'Kategori wajib dipilih',
'kategori.in' => 'Kategori harus Hama atau Penyakit',
'tingkat_bahaya.in' => 'Tingkat bahaya tidak valid',
'gambar.image' => 'File harus berupa gambar',
'gambar.mimes' => 'Format gambar harus JPG, JPEG, PNG, atau WebP',
'gambar.max' => 'Ukuran gambar maksimal 2MB',
]);
// Handle file upload
if ($request->hasFile('gambar')) {
$file = $request->file('gambar');
$filename = time() . '_' . $file->getClientOriginalName();
$file->move(public_path('uploads/penyakit'), $filename);
$validated['gambar_url'] = '/uploads/penyakit/' . $filename;
}
MasterPenyakit::create($validated);
return redirect()->route('admin.penyakit.index')
->with('success', 'Penyakit berhasil ditambahkan!');
}
/**
* Display the specified penyakit.
*/
public function show(MasterPenyakit $penyakit)
{
return view('admin.penyakit.show', compact('penyakit'));
}
/**
* Show the form for editing penyakit.
*/
public function edit(MasterPenyakit $penyakit)
{
return view('admin.penyakit.edit', compact('penyakit'));
}
/**
* Update the specified penyakit.
*/
public function update(Request $request, MasterPenyakit $penyakit)
{
$validated = $request->validate([
'nama_penyakit' => ['required', 'string', 'max:255'],
'nama_latin' => ['nullable', 'string', 'max:255'],
'kategori' => ['required', 'in:Hama,Penyakit'],
'deskripsi_singkat' => ['nullable', 'string'],
'deskripsi_lengkap' => ['nullable', 'string'],
'pengendalian_pencegahan' => ['nullable', 'string'],
'pengendalian_kimia' => ['nullable', 'string'],
'pengendalian_organik' => ['nullable', 'string'],
'pengendalian_budidaya' => ['nullable', 'string'],
'tingkat_bahaya' => ['nullable', 'in:Rendah,Sedang,Tinggi,Sangat Tinggi'],
'gambar' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
], [
'nama_penyakit.required' => 'Nama penyakit wajib diisi',
'kategori.required' => 'Kategori wajib dipilih',
'kategori.in' => 'Kategori harus Hama atau Penyakit',
'tingkat_bahaya.in' => 'Tingkat bahaya tidak valid',
'gambar.image' => 'File harus berupa gambar',
'gambar.mimes' => 'Format gambar harus JPG, JPEG, PNG, atau WebP',
'gambar.max' => 'Ukuran gambar maksimal 2MB',
]);
// Handle file upload
if ($request->hasFile('gambar')) {
// Delete old image if exists
if ($penyakit->gambar_url && file_exists(public_path($penyakit->gambar_url))) {
unlink(public_path($penyakit->gambar_url));
}
// Store new image
$file = $request->file('gambar');
$filename = time() . '_' . $file->getClientOriginalName();
$file->move(public_path('uploads/penyakit'), $filename);
$validated['gambar_url'] = '/uploads/penyakit/' . $filename;
}
$penyakit->update($validated);
return redirect()->route('admin.penyakit.index')
->with('success', 'Data penyakit berhasil diupdate!');
}
/**
* Check if kode penyakit already exists (AJAX)
*/
public function checkKode(Request $request)
{
$kode = $request->query('kode');
$exists = MasterPenyakit::where('id_penyakit', $kode)->exists();
return response()->json(['exists' => $exists]);
}
/**
* Remove the specified penyakit.
*/
public function destroy(MasterPenyakit $penyakit)
{
$penyakit->delete();
return redirect()->route('admin.penyakit.index')
->with('success', 'Penyakit berhasil dihapus!');
}
}

View File

@ -0,0 +1,104 @@
<?php
namespace App\Http\Controllers\SuperAdmin;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
class UserManagementController extends Controller
{
/**
* Display a listing of users.
*/
public function index()
{
$users = User::orderBy('created_at', 'desc')->paginate(10);
return view('super-admin.users.index', compact('users'));
}
/**
* Show the form for creating a new user.
*/
public function create()
{
return view('super-admin.users.create');
}
/**
* Store a newly created user.
*/
public function store(Request $request)
{
$validated = $request->validate([
'username' => ['required', 'string', 'max:50', 'alpha_dash', 'unique:users,username'],
'nama' => ['required', 'string', 'max:100'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users,email'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'no_hp' => ['nullable', 'string', 'max:15'],
'role' => ['required', 'in:super_admin,admin,user'],
]);
$validated['password'] = Hash::make($validated['password']);
$validated['email_verified_at'] = now();
User::create($validated);
return redirect()->route('super-admin.users.index')
->with('success', 'User berhasil ditambahkan!');
}
/**
* Show the form for editing user.
*/
public function edit(User $user)
{
return view('super-admin.users.edit', compact('user'));
}
/**
* Update the specified user.
*/
public function update(Request $request, User $user)
{
$validated = $request->validate([
'username' => ['required', 'string', 'max:50', 'alpha_dash', Rule::unique('users')->ignore($user->id)],
'nama' => ['required', 'string', 'max:100'],
'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
'no_hp' => ['nullable', 'string', 'max:15'],
'role' => ['required', 'in:super_admin,admin,user'],
]);
// Update password hanya jika diisi
if ($request->filled('password')) {
$request->validate([
'password' => ['string', 'min:8', 'confirmed'],
]);
$validated['password'] = Hash::make($request->password);
}
$user->update($validated);
return redirect()->route('super-admin.users.index')
->with('success', 'User berhasil diupdate!');
}
/**
* Remove the specified user.
*/
public function destroy(User $user)
{
// Cegah super admin menghapus dirinya sendiri
if ($user->id === auth()->id()) {
return redirect()->route('super-admin.users.index')
->with('error', 'Anda tidak dapat menghapus akun sendiri!');
}
$user->delete();
return redirect()->route('super-admin.users.index')
->with('success', 'User berhasil dihapus!');
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class IsAdmin
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
// Cek apakah user sudah login
if (!auth()->check()) {
return redirect()->route('login')->with('error', 'Silakan login terlebih dahulu');
}
// Cek apakah user punya akses admin (admin ATAU super_admin)
if (!auth()->user()->hasAdminAccess()) {
abort(403, 'Akses ditolak. Hanya Admin yang diizinkan.');
}
return $next($request);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class IsSuperAdmin
{
public function handle(Request $request, Closure $next): Response
{
if (!auth()->check()) {
return redirect()->route('login')->with('error', 'Silakan login terlebih dahulu');
}
if (!auth()->user()->isSuperAdmin()) {
abort(403, 'Akses ditolak. Hanya Super Admin yang diizinkan.');
}
return $next($request);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
// Redirect berdasarkan role
$user = Auth::user();
if ($user->role === 'super_admin') {
return redirect('/super-admin/dashboard');
} elseif ($user->role === 'admin') {
return redirect('/admin/dashboard');
} else {
return redirect(RouteServiceProvider::HOME);
}
}
}
return $next($request);
}
}

View File

@ -2,77 +2,87 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
use Notifiable;
/**
* Kolom yang bisa diisi mass assignment
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'username',
'nama',
'email',
'password',
'no_hp',
'role',
'password',
];
/**
* Kolom yang di-hidden saat serialization (JSON)
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Casting tipe data
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'password' => 'hashed', // Laravel 10+ auto hash
];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
// ============================================
// RELATIONSHIPS
// ============================================
/**
* User punya banyak riwayat diagnosis
*/
public function riwayatDiagnosis()
{
return $this->hasMany(RiwayatDiagnosis::class, 'user_id');
}
/**
* Admin upload banyak artikel budidaya
*/
public function artikelBudidaya()
{
return $this->hasMany(InformasiBudidaya::class, 'created_by');
}
/**
* Admin upload banyak artikel hama/penyakit
*/
public function artikelHamaPenyakit()
{
return $this->hasMany(InformasiHamaPenyakit::class, 'created_by');
}
// ============================================
// HELPER METHODS
// ROLE CHECKER HELPER METHODS
// ============================================
/**
* Cek apakah user adalah admin
* Cek apakah user adalah Super Admin
*
* @return bool
*/
public function isSuperAdmin()
{
return $this->role === 'super_admin';
}
/**
* Cek apakah user adalah Admin (TETAP SEPERTI SEBELUMNYA)
*
* @return bool
*/
public function isAdmin()
{
@ -80,7 +90,9 @@ public function isAdmin()
}
/**
* Cek apakah user biasa
* Cek apakah user biasa (TETAP SEPERTI SEBELUMNYA)
*
* @return bool
*/
public function isUser()
{
@ -88,11 +100,53 @@ public function isUser()
}
/**
* Get display name
* (Karena Laravel default cari 'name', kita redirect ke 'nama')
* Cek apakah user punya akses admin (Super Admin ATAU Admin)
* Helper baru untuk middleware yang butuh cek "apakah user ini admin atau super admin"
*
* @return bool
*/
public function getNameAttribute()
public function hasAdminAccess()
{
return $this->nama;
return in_array($this->role, ['super_admin', 'admin']);
}
/**
* Cek apakah user punya akses super admin (alias dari isSuperAdmin)
*
* @return bool
*/
public function hasSuperAdminAccess()
{
return $this->isSuperAdmin();
}
/**
* Get role display name (untuk tampilan)
*
* @return string
*/
public function getRoleDisplayName()
{
return match($this->role) {
'super_admin' => 'Super Administrator',
'admin' => 'Administrator',
'user' => 'User',
default => 'Unknown',
};
}
/**
* Get role badge color (untuk UI)
*
* @return string
*/
public function getRoleBadgeColor()
{
return match($this->role) {
'super_admin' => 'red',
'admin' => 'blue',
'user' => 'gray',
default => 'gray',
};
}
}

View File

@ -11,7 +11,10 @@
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
//
$middleware->alias([
'admin' => \App\Http\Middleware\IsAdmin::class,
'super_admin' => \App\Http\Middleware\IsSuperAdmin::class,
]);
})
->withExceptions(function (Exceptions $exceptions): void {
//

View File

@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
public function up(): void
{
// Ubah ENUM untuk tambah 'super_admin'
DB::statement("ALTER TABLE users MODIFY COLUMN role ENUM('super_admin', 'admin', 'user') DEFAULT 'user'");
}
public function down(): void
{
// Rollback ke ENUM lama
DB::statement("ALTER TABLE users MODIFY COLUMN role ENUM('admin', 'user') DEFAULT 'user'");
}
};

View File

@ -0,0 +1,54 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// Step 1: Drop foreign key constraint sementara
Schema::table('riwayat_diagnosis', function (Blueprint $table) {
$table->dropForeign('riwayat_diagnosis_penyakit_final_foreign');
});
// Step 2: Set id_penyakit jadi AUTO_INCREMENT
DB::statement('ALTER TABLE master_penyakit MODIFY id_penyakit BIGINT UNSIGNED NOT NULL AUTO_INCREMENT');
// Step 3: Ubah penyakit_final dari VARCHAR(10) jadi BIGINT UNSIGNED
DB::statement('ALTER TABLE riwayat_diagnosis MODIFY penyakit_final BIGINT UNSIGNED NULL');
// Step 4: Buat foreign key lagi dengan tipe data yang benar
Schema::table('riwayat_diagnosis', function (Blueprint $table) {
$table->foreign('penyakit_final')
->references('id_penyakit')
->on('master_penyakit')
->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Rollback: Drop foreign key
Schema::table('riwayat_diagnosis', function (Blueprint $table) {
$table->dropForeign(['penyakit_final']);
});
// Kembalikan penyakit_final jadi VARCHAR(10)
DB::statement('ALTER TABLE riwayat_diagnosis MODIFY penyakit_final VARCHAR(10) NULL');
// Hapus AUTO_INCREMENT dari id_penyakit
DB::statement('ALTER TABLE master_penyakit MODIFY id_penyakit BIGINT UNSIGNED NOT NULL');
// Buat foreign key lagi ke kode_penyakit (struktur lama)
// Note: Ini opsional, sesuaikan dengan struktur database awal kamu
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,101 @@
@extends('layouts.admin-app')
@section('page-title', '📰 Manajemen Artikel')
@section('page-subtitle', 'Kelola artikel edukasi')
@section('content')
@if (session('success'))
<div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg flex items-center">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="font-semibold">{{ session('success') }}</span>
</div>
@endif
<div class="bg-white rounded-2xl shadow-lg">
<div class="p-8">
<div class="flex justify-between items-center mb-6">
<div class="flex items-center">
<div class="bg-gradient-to-r from-yellow-500 to-yellow-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Daftar Artikel Edukasi</h3>
</div>
<a href="{{ route('admin.artikel.create') }}" class="flex items-center px-5 py-3 bg-gradient-to-r from-yellow-500 to-yellow-600 text-white font-bold rounded-xl hover:from-yellow-600 hover:to-yellow-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
Tambah Artikel
</a>
</div>
<div class="overflow-x-auto rounded-xl border border-gray-200">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">No</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Judul</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Kategori</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Penulis</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Tanggal</th>
<th class="px-6 py-4 text-center text-xs font-bold text-gray-600 uppercase">Aksi</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse ($artikels as $index => $artikel)
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 text-sm">{{ $artikels->firstItem() + $index }}</td>
<td class="px-6 py-4">
<div class="text-sm font-semibold text-gray-900">{{ $artikel->judul }}</div>
</td>
<td class="px-6 py-4">
<span class="px-3 py-1 text-xs font-bold rounded-full bg-yellow-100 text-yellow-800">
{{ $artikel->kategori }}
</span>
</td>
<td class="px-6 py-4 text-sm text-gray-600">{{ $artikel->penulis ?? 'Admin' }}</td>
<td class="px-6 py-4 text-sm text-gray-600">{{ $artikel->created_at->format('d M Y') }}</td>
<td class="px-6 py-4 text-center">
<div class="flex justify-center space-x-2">
<a href="{{ route('admin.artikel.edit', $artikel) }}" class="inline-flex items-center px-3 py-2 bg-blue-500 text-white text-xs font-semibold rounded-lg hover:bg-blue-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
Edit
</a>
<form action="{{ route('admin.artikel.destroy', $artikel) }}" method="POST" onsubmit="return confirm('Yakin ingin menghapus artikel ini?')">
@csrf
@method('DELETE')
<button type="submit" class="inline-flex items-center px-3 py-2 bg-red-500 text-white text-xs font-semibold rounded-lg hover:bg-red-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-6 py-12 text-center">
<div class="text-gray-400">
<svg class="w-16 h-16 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
</svg>
<p class="text-xl font-semibold mb-2">Belum ada artikel</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-6">{{ $artikels->links() }}</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,148 @@
@extends('layouts.admin-app')
@section('page-title', '📊 Dashboard')
@section('page-subtitle', 'Overview statistik sistem pakar')
@section('content')
<!-- Welcome Banner -->
<div class="bg-gradient-to-r from-green-600 via-green-700 to-green-800 rounded-2xl p-8 mb-6 text-white relative overflow-hidden shadow-xl">
<div class="absolute right-0 top-0 w-64 h-64 bg-white opacity-5 rounded-full -mr-32 -mt-32"></div>
<div class="absolute right-10 bottom-0 w-48 h-48 bg-white opacity-5 rounded-full -mb-24"></div>
<div class="relative z-10">
<h1 class="text-3xl font-bold mb-2">Selamat Datang Kembali! 👋</h1>
<p class="text-green-100 text-lg">{{ Auth::user()->nama }}</p>
<p class="text-green-50 mt-4">{{ now()->translatedFormat('l, d F Y') }}</p>
</div>
</div>
<!-- Statistics Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
<!-- Total Gejala -->
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">Total Gejala</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ $totalGejala }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-green-400 to-green-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
</svg>
</div>
</div>
</div>
<!-- Total Penyakit -->
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">Total Penyakit</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ $totalPenyakit }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-red-400 to-red-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
</div>
</div>
<!-- Total Diagnosa -->
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">Total Diagnosa</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ $totalDiagnosa }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-purple-400 to-purple-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
</div>
</div>
</div>
<!-- Total Artikel -->
<div class="bg-white rounded-2xl p-6 shadow-lg hover:shadow-xl transition-shadow">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase">Total Artikel</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ $totalArtikel }}</h3>
</div>
<div class="w-14 h-14 bg-gradient-to-br from-yellow-400 to-yellow-600 rounded-2xl flex items-center justify-center shadow-lg">
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"></path>
</svg>
</div>
</div>
</div>
</div>
<!-- Charts Section -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Penyakit Chart -->
<div class="bg-white rounded-2xl p-6 shadow-lg">
<h3 class="text-xl font-bold text-gray-800 mb-4">Penyakit Paling Sering Didiagnosa</h3>
<div style="height: 300px;">
<canvas id="penyakitChart"></canvas>
</div>
</div>
<!-- Diagnosa Trend Chart -->
<div class="bg-white rounded-2xl p-6 shadow-lg">
<h3 class="text-xl font-bold text-gray-800 mb-4">Trend Diagnosa (6 Bulan Terakhir)</h3>
<div style="height: 300px;">
<canvas id="diagnosaChart"></canvas>
</div>
</div>
</div>
<!-- Chart.js Script -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
// Penyakit Bar Chart
const penyakitData = @json($chartPenyakit);
const penyakitCtx = document.getElementById('penyakitChart').getContext('2d');
new Chart(penyakitCtx, {
type: 'bar',
data: {
labels: penyakitData.labels,
datasets: [{
label: 'Jumlah Diagnosa',
data: penyakitData.data,
backgroundColor: ['rgba(239, 68, 68, 0.8)', 'rgba(249, 115, 22, 0.8)', 'rgba(234, 179, 8, 0.8)', 'rgba(34, 197, 94, 0.8)', 'rgba(59, 130, 246, 0.8)'],
borderRadius: 8,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } }
}
});
// Diagnosa Line Chart
const diagnosaData = @json($chartDiagnosa);
const diagnosaCtx = document.getElementById('diagnosaChart').getContext('2d');
new Chart(diagnosaCtx, {
type: 'line',
data: {
labels: diagnosaData.labels,
datasets: [{
label: 'Jumlah Diagnosa',
data: diagnosaData.data,
borderColor: 'rgb(95, 163, 87)',
backgroundColor: 'rgba(95, 163, 87, 0.1)',
borderWidth: 3,
fill: true,
tension: 0.4,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } }
}
});
</script>
@endsection

View File

@ -0,0 +1,97 @@
@extends('layouts.admin-app')
@section('page-title', '✅ Manajemen Gejala')
@section('page-subtitle', 'Kelola data gejala penyakit')
@section('content')
@if (session('success'))
<div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg flex items-center">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="font-semibold">{{ session('success') }}</span>
</div>
@endif
<div class="bg-white rounded-2xl shadow-lg">
<div class="p-8">
<div class="flex justify-between items-center mb-6">
<div class="flex items-center">
<div class="bg-gradient-to-r from-green-500 to-green-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Daftar Gejala Penyakit</h3>
</div>
<a href="{{ route('admin.gejala.create') }}" class="flex items-center px-5 py-3 bg-gradient-to-r from-green-500 to-green-600 text-white font-bold rounded-xl hover:from-green-600 hover:to-green-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
Tambah Gejala
</a>
</div>
<div class="overflow-x-auto rounded-xl border border-gray-200">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">No</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Kode</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase">Nama Gejala</th>
<th class="px-6 py-4 text-center text-xs font-bold text-gray-600 uppercase">Aksi</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse ($gejalas as $index => $gejala)
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 text-sm text-gray-900">{{ $gejalas->firstItem() + $index }}</td>
<td class="px-6 py-4">
<span class="px-3 py-1 text-xs font-bold rounded-full bg-green-100 text-green-800">
{{ $gejala->kode_gejala }}
</span>
</td>
<td class="px-6 py-4">
<div class="text-sm font-semibold text-gray-900">{{ $gejala->nama_gejala }}</div>
</td>
<td class="px-6 py-4 text-center">
<div class="flex justify-center space-x-2">
<a href="{{ route('admin.gejala.edit', $gejala) }}" class="inline-flex items-center px-3 py-2 bg-blue-500 text-white text-xs font-semibold rounded-lg hover:bg-blue-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
Edit
</a>
<form action="{{ route('admin.gejala.destroy', $gejala) }}" method="POST" onsubmit="return confirm('Yakin ingin menghapus gejala ini?')">
@csrf
@method('DELETE')
<button type="submit" class="inline-flex items-center px-3 py-2 bg-red-500 text-white text-xs font-semibold rounded-lg hover:bg-red-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-12 text-center">
<div class="text-gray-400">
<svg class="w-16 h-16 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
</svg>
<p class="text-xl font-semibold mb-2">Belum ada data gejala</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-6">{{ $gejalas->links() }}</div>
</div>
</div>
@endsection

View File

@ -0,0 +1,274 @@
@extends('layouts.admin-app')
@section('page-title', ' Tambah Penyakit')
@section('page-subtitle', 'Tambah data penyakit tanaman kopi baru')
@section('content')
@if ($errors->any())
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg">
<div class="flex items-start">
<svg class="w-6 h-6 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<div>
<p class="font-semibold mb-2">Terdapat kesalahan:</p>
<ul class="list-disc list-inside space-y-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<form action="{{ route('admin.penyakit.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header -->
<div class="p-6 border-b border-gray-200">
<div class="flex items-center">
<div class="bg-gradient-to-r from-green-500 to-green-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Form Tambah Penyakit Baru</h3>
</div>
</div>
<!-- Form Body -->
<div class="p-8 space-y-6">
<!-- ID Penyakit -->
<div>
<label for="id_penyakit" class="block text-sm font-semibold text-gray-700 mb-2">
Kode Penyakit <span class="text-red-500">*</span>
</label>
<input
type="text"
name="id_penyakit"
id="id_penyakit"
value="{{ old('id_penyakit') }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition @error('id_penyakit') border-red-500 @enderror"
placeholder="Contoh: HP001 atau P001"
required
maxlength="10"
>
<p class="mt-1 text-sm text-gray-500">Format: HP001 (Hama) atau P001 (Penyakit). Maksimal 10 karakter.</p>
@error('id_penyakit')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Nama Penyakit -->
<div>
<label for="nama_penyakit" class="block text-sm font-semibold text-gray-700 mb-2">
Nama Penyakit <span class="text-red-500">*</span>
</label>
<input
type="text"
name="nama_penyakit"
id="nama_penyakit"
value="{{ old('nama_penyakit') }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition @error('nama_penyakit') border-red-500 @enderror"
placeholder="Contoh: Penggerek Buah Kopi"
required
>
@error('nama_penyakit')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Nama Latin -->
<div>
<label for="nama_latin" class="block text-sm font-semibold text-gray-700 mb-2">
Nama Latin
</label>
<input
type="text"
name="nama_latin"
id="nama_latin"
value="{{ old('nama_latin') }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="Contoh: Hypothenemus hampei"
>
</div>
<!-- Kategori -->
<div>
<label for="kategori" class="block text-sm font-semibold text-gray-700 mb-2">
Kategori <span class="text-red-500">*</span>
</label>
<select
name="kategori"
id="kategori"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
required
>
<option value="">-- Pilih Kategori --</option>
<option value="Hama" {{ old('kategori') == 'Hama' ? 'selected' : '' }}>🐛 Hama</option>
<option value="Penyakit" {{ old('kategori') == 'Penyakit' ? 'selected' : '' }}>🦠 Penyakit</option>
</select>
@error('kategori')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Deskripsi Singkat -->
<div>
<label for="deskripsi_singkat" class="block text-sm font-semibold text-gray-700 mb-2">
Deskripsi Singkat
</label>
<textarea
name="deskripsi_singkat"
id="deskripsi_singkat"
rows="3"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="Ringkasan singkat tentang penyakit/hama ini..."
>{{ old('deskripsi_singkat') }}</textarea>
</div>
<!-- Deskripsi Lengkap -->
<div>
<label for="deskripsi_lengkap" class="block text-sm font-semibold text-gray-700 mb-2">
Deskripsi Lengkap
</label>
<textarea
name="deskripsi_lengkap"
id="deskripsi_lengkap"
rows="5"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="Penjelasan detail tentang penyakit/hama, gejala, dampak, dll..."
>{{ old('deskripsi_lengkap') }}</textarea>
</div>
<!-- Pengendalian Pencegahan -->
<div>
<label for="pengendalian_pencegahan" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Pencegahan
</label>
<textarea
name="pengendalian_pencegahan"
id="pengendalian_pencegahan"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="Cara pencegahan sebelum terjadi serangan..."
>{{ old('pengendalian_pencegahan') }}</textarea>
</div>
<!-- Pengendalian Kimia -->
<div>
<label for="pengendalian_kimia" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Kimia
</label>
<textarea
name="pengendalian_kimia"
id="pengendalian_kimia"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="Pestisida atau bahan kimia yang dapat digunakan..."
>{{ old('pengendalian_kimia') }}</textarea>
</div>
<!-- Pengendalian Organik -->
<div>
<label for="pengendalian_organik" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Organik
</label>
<textarea
name="pengendalian_organik"
id="pengendalian_organik"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="Cara pengendalian dengan bahan organik/alami..."
>{{ old('pengendalian_organik') }}</textarea>
</div>
<!-- Pengendalian Budidaya -->
<div>
<label for="pengendalian_budidaya" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Budidaya
</label>
<textarea
name="pengendalian_budidaya"
id="pengendalian_budidaya"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="Teknik budidaya untuk mengendalikan hama/penyakit..."
>{{ old('pengendalian_budidaya') }}</textarea>
</div>
<!-- Tingkat Bahaya -->
<div>
<label for="tingkat_bahaya" class="block text-sm font-semibold text-gray-700 mb-2">
Tingkat Bahaya
</label>
<select
name="tingkat_bahaya"
id="tingkat_bahaya"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
>
<option value="">-- Pilih Tingkat Bahaya --</option>
<option value="Rendah" {{ old('tingkat_bahaya') == 'Rendah' ? 'selected' : '' }}>Rendah</option>
<option value="Sedang" {{ old('tingkat_bahaya') == 'Sedang' ? 'selected' : '' }}>Sedang</option>
<option value="Tinggi" {{ old('tingkat_bahaya') == 'Tinggi' ? 'selected' : '' }}>Tinggi</option>
<option value="Sangat Tinggi" {{ old('tingkat_bahaya') == 'Sangat Tinggi' ? 'selected' : '' }}>Sangat Tinggi</option>
</select>
</div>
<!-- Upload Gambar -->
<div>
<label for="gambar" class="block text-sm font-semibold text-gray-700 mb-2">
Upload Gambar
</label>
<input
type="file"
name="gambar"
id="gambar"
accept="image/*"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-green-50 file:text-green-700 hover:file:bg-green-100"
onchange="previewImage(event)"
>
<p class="mt-2 text-sm text-gray-500">Format: JPG, PNG, WebP. Maksimal 2MB</p>
<!-- Preview Gambar -->
<div id="imagePreview" class="mt-3 hidden">
<p class="text-sm text-gray-600 mb-2">Preview gambar:</p>
<img id="preview" src="" alt="Preview" class="w-32 h-32 object-cover rounded-lg shadow-sm">
</div>
</div>
</div>
<!-- Footer Buttons -->
<div class="px-8 py-6 bg-gray-50 border-t border-gray-200 flex justify-between items-center rounded-b-2xl">
<a href="{{ route('admin.penyakit.index') }}" class="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-xl hover:bg-gray-400 transition">
Batal
</a>
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-green-500 to-green-600 text-white font-bold rounded-xl hover:from-green-600 hover:to-green-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"></path>
</svg>
Simpan Data
</button>
</div>
</div>
</form>
<!-- JavaScript for Image Preview -->
<script>
function previewImage(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('preview').src = e.target.result;
document.getElementById('imagePreview').classList.remove('hidden');
}
reader.readAsDataURL(file);
}
}
</script>
@endsection

View File

@ -0,0 +1,280 @@
@extends('layouts.admin-app')
@section('page-title', '✏️ Edit Penyakit')
@section('page-subtitle', 'Edit data penyakit tanaman kopi')
@section('content')
@if ($errors->any())
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg">
<div class="flex items-start">
<svg class="w-6 h-6 mr-3 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<div>
<p class="font-semibold mb-2">Terdapat kesalahan:</p>
<ul class="list-disc list-inside space-y-1">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<form action="{{ route('admin.penyakit.update', $penyakit) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header -->
<div class="p-6 border-b border-gray-200">
<div class="flex items-center">
<div class="bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Form Edit Penyakit</h3>
</div>
</div>
<!-- Form Body -->
<div class="p-8 space-y-6">
<!-- ID Penyakit (Read-only) -->
<div>
<label for="id_penyakit" class="block text-sm font-semibold text-gray-700 mb-2">
Kode Penyakit
</label>
<input
type="text"
name="id_penyakit"
id="id_penyakit"
value="{{ $penyakit->id_penyakit }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl bg-gray-100 cursor-not-allowed"
readonly
disabled
>
<p class="mt-1 text-sm text-gray-500">Kode penyakit tidak dapat diubah setelah dibuat.</p>
</div>
<!-- Nama Penyakit -->
<div>
<label for="nama_penyakit" class="block text-sm font-semibold text-gray-700 mb-2">
Nama Penyakit <span class="text-red-500">*</span>
</label>
<input
type="text"
name="nama_penyakit"
id="nama_penyakit"
value="{{ old('nama_penyakit', $penyakit->nama_penyakit) }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition @error('nama_penyakit') border-red-500 @enderror"
placeholder="Contoh: Penggerek Buah Kopi"
required
>
@error('nama_penyakit')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Nama Latin -->
<div>
<label for="nama_latin" class="block text-sm font-semibold text-gray-700 mb-2">
Nama Latin
</label>
<input
type="text"
name="nama_latin"
id="nama_latin"
value="{{ old('nama_latin', $penyakit->nama_latin) }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Contoh: Hypothenemus hampei"
>
</div>
<!-- Kategori -->
<div>
<label for="kategori" class="block text-sm font-semibold text-gray-700 mb-2">
Kategori <span class="text-red-500">*</span>
</label>
<select
name="kategori"
id="kategori"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
required
>
<option value="">-- Pilih Kategori --</option>
<option value="Hama" {{ old('kategori', $penyakit->kategori) == 'Hama' ? 'selected' : '' }}>Hama</option>
<option value="Penyakit" {{ old('kategori', $penyakit->kategori) == 'Penyakit' ? 'selected' : '' }}>Penyakit</option>
</select>
@error('kategori')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Deskripsi Singkat -->
<div>
<label for="deskripsi_singkat" class="block text-sm font-semibold text-gray-700 mb-2">
Deskripsi Singkat
</label>
<textarea
name="deskripsi_singkat"
id="deskripsi_singkat"
rows="3"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Ringkasan singkat tentang penyakit/hama ini..."
>{{ old('deskripsi_singkat', $penyakit->deskripsi_singkat) }}</textarea>
</div>
<!-- Deskripsi Lengkap -->
<div>
<label for="deskripsi_lengkap" class="block text-sm font-semibold text-gray-700 mb-2">
Deskripsi Lengkap
</label>
<textarea
name="deskripsi_lengkap"
id="deskripsi_lengkap"
rows="5"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Penjelasan detail tentang penyakit/hama, gejala, dampak, dll..."
>{{ old('deskripsi_lengkap', $penyakit->deskripsi_lengkap) }}</textarea>
</div>
<!-- Pengendalian Pencegahan -->
<div>
<label for="pengendalian_pencegahan" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Pencegahan
</label>
<textarea
name="pengendalian_pencegahan"
id="pengendalian_pencegahan"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Cara pencegahan sebelum terjadi serangan..."
>{{ old('pengendalian_pencegahan', $penyakit->pengendalian_pencegahan) }}</textarea>
</div>
<!-- Pengendalian Kimia -->
<div>
<label for="pengendalian_kimia" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Kimia
</label>
<textarea
name="pengendalian_kimia"
id="pengendalian_kimia"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Pestisida atau bahan kimia yang dapat digunakan..."
>{{ old('pengendalian_kimia', $penyakit->pengendalian_kimia) }}</textarea>
</div>
<!-- Pengendalian Organik -->
<div>
<label for="pengendalian_organik" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Organik
</label>
<textarea
name="pengendalian_organik"
id="pengendalian_organik"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Cara pengendalian dengan bahan organik/alami..."
>{{ old('pengendalian_organik', $penyakit->pengendalian_organik) }}</textarea>
</div>
<!-- Pengendalian Budidaya -->
<div>
<label for="pengendalian_budidaya" class="block text-sm font-semibold text-gray-700 mb-2">
Pengendalian Budidaya
</label>
<textarea
name="pengendalian_budidaya"
id="pengendalian_budidaya"
rows="4"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
placeholder="Teknik budidaya untuk mengendalikan hama/penyakit..."
>{{ old('pengendalian_budidaya', $penyakit->pengendalian_budidaya) }}</textarea>
</div>
<!-- Tingkat Bahaya -->
<div>
<label for="tingkat_bahaya" class="block text-sm font-semibold text-gray-700 mb-2">
Tingkat Bahaya
</label>
<select
name="tingkat_bahaya"
id="tingkat_bahaya"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
>
<option value="">-- Pilih Tingkat Bahaya --</option>
<option value="Rendah" {{ old('tingkat_bahaya', $penyakit->tingkat_bahaya) == 'Rendah' ? 'selected' : '' }}>Rendah</option>
<option value="Sedang" {{ old('tingkat_bahaya', $penyakit->tingkat_bahaya) == 'Sedang' ? 'selected' : '' }}>Sedang</option>
<option value="Tinggi" {{ old('tingkat_bahaya', $penyakit->tingkat_bahaya) == 'Tinggi' ? 'selected' : '' }}>Tinggi</option>
<option value="Sangat Tinggi" {{ old('tingkat_bahaya', $penyakit->tingkat_bahaya) == 'Sangat Tinggi' ? 'selected' : '' }}>Sangat Tinggi</option>
</select>
</div>
<!-- Upload Gambar -->
<div>
<label for="gambar" class="block text-sm font-semibold text-gray-700 mb-2">
Upload Gambar
</label>
<input
type="file"
name="gambar"
id="gambar"
accept="image/*"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
onchange="previewImage(event)"
>
<p class="mt-2 text-sm text-gray-500">Format: JPG, PNG, WebP. Maksimal 2MB</p>
<!-- Preview Gambar Baru -->
<div id="imagePreview" class="mt-3 hidden">
<p class="text-sm text-gray-600 mb-2">Preview gambar baru:</p>
<img id="preview" src="" alt="Preview" class="w-32 h-32 object-cover rounded-lg shadow-sm">
</div>
<!-- Preview Gambar Lama -->
@if($penyakit->gambar_url)
<div class="mt-3">
<p class="text-sm text-gray-600 mb-2">Gambar saat ini:</p>
<img src="{{ $penyakit->gambar_url }}" alt="Current" class="w-32 h-32 object-cover rounded-lg shadow-sm">
<p class="text-xs text-gray-500 mt-2">Upload gambar baru untuk mengganti</p>
</div>
@endif
</div>
</div>
<!-- Footer Buttons -->
<div class="px-8 py-6 bg-gray-50 border-t border-gray-200 flex justify-between items-center rounded-b-2xl">
<a href="{{ route('admin.penyakit.index') }}" class="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-xl hover:bg-gray-400 transition">
Batal
</a>
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white font-bold rounded-xl hover:from-blue-600 hover:to-blue-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"></path>
</svg>
Update Data
</button>
</div>
</div>
</form>
<!-- JavaScript for Image Preview -->
<script>
function previewImage(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('preview').src = e.target.result;
document.getElementById('imagePreview').classList.remove('hidden');
}
reader.readAsDataURL(file);
}
}
</script>
@endsection

View File

@ -0,0 +1,266 @@
@extends('layouts.admin-app')
@section('page-title', '🦠 Manajemen Penyakit')
@section('page-subtitle', 'Kelola data penyakit tanaman kopi')
@section('content')
@if (session('success'))
<div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg flex items-center" role="alert">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="font-semibold">{{ session('success') }}</span>
</div>
@endif
@if (session('error'))
<div class="mb-6 bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded-lg flex items-center" role="alert">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
<span class="font-semibold">{{ session('error') }}</span>
</div>
@endif
<div class="bg-white rounded-2xl shadow-lg">
<!-- Header Section - Normal (No Sticky) -->
<div class="p-6 border-b border-gray-200">
<div class="flex justify-between items-center">
<div class="flex items-center">
<div class="bg-gradient-to-r from-red-500 to-red-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
<h3 class="text-xl font-bold text-gray-800">Daftar Penyakit & Hama Tanaman Kopi</h3>
</div>
<a href="{{ route('admin.penyakit.create') }}" class="flex items-center px-5 py-3 bg-gradient-to-r from-red-500 to-red-600 text-white font-bold rounded-xl hover:from-red-600 hover:to-red-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
Tambah Data
</a>
</div>
</div>
<!-- Table Section with Horizontal Scroll Only -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">No</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Kode</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Gambar</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Nama Penyakit</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Nama Latin</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Kategori</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Deskripsi Singkat</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Deskripsi Lengkap</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Pengendalian Pencegahan</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Pengendalian Kimia</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Pengendalian Organik</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Pengendalian Budidaya</th>
<th class="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase">Tingkat Bahaya</th>
<th class="px-4 py-3 text-center text-xs font-bold text-gray-600 uppercase">Aksi</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse ($penyakits as $index => $penyakit)
<tr class="hover:bg-gray-50 transition-colors">
<!-- No -->
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-900 font-semibold">
{{ $penyakits->firstItem() + $index }}
</td>
<!-- Kode Penyakit -->
<td class="px-4 py-4 whitespace-nowrap">
<span class="px-3 py-1 text-xs font-bold rounded-full bg-gray-800 text-white">
{{ $penyakit->id_penyakit }}
</span>
</td>
<!-- Gambar Preview -->
<td class="px-4 py-4">
@if($penyakit->gambar_url)
<img
src="{{ $penyakit->gambar_url }}"
alt="{{ $penyakit->nama_penyakit }}"
class="w-16 h-16 object-cover rounded-lg shadow-sm cursor-pointer hover:scale-110 transition-transform"
onclick="openModal('{{ $penyakit->gambar_url }}', '{{ $penyakit->nama_penyakit }}')"
>
@else
<div class="w-16 h-16 bg-gray-200 rounded-lg flex items-center justify-center">
<svg class="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>
</div>
@endif
</td>
<!-- Nama Penyakit -->
<td class="px-4 py-4">
<div class="text-sm font-bold text-gray-900 min-w-[150px]">{{ $penyakit->nama_penyakit }}</div>
</td>
<!-- Nama Latin -->
<td class="px-4 py-4">
<div class="text-sm text-gray-600 italic min-w-[120px]">{{ $penyakit->nama_latin ?? '-' }}</div>
</td>
<!-- Kategori -->
<td class="px-4 py-4 whitespace-nowrap">
@if($penyakit->kategori === 'Penyakit')
<span class="px-3 py-1 text-xs font-bold rounded-full bg-red-100 text-red-800">
🦠 Penyakit
</span>
@elseif($penyakit->kategori === 'Hama')
<span class="px-3 py-1 text-xs font-bold rounded-full bg-orange-100 text-orange-800">
🐛 Hama
</span>
@else
<span class="px-3 py-1 text-xs font-bold rounded-full bg-gray-100 text-gray-800">
{{ $penyakit->kategori ?? '-' }}
</span>
@endif
</td>
<!-- Deskripsi Singkat -->
<td class="px-4 py-4">
<div class="text-sm text-gray-600 max-w-xs">{{ Str::limit($penyakit->deskripsi_singkat ?? '-', 80) }}</div>
</td>
<!-- Deskripsi Lengkap -->
<td class="px-4 py-4">
<div class="text-sm text-gray-600 max-w-xs">{{ Str::limit($penyakit->deskripsi_lengkap ?? '-', 80) }}</div>
</td>
<!-- Pengendalian Pencegahan -->
<td class="px-4 py-4">
<div class="text-sm text-gray-600 max-w-xs">{{ Str::limit($penyakit->pengendalian_pencegahan ?? '-', 60) }}</div>
</td>
<!-- Pengendalian Kimia -->
<td class="px-4 py-4">
<div class="text-sm text-gray-600 max-w-xs">{{ Str::limit($penyakit->pengendalian_kimia ?? '-', 60) }}</div>
</td>
<!-- Pengendalian Organik -->
<td class="px-4 py-4">
<div class="text-sm text-gray-600 max-w-xs">{{ Str::limit($penyakit->pengendalian_organik ?? '-', 60) }}</div>
</td>
<!-- Pengendalian Budidaya -->
<td class="px-4 py-4">
<div class="text-sm text-gray-600 max-w-xs">{{ Str::limit($penyakit->pengendalian_budidaya ?? '-', 60) }}</div>
</td>
<!-- Tingkat Bahaya -->
<td class="px-4 py-4 whitespace-nowrap">
@if($penyakit->tingkat_bahaya === 'Sangat Tinggi')
<span class="px-2 py-1 text-xs font-bold rounded-full bg-red-600 text-white">{{ $penyakit->tingkat_bahaya }}</span>
@elseif($penyakit->tingkat_bahaya === 'Tinggi')
<span class="px-2 py-1 text-xs font-bold rounded-full bg-orange-500 text-white">{{ $penyakit->tingkat_bahaya }}</span>
@elseif($penyakit->tingkat_bahaya === 'Sedang')
<span class="px-2 py-1 text-xs font-bold rounded-full bg-yellow-500 text-white">{{ $penyakit->tingkat_bahaya }}</span>
@elseif($penyakit->tingkat_bahaya === 'Rendah')
<span class="px-2 py-1 text-xs font-bold rounded-full bg-green-500 text-white">{{ $penyakit->tingkat_bahaya }}</span>
@else
<span class="text-sm text-gray-500">{{ $penyakit->tingkat_bahaya ?? '-' }}</span>
@endif
</td>
<!-- Aksi -->
<td class="px-4 py-4 whitespace-nowrap text-center">
<div class="flex justify-center space-x-2">
<a href="{{ route('admin.penyakit.edit', $penyakit) }}" class="inline-flex items-center px-3 py-2 bg-blue-500 text-white text-xs font-semibold rounded-lg hover:bg-blue-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
</svg>
Edit
</a>
<form action="{{ route('admin.penyakit.destroy', $penyakit) }}" method="POST" class="inline" onsubmit="return confirm('Yakin ingin menghapus data ini?')">
@csrf
@method('DELETE')
<button type="submit" class="inline-flex items-center px-3 py-2 bg-red-500 text-white text-xs font-semibold rounded-lg hover:bg-red-600 transition">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Hapus
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="13" class="px-6 py-12 text-center">
<div class="text-gray-400">
<svg class="w-16 h-16 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
</svg>
<p class="text-xl font-semibold mb-2">Belum ada data</p>
<p class="text-sm">Klik tombol "Tambah Data" untuk menambahkan penyakit atau hama</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="px-6 py-4 border-t border-gray-200">
{{ $penyakits->links() }}
</div>
</div>
<!-- Image Modal Lightbox -->
<div id="imageModal" class="fixed inset-0 z-50 hidden bg-black bg-opacity-75 flex items-center justify-center p-4" onclick="closeModal()">
<div class="relative max-w-5xl max-h-full" onclick="event.stopPropagation()">
<!-- Close Button -->
<button
onclick="closeModal()"
class="absolute -top-12 right-0 text-white hover:text-gray-300 transition"
>
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
</button>
<!-- Image -->
<img
id="modalImage"
src=""
alt=""
class="max-w-full max-h-[80vh] object-contain rounded-lg shadow-2xl"
>
<!-- Image Title -->
<div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent p-4 rounded-b-lg">
<p id="modalTitle" class="text-white text-lg font-semibold"></p>
</div>
</div>
</div>
<!-- JavaScript for Modal -->
<script>
function openModal(imageUrl, title) {
document.getElementById('modalImage').src = imageUrl;
document.getElementById('modalTitle').textContent = title;
document.getElementById('imageModal').classList.remove('hidden');
document.body.style.overflow = 'hidden'; // Prevent background scroll
}
function closeModal() {
document.getElementById('imageModal').classList.add('hidden');
document.body.style.overflow = 'auto'; // Restore scroll
}
// Close modal with ESC key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
closeModal();
}
});
</script>
@endsection

View File

@ -0,0 +1,281 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" x-data="{ sidebarOpen: true }" x-cloak>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }} - Admin</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=plus-jakarta-sans:400,500,600,700,800&display=swap" rel="stylesheet" />
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
<!-- Alpine.js -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>
[x-cloak] { display: none !important; }
* {
font-family: 'Plus Jakarta Sans', sans-serif;
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #5FA357;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #4a8345;
}
/* Sidebar Transition */
.sidebar-transition {
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), margin-left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Logo Float Animation */
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-5px); }
}
.logo-float {
animation: float 3s ease-in-out infinite;
}
/* Menu Item Active Indicator */
.menu-item {
position: relative;
}
.menu-item::before {
content: '';
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 4px;
background: linear-gradient(to bottom, #5FA357, #C1FA70);
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.menu-item:hover::before,
.menu-item.active::before {
transform: translateX(0);
}
/* Page Fade In Animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.page-content {
animation: fadeInUp 0.5s ease-out;
}
/* Badge Pulse */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.badge-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>
@stack('styles')
</head>
<body class="font-sans antialiased bg-gray-50">
<div class="min-h-screen flex">
<!-- Sidebar -->
<aside
:class="sidebarOpen ? 'w-64' : 'w-20'"
class="sidebar-transition bg-gradient-to-b from-gray-900 via-gray-800 to-gray-900 text-white fixed h-full z-30 flex flex-col shadow-2xl"
>
<!-- Logo Section -->
<div class="p-6 border-b border-gray-700">
<div class="flex items-center justify-between">
<div x-show="sidebarOpen" class="flex items-center space-x-3">
<div class="logo-float">
<div class="w-10 h-10 bg-gradient-to-br from-green-400 to-green-600 rounded-xl flex items-center justify-center shadow-lg">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
</svg>
</div>
</div>
<div>
<h1 class="text-lg font-bold bg-gradient-to-r from-green-400 to-green-200 bg-clip-text text-transparent">Coffee Expert</h1>
<p class="text-xs text-gray-400">Admin Panel</p>
</div>
</div>
<button
@click="sidebarOpen = !sidebarOpen"
class="p-2 rounded-lg hover:bg-gray-700 transition-colors"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
</div>
<!-- Navigation Menu -->
<nav class="flex-1 px-4 py-6 space-y-2 overflow-y-auto">
<!-- Dashboard -->
<a href="{{ route('admin.dashboard') }}"
class="menu-item flex items-center px-4 py-3 rounded-xl hover:bg-gray-700 {{ request()->routeIs('admin.dashboard') ? 'active bg-gray-700' : '' }} group">
<div class="flex items-center flex-1">
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-blue-500/20 group-hover:bg-blue-500/30 transition-colors">
<svg class="w-5 h-5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
</svg>
</div>
<span x-show="sidebarOpen" class="ml-3 font-semibold">Dashboard</span>
</div>
</a>
<!-- Penyakit Management -->
<a href="{{ route('admin.penyakit.index') }}"
class="menu-item flex items-center px-4 py-3 rounded-xl hover:bg-gray-700 {{ request()->routeIs('admin.penyakit.*') ? 'active bg-gray-700' : '' }} group">
<div class="flex items-center flex-1">
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-red-500/20 group-hover:bg-red-500/30 transition-colors">
<svg class="w-5 h-5 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
<span x-show="sidebarOpen" class="ml-3 font-semibold">Penyakit</span>
</div>
</a>
<!-- Gejala Management -->
<a href="{{ route('admin.gejala.index') }}"
class="menu-item flex items-center px-4 py-3 rounded-xl hover:bg-gray-700 {{ request()->routeIs('admin.gejala.*') ? 'active bg-gray-700' : '' }} group">
<div class="flex items-center flex-1">
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-green-500/20 group-hover:bg-green-500/30 transition-colors">
<svg class="w-5 h-5 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path>
</svg>
</div>
<span x-show="sidebarOpen" class="ml-3 font-semibold">Gejala</span>
</div>
</a>
<!-- Artikel Management -->
<a href="{{ route('admin.artikel.index') }}"
class="menu-item flex items-center px-4 py-3 rounded-xl hover:bg-gray-700 {{ request()->routeIs('admin.artikel.*') ? 'active bg-gray-700' : '' }} group">
<div class="flex items-center flex-1">
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-yellow-500/20 group-hover:bg-yellow-500/30 transition-colors">
<svg class="w-5 h-5 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"></path>
</svg>
</div>
<span x-show="sidebarOpen" class="ml-3 font-semibold">Artikel</span>
</div>
</a>
</nav>
<!-- Logout Button -->
<div class="p-4 border-t border-gray-700">
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="menu-item w-full flex items-center px-4 py-3 rounded-xl hover:bg-red-600/20 group">
<div class="flex items-center flex-1">
<div class="w-10 h-10 flex items-center justify-center rounded-lg bg-red-500/20 group-hover:bg-red-500/30 transition-colors">
<svg class="w-5 h-5 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
</svg>
</div>
<span x-show="sidebarOpen" class="ml-3 font-semibold text-red-400">Logout</span>
</div>
</button>
</form>
</div>
</aside>
<!-- Main Content -->
<div :class="sidebarOpen ? 'ml-64' : 'ml-20'" class="flex-1 sidebar-transition">
<!-- Top Header - Fixed at top -->
<header class="bg-white border-b border-gray-200 fixed top-0 right-0 z-20 shadow-sm" :class="sidebarOpen ? 'left-64' : 'left-20'" style="transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1);">
<div class="px-8 py-4">
<div class="flex items-center justify-between">
<!-- Page Title -->
<div>
<h2 class="text-2xl font-bold text-gray-800">
@yield('page-title', 'Dashboard')
</h2>
<p class="text-sm text-gray-500 mt-1">
@yield('page-subtitle', 'Sistem Pakar Diagnosa Penyakit Tanaman Kopi')
</p>
</div>
<!-- User Info -->
<div class="flex items-center space-x-4">
<!-- Notifications -->
<button class="relative p-2 text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded-xl transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"></path>
</svg>
<span class="absolute top-1 right-1 w-2 h-2 bg-red-500 rounded-full badge-pulse"></span>
</button>
<!-- User Profile - Clickable -->
<a href="{{ route('profile.edit') }}" class="flex items-center space-x-3 px-3 py-2 rounded-xl bg-gradient-to-r from-green-50 to-green-100 hover:from-green-100 hover:to-green-200 transition-all">
<div class="w-10 h-10 rounded-full bg-gradient-to-br from-green-400 to-green-600 flex items-center justify-center text-white font-bold shadow-lg">
{{ substr(Auth::user()->nama, 0, 1) }}
</div>
<div class="text-left">
<p class="text-sm font-bold text-gray-800">{{ Auth::user()->nama }}</p>
<p class="text-xs text-green-600 font-semibold uppercase">Admin</p>
</div>
</a>
</div>
</div>
</div>
</header>
<!-- Page Content - Scrollable with top padding for fixed header -->
<main class="pt-28">
<div class="p-8 page-content">
@yield('content')
</div>
</main>
<!-- Footer -->
<footer class="px-8 py-6 bg-white border-t border-gray-200">
<div class="text-center text-sm text-gray-600">
&copy; {{ date('Y') }} Coffee Expert System. All rights reserved.
</div>
</footer>
</div>
</div>
@stack('scripts')
</body>
</html>

View File

@ -1,29 +1,232 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Profile') }}
</h2>
</x-slot>
@extends('layouts.admin-app')
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-profile-information-form')
@section('page-title', '👤 Profile Settings')
@section('page-subtitle', 'Kelola informasi profil Anda')
@section('content')
<!-- Profile Information Card -->
<div class="bg-white rounded-2xl shadow-lg mb-6">
<div class="p-8">
<div class="flex items-center mb-6">
<div class="bg-gradient-to-r from-green-500 to-green-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
</div>
<div>
<h3 class="text-xl font-bold text-gray-800">Profile Information</h3>
<p class="text-sm text-gray-600">Update your account's profile information and email address</p>
</div>
</div>
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-password-form')
@if (session('status') === 'profile-updated')
<div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg flex items-center">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="font-semibold">Profile updated successfully!</span>
</div>
</div>
@endif
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.delete-user-form')
<form method="post" action="{{ route('profile.update') }}" class="space-y-6">
@csrf
@method('patch')
<!-- Username -->
<div>
<label for="username" class="block text-sm font-semibold text-gray-700 mb-2">Username</label>
<input
type="text"
id="username"
name="username"
value="{{ old('username', $user->username) }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
required
>
@error('username')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
</div>
<!-- Nama -->
<div>
<label for="nama" class="block text-sm font-semibold text-gray-700 mb-2">Nama Lengkap</label>
<input
type="text"
id="nama"
name="nama"
value="{{ old('nama', $user->nama) }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
required
>
@error('nama')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Email -->
<div>
<label for="email" class="block text-sm font-semibold text-gray-700 mb-2">Email</label>
<input
type="email"
id="email"
name="email"
value="{{ old('email', $user->email) }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
required
>
@error('email')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())
<div class="mt-3 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
<p class="text-sm text-yellow-800">
Your email address is unverified.
<button form="send-verification" class="underline text-sm text-yellow-600 hover:text-yellow-900">
Click here to re-send the verification email.
</button>
</p>
</div>
@endif
</div>
<!-- Nomor HP -->
<div>
<label for="no_hp" class="block text-sm font-semibold text-gray-700 mb-2">Nomor HP</label>
<input
type="text"
id="no_hp"
name="no_hp"
value="{{ old('no_hp', $user->no_hp) }}"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-green-500 focus:border-green-500 transition"
placeholder="08123456789"
>
@error('no_hp')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Submit Button -->
<div class="flex items-center gap-4 pt-4">
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-green-500 to-green-600 text-white font-bold rounded-xl hover:from-green-600 hover:to-green-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105">
Save Changes
</button>
</div>
</form>
</div>
</div>
</x-app-layout>
<!-- Update Password Card -->
<div class="bg-white rounded-2xl shadow-lg mb-6">
<div class="p-8">
<div class="flex items-center mb-6">
<div class="bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
</svg>
</div>
<div>
<h3 class="text-xl font-bold text-gray-800">Update Password</h3>
<p class="text-sm text-gray-600">Ensure your account is using a long, random password to stay secure</p>
</div>
</div>
@if (session('status') === 'password-updated')
<div class="mb-6 bg-green-100 border-l-4 border-green-500 text-green-700 p-4 rounded-lg flex items-center">
<svg class="w-6 h-6 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="font-semibold">Password updated successfully!</span>
</div>
@endif
<form method="post" action="{{ route('password.update') }}" class="space-y-6">
@csrf
@method('put')
<!-- Current Password -->
<div>
<label for="current_password" class="block text-sm font-semibold text-gray-700 mb-2">Current Password</label>
<input
type="password"
id="current_password"
name="current_password"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
autocomplete="current-password"
>
@error('current_password', 'updatePassword')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- New Password -->
<div>
<label for="password" class="block text-sm font-semibold text-gray-700 mb-2">New Password</label>
<input
type="password"
id="password"
name="password"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
autocomplete="new-password"
>
@error('password', 'updatePassword')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Confirm Password -->
<div>
<label for="password_confirmation" class="block text-sm font-semibold text-gray-700 mb-2">Confirm Password</label>
<input
type="password"
id="password_confirmation"
name="password_confirmation"
class="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition"
autocomplete="new-password"
>
@error('password_confirmation', 'updatePassword')
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Submit Button -->
<div class="flex items-center gap-4 pt-4">
<button type="submit" class="px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white font-bold rounded-xl hover:from-blue-600 hover:to-blue-700 transition-all shadow-lg hover:shadow-xl transform hover:scale-105">
Update Password
</button>
</div>
</form>
</div>
</div>
<!-- Delete Account Card -->
<div class="bg-white rounded-2xl shadow-lg border-2 border-red-200">
<div class="p-8">
<div class="flex items-center mb-6">
<div class="bg-gradient-to-r from-red-500 to-red-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
</div>
<div>
<h3 class="text-xl font-bold text-gray-800">Delete Account</h3>
<p class="text-sm text-gray-600">Once your account is deleted, all of its resources and data will be permanently deleted</p>
</div>
</div>
<button
type="button"
onclick="if(confirm('Are you sure you want to delete your account? This action cannot be undone.')) { document.getElementById('delete-account-form').submit(); }"
class="px-6 py-3 bg-gradient-to-r from-red-500 to-red-600 text-white font-bold rounded-xl hover:from-red-600 hover:to-red-700 transition-all shadow-lg hover:shadow-xl"
>
Delete Account
</button>
<form id="delete-account-form" method="post" action="{{ route('profile.destroy') }}" class="hidden">
@csrf
@method('delete')
</form>
</div>
</div>
@endsection

View File

@ -0,0 +1,318 @@
<x-app-layout>
<x-slot name="header">
<div class="flex items-center justify-between">
<h2 class="font-semibold text-2xl text-gray-800 leading-tight">
{{ __('Super Admin Dashboard') }}
</h2>
<span class="px-4 py-2 bg-gradient-to-r from-red-500 to-red-600 text-white text-sm font-bold rounded-full shadow-lg">
SUPER ADMIN
</span>
</div>
</x-slot>
<div class="py-8">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<!-- Welcome Card with Gradient -->
<div class="bg-gradient-to-r from-green-600 via-green-700 to-green-800 overflow-hidden shadow-xl sm:rounded-2xl transform hover:scale-[1.01] transition duration-300">
<div class="p-8 text-white relative overflow-hidden">
<!-- Decorative circles -->
<div class="absolute -right-10 -top-10 w-40 h-40 bg-white opacity-10 rounded-full"></div>
<div class="absolute -right-5 -bottom-5 w-32 h-32 bg-white opacity-10 rounded-full"></div>
<div class="relative z-10">
<div class="flex items-center mb-4">
<div class="bg-white bg-opacity-20 rounded-full p-3 mr-4">
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/>
</svg>
</div>
<div>
<h3 class="text-3xl font-bold">Selamat Datang Kembali! 👋</h3>
<p class="text-green-100 text-lg mt-1">{{ Auth::user()->nama }}</p>
</div>
</div>
<p class="text-green-50 text-base">Sistem Pakar Diagnosa Tanaman Kopi</p>
<div class="mt-4 flex items-center space-x-4">
<div class="flex items-center bg-white bg-opacity-20 rounded-full px-4 py-2">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/>
</svg>
<span class="text-sm">{{ now()->translatedFormat('l, d F Y') }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- Statistics Cards with Hover Effects -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Total Users Card -->
<div class="bg-white overflow-hidden shadow-lg sm:rounded-2xl transform hover:scale-105 transition duration-300 hover:shadow-2xl">
<div class="p-6">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase tracking-wide">Total Users</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ \App\Models\User::count() }}</h3>
<p class="text-xs text-gray-500 mt-2">Semua pengguna terdaftar</p>
</div>
<div class="bg-gradient-to-br from-blue-400 to-blue-600 rounded-2xl p-4 shadow-lg">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path>
</svg>
</div>
</div>
<div class="mt-4">
<div class="flex items-center text-green-600">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12 7a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0V8.414l-4.293 4.293a1 1 0 01-1.414 0L8 10.414l-4.293 4.293a1 1 0 01-1.414-1.414l5-5a1 1 0 011.414 0L11 10.586 14.586 7H12z" clip-rule="evenodd"/>
</svg>
<span class="text-xs font-semibold">Aktif</span>
</div>
</div>
</div>
</div>
<!-- Super Admins Card -->
<div class="bg-white overflow-hidden shadow-lg sm:rounded-2xl transform hover:scale-105 transition duration-300 hover:shadow-2xl">
<div class="p-6">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase tracking-wide">Super Admins</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ \App\Models\User::where('role', 'super_admin')->count() }}</h3>
<p class="text-xs text-gray-500 mt-2">Administrator tertinggi</p>
</div>
<div class="bg-gradient-to-br from-red-400 to-red-600 rounded-2xl p-4 shadow-lg">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
</svg>
</div>
</div>
<div class="mt-4">
<div class="flex items-center text-red-600">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"/>
</svg>
<span class="text-xs font-semibold">Full Access</span>
</div>
</div>
</div>
</div>
<!-- Regular Admins Card -->
<div class="bg-white overflow-hidden shadow-lg sm:rounded-2xl transform hover:scale-105 transition duration-300 hover:shadow-2xl">
<div class="p-6">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase tracking-wide">Admins</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ \App\Models\User::where('role', 'admin')->count() }}</h3>
<p class="text-xs text-gray-500 mt-2">Administrator biasa</p>
</div>
<div class="bg-gradient-to-br from-green-400 to-green-600 rounded-2xl p-4 shadow-lg">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
</div>
<div class="mt-4">
<div class="flex items-center text-green-600">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="text-xs font-semibold">Verified</span>
</div>
</div>
</div>
</div>
<!-- Regular Users Card -->
<div class="bg-white overflow-hidden shadow-lg sm:rounded-2xl transform hover:scale-105 transition duration-300 hover:shadow-2xl">
<div class="p-6">
<div class="flex items-center justify-between">
<div class="flex-1">
<p class="text-sm font-semibold text-gray-500 uppercase tracking-wide">Regular Users</p>
<h3 class="text-4xl font-bold text-gray-800 mt-2">{{ \App\Models\User::where('role', 'user')->count() }}</h3>
<p class="text-xs text-gray-500 mt-2">Pengguna biasa</p>
</div>
<div class="bg-gradient-to-br from-purple-400 to-purple-600 rounded-2xl p-4 shadow-lg">
<svg class="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</div>
</div>
<div class="mt-4">
<div class="flex items-center text-purple-600">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"/>
</svg>
<span class="text-xs font-semibold">Community</span>
</div>
</div>
</div>
</div>
</div>
<!-- Quick Actions with Modern Design -->
<div class="bg-white overflow-hidden shadow-xl sm:rounded-2xl">
<div class="p-8">
<div class="flex items-center mb-6">
<div class="bg-gradient-to-r from-green-500 to-green-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<h3 class="text-2xl font-bold text-gray-800">Quick Actions</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<!-- Manage Users -->
<a href="{{ route('super-admin.users.index') }}" class="group relative overflow-hidden bg-gradient-to-br from-blue-50 to-blue-100 hover:from-blue-100 hover:to-blue-200 rounded-2xl p-6 transition-all duration-300 transform hover:scale-105 hover:shadow-xl">
<div class="flex items-center">
<div class="bg-blue-500 text-white rounded-xl p-4 mr-4 group-hover:bg-blue-600 transition-colors">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path>
</svg>
</div>
<div class="flex-1">
<p class="font-bold text-gray-800 text-lg">Manage Users</p>
<p class="text-sm text-gray-600 mt-1">Kelola semua pengguna</p>
</div>
<svg class="w-5 h-5 text-blue-500 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</div>
</a>
<!-- Add New User -->
<a href="{{ route('super-admin.users.create') }}" class="group relative overflow-hidden bg-gradient-to-br from-green-50 to-green-100 hover:from-green-100 hover:to-green-200 rounded-2xl p-6 transition-all duration-300 transform hover:scale-105 hover:shadow-xl">
<div class="flex items-center">
<div class="bg-green-500 text-white rounded-xl p-4 mr-4 group-hover:bg-green-600 transition-colors">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path>
</svg>
</div>
<div class="flex-1">
<p class="font-bold text-gray-800 text-lg">Add New User</p>
<p class="text-sm text-gray-600 mt-1">Tambah pengguna baru</p>
</div>
<svg class="w-5 h-5 text-green-500 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</div>
</a>
<!-- Settings -->
<a href="{{ route('profile.edit') }}" class="group relative overflow-hidden bg-gradient-to-br from-purple-50 to-purple-100 hover:from-purple-100 hover:to-purple-200 rounded-2xl p-6 transition-all duration-300 transform hover:scale-105 hover:shadow-xl">
<div class="flex items-center">
<div class="bg-purple-500 text-white rounded-xl p-4 mr-4 group-hover:bg-purple-600 transition-colors">
<svg class="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
</svg>
</div>
<div class="flex-1">
<p class="font-bold text-gray-800 text-lg">Settings</p>
<p class="text-sm text-gray-600 mt-1">Edit profil Anda</p>
</div>
<svg class="w-5 h-5 text-purple-500 group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</div>
</a>
</div>
</div>
</div>
<!-- Recent Users Table -->
<div class="bg-white overflow-hidden shadow-xl sm:rounded-2xl">
<div class="p-8">
<div class="flex justify-between items-center mb-6">
<div class="flex items-center">
<div class="bg-gradient-to-r from-blue-500 to-blue-600 rounded-xl p-3 mr-4">
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
</div>
<h3 class="text-2xl font-bold text-gray-800">Recent Users</h3>
</div>
<a href="{{ route('super-admin.users.index') }}" class="flex items-center px-4 py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white text-sm font-semibold rounded-xl hover:from-blue-600 hover:to-blue-700 transition-all shadow-lg hover:shadow-xl">
View All
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
</svg>
</a>
</div>
<div class="overflow-x-auto rounded-xl border border-gray-200">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider">Username</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider">Nama</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider">Email</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider">Role</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-600 uppercase tracking-wider">Registered</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@forelse(\App\Models\User::latest()->take(5)->get() as $user)
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="flex-shrink-0 h-10 w-10 bg-gradient-to-br from-blue-400 to-blue-600 rounded-full flex items-center justify-center text-white font-bold">
{{ strtoupper(substr($user->username, 0, 1)) }}
</div>
<div class="ml-4">
<div class="text-sm font-semibold text-gray-900">{{ $user->username }}</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-900 font-medium">{{ $user->nama }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-600">{{ $user->email }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
@if($user->role === 'super_admin')
<span class="px-3 py-1 inline-flex text-xs leading-5 font-bold rounded-full bg-gradient-to-r from-red-500 to-red-600 text-white shadow-md">
Super Admin
</span>
@elseif($user->role === 'admin')
<span class="px-3 py-1 inline-flex text-xs leading-5 font-bold rounded-full bg-gradient-to-r from-blue-500 to-blue-600 text-white shadow-md">
👤 Admin
</span>
@else
<span class="px-3 py-1 inline-flex text-xs leading-5 font-bold rounded-full bg-gradient-to-r from-gray-400 to-gray-500 text-white shadow-md">
👥 User
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
<div class="flex items-center">
<svg class="w-4 h-4 mr-2 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/>
</svg>
{{ $user->created_at->diffForHumans() }}
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-6 py-8 text-center">
<div class="text-gray-400">
<svg class="w-12 h-12 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
</svg>
<p class="text-lg font-semibold">Belum ada data user</p>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,79 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Tambah User Baru') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
<form method="POST" action="{{ route('super-admin.users.store') }}" class="space-y-6">
@csrf
<!-- Username -->
<div>
<x-input-label for="username" :value="__('Username')" />
<x-text-input id="username" name="username" type="text" class="mt-1 block w-full" :value="old('username')" required autofocus />
<x-input-error class="mt-2" :messages="$errors->get('username')" />
</div>
<!-- Nama -->
<div>
<x-input-label for="nama" :value="__('Nama Lengkap')" />
<x-text-input id="nama" name="nama" type="text" class="mt-1 block w-full" :value="old('nama')" required />
<x-input-error class="mt-2" :messages="$errors->get('nama')" />
</div>
<!-- Email -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" :value="old('email')" required />
<x-input-error class="mt-2" :messages="$errors->get('email')" />
</div>
<!-- Password -->
<div>
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" name="password" type="password" class="mt-1 block w-full" required />
<x-input-error class="mt-2" :messages="$errors->get('password')" />
</div>
<!-- Confirm Password -->
<div>
<x-input-label for="password_confirmation" :value="__('Konfirmasi Password')" />
<x-text-input id="password_confirmation" name="password_confirmation" type="password" class="mt-1 block w-full" required />
</div>
<!-- No HP -->
<div>
<x-input-label for="no_hp" :value="__('Nomor HP')" />
<x-text-input id="no_hp" name="no_hp" type="text" class="mt-1 block w-full" :value="old('no_hp')" />
<x-input-error class="mt-2" :messages="$errors->get('no_hp')" />
</div>
<!-- Role -->
<div>
<x-input-label for="role" :value="__('Role')" />
<select id="role" name="role" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" required>
<option value="">Pilih Role</option>
<option value="super_admin" {{ old('role') == 'super_admin' ? 'selected' : '' }}>Super Admin</option>
<option value="admin" {{ old('role') == 'admin' ? 'selected' : '' }}>Admin</option>
<option value="user" {{ old('role') == 'user' ? 'selected' : '' }}>User</option>
</select>
<x-input-error class="mt-2" :messages="$errors->get('role')" />
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Simpan') }}</x-primary-button>
<a href="{{ route('super-admin.users.index') }}" class="text-gray-600 hover:text-gray-900">Batal</a>
</div>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,79 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Edit User') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
<form method="POST" action="{{ route('super-admin.users.update', $user) }}" class="space-y-6">
@csrf
@method('PUT')
<!-- Username -->
<div>
<x-input-label for="username" :value="__('Username')" />
<x-text-input id="username" name="username" type="text" class="mt-1 block w-full" :value="old('username', $user->username)" required autofocus />
<x-input-error class="mt-2" :messages="$errors->get('username')" />
</div>
<!-- Nama -->
<div>
<x-input-label for="nama" :value="__('Nama Lengkap')" />
<x-text-input id="nama" name="nama" type="text" class="mt-1 block w-full" :value="old('nama', $user->nama)" required />
<x-input-error class="mt-2" :messages="$errors->get('nama')" />
</div>
<!-- Email -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" :value="old('email', $user->email)" required />
<x-input-error class="mt-2" :messages="$errors->get('email')" />
</div>
<!-- Password (Optional) -->
<div>
<x-input-label for="password" :value="__('Password Baru (kosongkan jika tidak ingin mengubah)')" />
<x-text-input id="password" name="password" type="password" class="mt-1 block w-full" />
<x-input-error class="mt-2" :messages="$errors->get('password')" />
</div>
<!-- Confirm Password -->
<div>
<x-input-label for="password_confirmation" :value="__('Konfirmasi Password Baru')" />
<x-text-input id="password_confirmation" name="password_confirmation" type="password" class="mt-1 block w-full" />
</div>
<!-- No HP -->
<div>
<x-input-label for="no_hp" :value="__('Nomor HP')" />
<x-text-input id="no_hp" name="no_hp" type="text" class="mt-1 block w-full" :value="old('no_hp', $user->no_hp)" />
<x-input-error class="mt-2" :messages="$errors->get('no_hp')" />
</div>
<!-- Role -->
<div>
<x-input-label for="role" :value="__('Role')" />
<select id="role" name="role" class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" required>
<option value="super_admin" {{ old('role', $user->role) == 'super_admin' ? 'selected' : '' }}>Super Admin</option>
<option value="admin" {{ old('role', $user->role) == 'admin' ? 'selected' : '' }}>Admin</option>
<option value="user" {{ old('role', $user->role) == 'user' ? 'selected' : '' }}>User</option>
</select>
<x-input-error class="mt-2" :messages="$errors->get('role')" />
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Update') }}</x-primary-button>
<a href="{{ route('super-admin.users.index') }}" class="text-gray-600 hover:text-gray-900">Batal</a>
</div>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,95 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('User Management') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
@if (session('success'))
<div class="mb-4 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
{{ session('error') }}
</div>
@endif
<div class="flex justify-between items-center mb-6">
<h3 class="text-lg font-semibold">Daftar User</h3>
<a href="{{ route('super-admin.users.create') }}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
+ Tambah User
</a>
</div>
<div class="overflow-x-auto">
<table class="min-w-full bg-white border">
<thead class="bg-gray-100">
<tr>
<th class="px-6 py-3 border-b text-left text-xs font-medium text-gray-500 uppercase">ID</th>
<th class="px-6 py-3 border-b text-left text-xs font-medium text-gray-500 uppercase">Username</th>
<th class="px-6 py-3 border-b text-left text-xs font-medium text-gray-500 uppercase">Nama</th>
<th class="px-6 py-3 border-b text-left text-xs font-medium text-gray-500 uppercase">Email</th>
<th class="px-6 py-3 border-b text-left text-xs font-medium text-gray-500 uppercase">No HP</th>
<th class="px-6 py-3 border-b text-left text-xs font-medium text-gray-500 uppercase">Role</th>
<th class="px-6 py-3 border-b text-left text-xs font-medium text-gray-500 uppercase">Aksi</th>
</tr>
</thead>
<tbody>
@forelse ($users as $user)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 border-b">{{ $user->id }}</td>
<td class="px-6 py-4 border-b">{{ $user->username }}</td>
<td class="px-6 py-4 border-b">{{ $user->nama }}</td>
<td class="px-6 py-4 border-b">{{ $user->email }}</td>
<td class="px-6 py-4 border-b">{{ $user->no_hp ?? '-' }}</td>
<td class="px-6 py-4 border-b">
@if($user->role === 'super_admin')
<span class="px-2 py-1 text-xs bg-red-100 text-red-800 rounded-full">Super Admin</span>
@elseif($user->role === 'admin')
<span class="px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded-full">Admin</span>
@else
<span class="px-2 py-1 text-xs bg-gray-100 text-gray-800 rounded-full">User</span>
@endif
</td>
<td class="px-6 py-4 border-b">
<div class="flex space-x-2">
<a href="{{ route('super-admin.users.edit', $user) }}" class="text-blue-600 hover:text-blue-900">Edit</a>
@if($user->id !== auth()->id())
<form action="{{ route('super-admin.users.destroy', $user) }}" method="POST" class="inline" onsubmit="return confirm('Yakin ingin menghapus user ini?')">
@csrf
@method('DELETE')
<button type="submit" class="text-red-600 hover:text-red-900">Hapus</button>
</form>
@endif
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-6 py-4 border-b text-center text-gray-500">
Belum ada data user.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-4">
{{ $users->links() }}
</div>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -2,19 +2,65 @@
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\SuperAdmin\UserManagementController;
use App\Http\Controllers\Admin\AdminDashboardController; // ← TAMBAHKAN INI
use App\Http\Controllers\Admin\PenyakitController; // ← DAN INI
use App\Http\Controllers\Admin\GejalaController; // ← DAN INI
use App\Http\Controllers\Admin\ArtikelController; // ← DAN INI
Route::get('/', function () {
return view('welcome');
});
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
// Dashboard untuk user biasa
Route::middleware('auth')->group(function () {
Route::get('/dashboard', function () {
// Redirect berdasarkan role
if (auth()->user()->role === 'super_admin') {
return redirect()->route('super-admin.dashboard');
} elseif (auth()->user()->role === 'admin') {
return redirect()->route('admin.dashboard');
}
return view('dashboard');
})->name('dashboard');
});
// Super Admin Routes
Route::middleware(['auth', App\Http\Middleware\IsSuperAdmin::class])->prefix('super-admin')->name('super-admin.')->group(function () {
Route::get('/dashboard', function () {
return view('super-admin.dashboard');
})->name('dashboard');
Route::resource('users', App\Http\Controllers\SuperAdmin\UserManagementController::class);
});
// Admin Routes (kalau ada)
Route::middleware(['auth', App\Http\Middleware\IsAdmin::class])
->prefix('admin')
->name('admin.')
->group(function () {
// Dashboard Admin (dengan chart)
Route::get('/dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
// Penyakit Management
Route::resource('penyakit', PenyakitController::class);
// Gejala Management
Route::resource('gejala', GejalaController::class);
// Artikel Management
Route::resource('artikel', ArtikelController::class);
// Bisa tambah route lain di sini:
// Route::resource('diagnosa', DiagnosaController::class);
// Route::resource('aturan', AturanController::class);
});
// Profile Routes
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});
require __DIR__.'/auth.php';
require __DIR__.'/auth.php';