관리-도구
편집 파일: edit-product.blade.php
@extends('layouts.apps') @section('content') <main class="main-wrapper"> <div class="main-content"> <div class="row mb-3 align-items-center"> <div class="col-md-6"> <h3 class="mb-0">Edit Product</h3> </div> <div class="col-md-6 text-md-end mt-2 mt-md-0"> <a href="{{ route('admin-product.index') }}" class="btn btn-primary">View Products</a> </div> </div> <div class="card"> <div class="card-body"> {{-- MAIN UPDATE FORM --}} <form id="product-update-form" action="{{ route('admin-product.update', $product->id) }}" method="POST" enctype="multipart/form-data"> @csrf @method('PUT') {{-- Product Title --}} <div class="mb-4"> <label class="form-label">Product Title</label> <input type="text" name="title" class="form-control" value="{{ old('title', $product->title) }}" required> </div> {{-- Short Description --}} <div class="mb-4"> <label class="form-label">Short Description</label> <textarea id="short_description" name="short_description" class="form-control" rows="3"> {{ old('short_description', $product->short_description) }} </textarea> </div> {{-- Long Description --}} <div class="mb-4"> <label class="form-label">Long Description</label> <textarea id="long_description" name="long_description" class="form-control" rows="5"> {{ old('long_description', $product->long_description) }} </textarea> </div> {{-- Category --}} <div class="mb-4"> <label class="form-label">Category</label> <select name="cat_id" class="form-select" required> <option value="">Select Category</option> @foreach($categories as $cat) <option value="{{ $cat->cat_id }}" {{ $product->cat_id == $cat->cat_id ? 'selected' : '' }}> {{ $cat->category }} </option> @endforeach </select> </div> {{-- Hot Offer --}} <div class="mb-4"> <label class="form-label">Hot Offer</label> <select name="hotoff_id" class="form-select"> <option value="">None</option> @foreach($hotOffers as $offer) <option value="{{ $offer->hotoff_id }}" {{ $product->hotoff_id == $offer->hotoff_id ? 'selected' : '' }}> {{ $offer->hotoffer }} </option> @endforeach </select> </div> {{-- Product Images --}} <div class="mb-4"> <label class="form-label">Product Images</label> <input type="file" name="images[]" class="form-control" multiple> {{-- Existing Images --}} @if($product->images->count()) <div id="existing-images" class="mt-3 d-flex flex-wrap gap-3"> @foreach($product->images as $img) <div class="image-box position-relative" data-image-id="{{ $img->id }}" style="width:100px;"> <img src="{{ asset('storage/' . $img->image_url) }}" class="img-thumbnail w-100" style="height: 100px; object-fit: cover; display:block;"> {{-- Delete Button (AJAX) --}} <button type="button" class="delete-image-btn btn btn-sm btn-danger p-0 px-1" data-url="{{ route('admin-product.image.delete', $img->id) }}" aria-label="Delete image {{ $img->id }}" title="Delete"> × </button> </div> @endforeach </div> @endif </div> {{-- Submit --}} <div class="text-end"> <button type="submit" class="btn btn-success">Update Product</button> </div> </form> {{-- END MAIN FORM --}} </div> </div> </div> </main> {{-- CSRF token for JS --}} <meta name="csrf-token" content="{{ csrf_token() }}"> {{-- Minimal CSS to avoid overlap and ensure delete buttons sit inside image box only --}} <style> .image-box { position: relative; } .delete-image-btn { position: absolute; top: 4px; right: 4px; line-height: 1; z-index: 5; /* keep it above the image within the box */ } /* ensure the image container doesn't overlap the update button */ #existing-images { z-index: 0; } </style> <script> (function() { // Grab CSRF token const csrf = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); // Delegate clicks on delete-image-btn document.addEventListener('click', function(e) { const btn = e.target.closest('.delete-image-btn'); if (!btn) return; e.preventDefault(); const url = btn.getAttribute('data-url'); if (!url) return console.error('Delete URL missing'); if (!confirm('Delete this image?')) return; // Disable button while request in progress btn.disabled = true; btn.textContent = '...'; fetch(url, { method: 'DELETE', headers: { 'X-CSRF-TOKEN': csrf, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json' }, body: JSON.stringify({}) // Laravel will accept DELETE without body, but keep consistent }) .then(response => { if (!response.ok) throw response; return response.json().catch(() => ({ ok: true })); }) .then(data => { // remove the image box from DOM const imageBox = btn.closest('.image-box'); if (imageBox) imageBox.remove(); // Optionally show a message (you can replace with toast UI) console.log('Image deleted'); }) .catch(async err => { let msg = 'Failed to delete image.'; try { const json = await err.json(); if (json?.message) msg = json.message; } catch (_) {} alert(msg); btn.disabled = false; btn.textContent = '×'; console.error(err); }); }); })(); </script> @endsection <script src="https://cdn.ckeditor.com/ckeditor5/39.0.1/classic/ckeditor.js"></script> <style> /* CKEditor editable area text color fix */ .ck-editor__editable { color: #000 !important; background: #fff !important; min-height: 200px; max-height: 300px; overflow-y: auto !important; } .ck-content * { color: #000 !important; } </style> <script> document.addEventListener('DOMContentLoaded', function () { ClassicEditor.create(document.querySelector('#short_description')) .catch(error => console.error(error)); ClassicEditor.create(document.querySelector('#long_description')) .catch(error => console.error(error)); }); </script>