admin-penyakit done
|
|
@ -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'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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'");
|
||||
}
|
||||
};
|
||||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
After Width: | Height: | Size: 974 KiB |
|
After Width: | Height: | Size: 974 KiB |
|
After Width: | Height: | Size: 974 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 339 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 240 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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">
|
||||
© {{ date('Y') }} Coffee Expert System. All rights reserved.
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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';
|
||||