progres itulah
This commit is contained in:
parent
6b792d779c
commit
57faf795c5
|
|
@ -233,7 +233,9 @@ class _ResultScreenState extends State<ResultScreen> {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
...widget.result.allPredictions.entries.map((entry) {
|
||||
final score = entry.value as double;
|
||||
final score = (entry.value is num)
|
||||
? (entry.value as num).toDouble()
|
||||
: 0.0;
|
||||
final percentage = (score * 100).toStringAsFixed(2);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
|
|
@ -268,9 +270,12 @@ class _ResultScreenState extends State<ResultScreen> {
|
|||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
const Align(
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text("Rekomendasi Produk:", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
child: Text(
|
||||
"Rekomendasi Produk untuk ${_normalizeDiseaseName(widget.result.diseaseInfo.name)}:",
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
|
|
@ -312,6 +317,8 @@ class _ResultScreenState extends State<ResultScreen> {
|
|||
}
|
||||
|
||||
final products = snapshot.data ?? [];
|
||||
final diseaseName = _normalizeDiseaseName(widget.result.diseaseInfo.name);
|
||||
final filteredProducts = _filterProducts(products, diseaseName);
|
||||
|
||||
if (products.isEmpty) {
|
||||
return Container(
|
||||
|
|
@ -326,7 +333,10 @@ class _ResultScreenState extends State<ResultScreen> {
|
|||
);
|
||||
}
|
||||
|
||||
return GridView.builder(
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
|
|
@ -335,10 +345,12 @@ class _ResultScreenState extends State<ResultScreen> {
|
|||
mainAxisSpacing: 15,
|
||||
childAspectRatio: 0.75,
|
||||
),
|
||||
itemCount: products.length,
|
||||
itemCount: filteredProducts.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildProductCard(products[index]);
|
||||
return _buildProductCard(filteredProducts[index]);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
@ -542,6 +554,47 @@ class _ResultScreenState extends State<ResultScreen> {
|
|||
return buffer.toString();
|
||||
}
|
||||
|
||||
String _normalizeDiseaseName(String diseaseName) {
|
||||
final normalized = {
|
||||
'Bacterial Blight': 'Bacterial Blight',
|
||||
'bacterial blight': 'Bacterial Blight',
|
||||
'Bacterialblight': 'Bacterial Blight',
|
||||
'Bercak Bakteri (Bacterial Blight)': 'Bacterial Blight',
|
||||
'Brown Spot': 'Brown Spot',
|
||||
'brown spot': 'Brown Spot',
|
||||
'Brownspot': 'Brown Spot',
|
||||
'Bercak Coklat (Brown Spot)': 'Brown Spot',
|
||||
'Leaf Smut': 'Leaf Smut',
|
||||
'leaf smut': 'Leaf Smut',
|
||||
'Leafsmut': 'Leaf Smut',
|
||||
'Jamur Daun (Leaf Smut)': 'Leaf Smut',
|
||||
'Healthy': 'Healthy',
|
||||
'healthy': 'Healthy',
|
||||
'Sehat (Healthy)': 'Healthy',
|
||||
};
|
||||
|
||||
return normalized[diseaseName] ?? diseaseName;
|
||||
}
|
||||
|
||||
List<ProductItem> _filterProducts(
|
||||
List<ProductItem> products,
|
||||
String disease,
|
||||
) {
|
||||
if (disease == 'Healthy') {
|
||||
return products;
|
||||
}
|
||||
|
||||
final tagged = products.where((product) {
|
||||
final tag = product.diseaseTag;
|
||||
if (tag == null || tag.trim().isEmpty) {
|
||||
return false;
|
||||
}
|
||||
return _normalizeDiseaseName(tag) == disease;
|
||||
}).toList();
|
||||
|
||||
return tagged;
|
||||
}
|
||||
|
||||
void _showDetailPopup(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ class ClassificationService {
|
|||
|
||||
final response = await _httpClient
|
||||
.get(
|
||||
Uri.parse('$_historyUrl?page=$page$userParam'),
|
||||
Uri.parse('$_historyUrl?page=$page&per_page=1000$userParam'),
|
||||
headers: {'Accept': 'application/json'},
|
||||
)
|
||||
.timeout(const Duration(seconds: 15));
|
||||
|
|
@ -278,7 +278,9 @@ class ClassificationResult {
|
|||
return ClassificationResult(
|
||||
predictedClass: json['predicted_class'] ?? '',
|
||||
confidence: json['confidence'] ?? '0%',
|
||||
confidenceValue: json['confidence_value'] ?? 0.0,
|
||||
confidenceValue: (json['confidence_value'] is num)
|
||||
? (json['confidence_value'] as num).toDouble()
|
||||
: 0.0,
|
||||
allPredictions: Map<String, dynamic>.from(
|
||||
json['all_predictions'] ?? {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ class ProductItem {
|
|||
final double price;
|
||||
final String? imageUrl;
|
||||
final String? marketplaceLink;
|
||||
final String? diseaseTag;
|
||||
|
||||
ProductItem({
|
||||
required this.id,
|
||||
|
|
@ -17,6 +18,7 @@ class ProductItem {
|
|||
this.description,
|
||||
this.imageUrl,
|
||||
this.marketplaceLink,
|
||||
this.diseaseTag,
|
||||
});
|
||||
|
||||
factory ProductItem.fromJson(Map<String, dynamic> json) {
|
||||
|
|
@ -35,6 +37,7 @@ class ProductItem {
|
|||
price: priceValue,
|
||||
imageUrl: json['image_url'],
|
||||
marketplaceLink: json['marketplace_link'],
|
||||
diseaseTag: json['disease_tag'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,15 @@ public function index(Request $request)
|
|||
$query->where('user_id', (int) $request->input('user_id'));
|
||||
}
|
||||
|
||||
$classifications = $query->paginate(15);
|
||||
$perPage = (int) $request->input('per_page', 15);
|
||||
if ($perPage < 1) {
|
||||
$perPage = 15;
|
||||
}
|
||||
if ($perPage > 1000) {
|
||||
$perPage = 1000;
|
||||
}
|
||||
|
||||
$classifications = $query->paginate($perPage);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
|
|
|||
|
|
@ -12,12 +12,51 @@ public function index() {
|
|||
return view('admin.products.index', compact('products'));
|
||||
}
|
||||
|
||||
public function edit(Product $product) {
|
||||
return view('admin.products.edit', compact('product'));
|
||||
}
|
||||
|
||||
public function update(Request $request, Product $product) {
|
||||
$request->validate([
|
||||
'name' => 'required',
|
||||
'price' => 'required|numeric',
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||
'marketplace_link' => 'required|url|max:2048',
|
||||
'disease_tag' => 'nullable|string|max:50'
|
||||
]);
|
||||
|
||||
$imagePath = $product->image;
|
||||
if ($request->hasFile('image')) {
|
||||
$newPath = $this->compressAndStoreProductImage($request->file('image'))
|
||||
?? $request->file('image')->store('products', 'public');
|
||||
|
||||
if ($newPath) {
|
||||
if ($imagePath) {
|
||||
Storage::disk('public')->delete($imagePath);
|
||||
}
|
||||
$imagePath = $newPath;
|
||||
}
|
||||
}
|
||||
|
||||
$product->update([
|
||||
'name' => $request->name,
|
||||
'description' => $request->description,
|
||||
'price' => $request->price,
|
||||
'image' => $imagePath,
|
||||
'marketplace_link' => $request->marketplace_link,
|
||||
'disease_tag' => $request->disease_tag ?: null
|
||||
]);
|
||||
|
||||
return redirect()->route('produk.index')->with('success', 'Produk berhasil diperbarui!');
|
||||
}
|
||||
|
||||
public function store(Request $request) {
|
||||
$request->validate([
|
||||
'name' => 'required',
|
||||
'price' => 'required|numeric',
|
||||
'image' => 'image|mimes:jpeg,png,jpg|max:2048',
|
||||
'marketplace_link' => 'required|url|max:2048'
|
||||
'marketplace_link' => 'required|url|max:2048',
|
||||
'disease_tag' => 'nullable|string|max:50'
|
||||
]);
|
||||
|
||||
$imagePath = null;
|
||||
|
|
@ -31,7 +70,8 @@ public function store(Request $request) {
|
|||
'description' => $request->description,
|
||||
'price' => $request->price,
|
||||
'image' => $imagePath,
|
||||
'marketplace_link' => $request->marketplace_link
|
||||
'marketplace_link' => $request->marketplace_link,
|
||||
'disease_tag' => $request->disease_tag ?: null
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Produk berhasil ditambahkan!');
|
||||
|
|
@ -55,6 +95,7 @@ public function apiIndex(Request $request) {
|
|||
'price' => $product->price,
|
||||
'image_url' => $imageUrl,
|
||||
'marketplace_link' => $product->marketplace_link,
|
||||
'disease_tag' => $product->disease_tag,
|
||||
];
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class Product extends Model
|
|||
'description',
|
||||
'price',
|
||||
'image',
|
||||
'marketplace_link'
|
||||
'marketplace_link',
|
||||
'disease_tag'
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->string('disease_tag', 50)->nullable()->after('marketplace_link');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->dropColumn('disease_tag');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
@extends('layouts.admin')
|
||||
@section('title', 'Edit Produk')
|
||||
|
||||
@section('content')
|
||||
|
||||
@if(session('success'))
|
||||
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($errors->any())
|
||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
|
||||
<ul class="list-disc list-inside">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="max-w-2xl">
|
||||
<div class="bg-white p-6 rounded-2xl shadow-sm border border-gray-100">
|
||||
<h3 class="text-lg font-bold text-gray-800 mb-4 border-b pb-2">Edit Produk</h3>
|
||||
<form action="{{ route('produk.update', $product) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nama Produk</label>
|
||||
<input type="text" name="name" value="{{ old('name', $product->name) }}" class="w-full border-gray-300 rounded-lg shadow-sm focus:ring-emerald-500 focus:border-emerald-500 p-2 border" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Harga (Rp)</label>
|
||||
<input type="number" name="price" value="{{ old('price', $product->price) }}" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Deskripsi</label>
|
||||
<textarea name="description" rows="3" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border">{{ old('description', $product->description) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Link Marketplace</label>
|
||||
<input type="url" name="marketplace_link" value="{{ old('marketplace_link', $product->marketplace_link) }}" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Foto Produk</label>
|
||||
<input type="file" name="image" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-emerald-50 file:text-emerald-700 hover:file:bg-emerald-100">
|
||||
<p class="text-xs text-gray-500 mt-1">Kosongkan jika tidak ingin mengganti.</p>
|
||||
@if($product->image)
|
||||
<div class="mt-3 flex items-center gap-3">
|
||||
<img class="h-12 w-12 rounded-lg object-cover" src="{{ asset('storage/' . $product->image) }}" alt="">
|
||||
<span class="text-xs text-gray-500">Foto saat ini</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<button type="submit" class="bg-emerald-600 hover:bg-emerald-700 text-white font-bold py-2 px-4 rounded-lg transition">Simpan Perubahan</button>
|
||||
<a href="{{ route('produk.index') }}" class="text-gray-600 hover:text-gray-900">Batal</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -46,6 +46,17 @@
|
|||
<input type="url" name="marketplace_link" value="{{ old('marketplace_link') }}" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border" placeholder="https://shopee.co.id/..." required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Kategori Penyakit</label>
|
||||
<select name="disease_tag" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border">
|
||||
<option value="">Tidak terkait</option>
|
||||
<option value="Bacterial Blight" {{ old('disease_tag') == 'Bacterial Blight' ? 'selected' : '' }}>Bacterial Blight</option>
|
||||
<option value="Brown Spot" {{ old('disease_tag') == 'Brown Spot' ? 'selected' : '' }}>Brown Spot</option>
|
||||
<option value="Leaf Smut" {{ old('disease_tag') == 'Leaf Smut' ? 'selected' : '' }}>Leaf Smut</option>
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 mt-1">Kosongkan jika produk untuk umum.</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Foto Produk</label>
|
||||
<input type="file" name="image" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-emerald-50 file:text-emerald-700 hover:file:bg-emerald-100">
|
||||
|
|
@ -59,18 +70,18 @@
|
|||
|
||||
<div class="w-full lg:w-2/3">
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<table class="w-full table-fixed divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Info Produk</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Harga</th>
|
||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Aksi</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-2/3">Info Produk</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-32">Harga</th>
|
||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider w-32">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
@foreach($products as $product)
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center">
|
||||
<div class="h-10 w-10 flex-shrink-0">
|
||||
@if($product->image)
|
||||
|
|
@ -79,17 +90,36 @@
|
|||
<div class="h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center">📦</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900">{{ $product->name }}</div>
|
||||
<div class="ml-4 min-w-0">
|
||||
<div class="text-sm font-medium text-gray-900 truncate">{{ $product->name }}</div>
|
||||
<div class="text-xs text-gray-500 truncate">
|
||||
{{ $product->disease_tag ?: 'Tidak terkait' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Rp {{ number_format($product->price) }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div class="inline-flex items-center justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
class="text-emerald-600 hover:text-emerald-900"
|
||||
data-edit-product
|
||||
data-id="{{ $product->id }}"
|
||||
data-name="{{ $product->name }}"
|
||||
data-price="{{ $product->price }}"
|
||||
data-description="{{ $product->description }}"
|
||||
data-marketplace="{{ $product->marketplace_link }}"
|
||||
data-disease="{{ $product->disease_tag }}"
|
||||
data-image="{{ $product->image ? asset('storage/' . $product->image) : '' }}"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<form action="{{ route('produk.destroy', $product->id) }}" method="POST" onsubmit="return confirm('Yakin hapus?')">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="text-red-600 hover:text-red-900">Hapus</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
|
@ -98,4 +128,120 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="editProductModal" class="fixed inset-0 z-50 hidden">
|
||||
<div class="absolute inset-0 bg-black/40" data-edit-close></div>
|
||||
<div class="absolute inset-0 flex items-center justify-center p-4">
|
||||
<div class="bg-white w-full max-w-2xl rounded-2xl shadow-lg border border-gray-100">
|
||||
<div class="flex items-center justify-between px-6 py-4 border-b">
|
||||
<h3 class="text-lg font-bold text-gray-800">Edit Produk</h3>
|
||||
<button type="button" class="text-gray-400 hover:text-gray-600" data-edit-close>✕</button>
|
||||
</div>
|
||||
<form id="editProductForm" method="POST" enctype="multipart/form-data" class="p-6">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Nama Produk</label>
|
||||
<input type="text" name="name" id="editName" class="w-full border-gray-300 rounded-lg shadow-sm focus:ring-emerald-500 focus:border-emerald-500 p-2 border" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Harga (Rp)</label>
|
||||
<input type="number" name="price" id="editPrice" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Deskripsi</label>
|
||||
<textarea name="description" id="editDescription" rows="3" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Link Marketplace</label>
|
||||
<input type="url" name="marketplace_link" id="editMarketplace" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Kategori Penyakit</label>
|
||||
<select name="disease_tag" id="editDisease" class="w-full border-gray-300 rounded-lg shadow-sm p-2 border">
|
||||
<option value="">Tidak terkait</option>
|
||||
<option value="Bacterial Blight">Bacterial Blight</option>
|
||||
<option value="Brown Spot">Brown Spot</option>
|
||||
<option value="Leaf Smut">Leaf Smut</option>
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 mt-1">Kosongkan jika produk untuk umum.</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Foto Produk</label>
|
||||
<input type="file" name="image" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-emerald-50 file:text-emerald-700 hover:file:bg-emerald-100">
|
||||
<p class="text-xs text-gray-500 mt-1">Kosongkan jika tidak ingin mengganti.</p>
|
||||
<div id="editImageWrap" class="mt-3 hidden items-center gap-3">
|
||||
<img id="editImagePreview" class="h-12 w-12 rounded-lg object-cover" src="" alt="">
|
||||
<span class="text-xs text-gray-500">Foto saat ini</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<button type="submit" class="bg-emerald-600 hover:bg-emerald-700 text-white font-bold py-2 px-4 rounded-lg transition">Simpan Perubahan</button>
|
||||
<button type="button" class="text-gray-600 hover:text-gray-900" data-edit-close>Batal</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const editModal = document.getElementById('editProductModal');
|
||||
const editForm = document.getElementById('editProductForm');
|
||||
const editName = document.getElementById('editName');
|
||||
const editPrice = document.getElementById('editPrice');
|
||||
const editDescription = document.getElementById('editDescription');
|
||||
const editMarketplace = document.getElementById('editMarketplace');
|
||||
const editDisease = document.getElementById('editDisease');
|
||||
const editImageWrap = document.getElementById('editImageWrap');
|
||||
const editImagePreview = document.getElementById('editImagePreview');
|
||||
|
||||
const openEditModal = (button) => {
|
||||
const id = button.dataset.id;
|
||||
editForm.action = `{{ url('produk') }}/${id}`;
|
||||
editName.value = button.dataset.name || '';
|
||||
editPrice.value = button.dataset.price || '';
|
||||
editDescription.value = button.dataset.description || '';
|
||||
editMarketplace.value = button.dataset.marketplace || '';
|
||||
editDisease.value = button.dataset.disease || '';
|
||||
|
||||
if (button.dataset.image) {
|
||||
editImagePreview.src = button.dataset.image;
|
||||
editImageWrap.classList.remove('hidden');
|
||||
editImageWrap.classList.add('flex');
|
||||
} else {
|
||||
editImagePreview.src = '';
|
||||
editImageWrap.classList.add('hidden');
|
||||
editImageWrap.classList.remove('flex');
|
||||
}
|
||||
|
||||
editModal.classList.remove('hidden');
|
||||
document.body.classList.add('overflow-hidden');
|
||||
};
|
||||
|
||||
const closeEditModal = () => {
|
||||
editModal.classList.add('hidden');
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
};
|
||||
|
||||
document.querySelectorAll('[data-edit-product]').forEach((button) => {
|
||||
button.addEventListener('click', () => openEditModal(button));
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-edit-close]').forEach((button) => {
|
||||
button.addEventListener('click', closeEditModal);
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape' && !editModal.classList.contains('hidden')) {
|
||||
closeEditModal();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
|
|
@ -19,7 +19,9 @@
|
|||
// Halaman Dashboard (Hanya bisa diakses jika sudah login)
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])->middleware('auth')->name('dashboard');
|
||||
|
||||
Route::resource('produk', ProductController::class);
|
||||
Route::resource('produk', ProductController::class)->parameters([
|
||||
'produk' => 'product'
|
||||
]);
|
||||
Route::resource('dataset', DatasetController::class);
|
||||
Route::resource('history-klasifikasi', ClassificationHistoryAdminController::class)
|
||||
->only(['index', 'update', 'destroy'])
|
||||
|
|
|
|||
Loading…
Reference in New Issue