add logic in foto controller and paket foto section for crud paket foto

This commit is contained in:
LailaWulandarii 2025-12-27 18:15:35 +07:00
parent 42c6961eb2
commit f9eb2397af
6 changed files with 422 additions and 288 deletions

View File

@ -3,7 +3,12 @@
namespace App\Http\Controllers\Admin; namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Additional;
use App\Models\PaketFoto;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\File;
class FotoController extends Controller class FotoController extends Controller
{ {
@ -12,54 +17,132 @@ class FotoController extends Controller
*/ */
public function index() public function index()
{ {
return view('admin.paket-foto'); $foto = PaketFoto::latest()->get();
$additional = Additional::latest()->get();
// Kirim keduanya ke view paket-foto.index
return view('admin.paket-foto.index', compact('foto', 'additional'));
} }
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request) public function store(Request $request)
{ {
// $validator = Validator::make($request->all(), [
'nama' => 'required|string|min:3|max:100',
'harga' => 'required|numeric|min:0',
'durasi' => 'required|integer|min:1',
'deskripsi' => 'required|string|min:10',
'foto' => 'required|image|mimes:jpeg,png,jpg|max:2048',
], [
'required' => 'Kolom :attribute wajib diisi.',
'string' => 'Input :attribute harus berupa teks valid.',
'integer' => 'Input :attribute harus berupa angka valid.',
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
'image' => ':attribute harus berupa file gambar (foto).',
'mimes' => 'Format :attribute tidak didukung. Gunakan format: jpeg, png, atau jpg.',
'max.file' => 'Ukuran :attribute maksimal 2MB.',
'min' => ':attribute minimal harus :min karakter/nilai.',
'numeric' => ':attribute harus berupa angka.',
], [
'nama' => 'nama paket',
'harga' => 'harga paket',
'durasi' => 'durasi paket',
'foto' => 'foto paket',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput()
->with('error_modal', 'createFoto'); // Agar modal terbuka otomatis saat error
}
$path = null;
if ($request->hasFile('foto')) {
$file = $request->file('foto');
$filename = time() . '_' . $file->getClientOriginalName();
// Simpan langsung ke folder public untuk asset()
$file->move(public_path('img/foto'), $filename);
$path = 'img/foto/' . $filename;
}
PaketFoto::create([
'nama' => $request->nama,
'harga' => $request->harga,
'durasi' => $request->durasi,
'deskripsi' => $request->deskripsi,
'foto' => $path,
]);
return redirect()->back()->with('success', 'Paket foto baru berhasil ditambahkan!');
} }
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id) public function update(Request $request, string $id)
{ {
// $paket = PaketFoto::findOrFail($id);
$validator = Validator::make($request->all(), [
'nama' => 'required|string|min:3|max:100',
'harga' => 'required|numeric|min:0',
'durasi' => 'required|integer|min:1',
'deskripsi' => 'required|string|min:10',
'foto' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
], [
'required' => 'Kolom :attribute wajib diisi.',
'string' => 'Input :attribute harus berupa teks valid.',
'integer' => 'Input :attribute harus berupa angka valid.',
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
'image' => ':attribute harus berupa file gambar (foto).',
'mimes' => 'Format :attribute tidak didukung. Gunakan format: jpeg, png, atau jpg.',
'max.file' => 'Ukuran :attribute maksimal 2MB.',
'min' => ':attribute minimal harus :min karakter/nilai.',
'numeric' => ':attribute harus berupa angka.',
], [
'nama' => 'nama paket',
'harga' => 'harga paket',
'durasi' => 'durasi paket',
'foto' => 'foto paket',
]);
if ($validator->fails()) {
return redirect()->back()
->withErrors($validator)
->withInput()
->with('error_id', $id); // Membuka modal edit yang error otomatis
}
$data = $request->only(['nama', 'harga', 'durasi', 'deskripsi']);
if ($request->hasFile('foto')) {
// Hapus file lama jika ada foto baru yang diunggah
if ($paket->foto && File::exists(public_path($paket->foto))) {
File::delete(public_path($paket->foto));
}
$file = $request->file('foto');
$filename = time() . '_' . $file->getClientOriginalName();
$file->move(public_path('img/foto'), $filename);
$data['foto'] = 'img/foto/' . $filename;
}
$paket->update($data);
return redirect()->back()->with('success', 'Paket foto berhasil diperbarui!');
} }
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id) public function destroy(string $id)
{ {
// // Cari data berdasarkan primary key id_paket
$paket = PaketFoto::where('id_paket', $id)->firstOrFail();
// 1. Cek dan hapus file foto dari folder public
if ($paket->foto && File::exists(public_path($paket->foto))) {
File::delete(public_path($paket->foto));
}
// 2. Hapus data dari database
$paket->delete();
return redirect()->back()->with('success', 'Paket foto dan filenya berhasil dihapus!');
} }
} }

View File

@ -3,6 +3,21 @@
@section('title', 'Paket Foto') @section('title', 'Paket Foto')
@section('content') @section('content')
{{-- ALERT SUKSES --}}
@if (session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
{{-- ALERT ERROR UMUM (Jika ada error selain validasi modal) --}}
@if (session('error'))
<div class="alert alert-danger alert-dismissible fade show" role="alert">
{{ session('error') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
<section class="section"> <section class="section">
<div class="d-flex gap-2 pb-3"> <div class="d-flex gap-2 pb-3">
@ -39,36 +54,54 @@
<table class="table table-striped" id="table1"> <table class="table table-striped" id="table1">
<thead> <thead>
<tr> <tr>
<th>No.</th> <th style="width: 5%" class="text-center">No.</th>
<th>Nama Paket</th> <th style="width: 20%">Nama Paket</th>
<th>Deskripsi</th> <th style="width: 35%">Deskripsi</th>
<th>Harga</th> <th style="width: 15%" class="text-nowrap">Harga</th>
<th>Foto</th> <th style="width: 10%" class="text-center">Foto</th>
<th class="text-center">Aksi</th> <th style="width: 15%" class="text-center">Aksi</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> @forelse ($foto as $f)
<td>Graiden</td> <tr>
<td>vehicula.aliquet@semconsequat.co.uk</td> <td style="width: 5%" class="text-center">{{ $loop->iteration }}</td>
<td>076 4820 8838</td> <td style="width: 20%">{{ $f->nama }}</td>
<td>Offenburg</td> <td style="width: 35%">{{ Str::limit($f->deskripsi, 50) }}</td>
<td>Offenburg</td> <td style="width: 15%">Rp {{ number_format($f->harga, 0, ',', '.') }}</td>
<td class="col-auto text-center"> <td style="width:10%" class="text-center">
<a href="#" class="btn icon btn-primary btn-action" data-bs-toggle="modal" <img src="{{ asset($f->foto) }}" alt="Foto Produk" class="rounded"
data-bs-target="#foto"> style="width: 50px; height: 50px; object-fit: cover;">
<i class="bi bi-eye"></i> </td>
</a> <td class="col-auto text-center" style="width: 15%">
<a href="#" class="btn icon btn-warning btn-action" data-bs-toggle="modal" <a href="#" class="btn icon btn-primary btn-action" data-bs-toggle="modal"
data-bs-target="#editFoto"> data-bs-target="#foto{{ $f->id_paket }}">
<i class="bi bi-pencil"></i> <i class="bi bi-eye"></i>
</a> </a>
<a href="#" class="btn icon btn-danger btn-action" data-bs-toggle="modal"
data-bs-target="#deleteFoto"> <a href="#" class="btn icon btn-warning btn-action" data-bs-toggle="modal"
<i class="bi bi-trash"></i> data-bs-target="#editFoto{{ $f->id_paket }}">
</a> <i class="bi bi-pencil"></i>
</td> </a>
</tr>
<a href="#" class="btn icon btn-danger btn-action" data-bs-toggle="modal"
data-bs-target="#deleteFoto{{ $f->id_paket }}">
<i class="bi bi-trash"></i>
</a>
</td>
</tr>
@include('admin.paket-foto.partials.modal-show-foto')
@include('admin.paket-foto.partials.modal-edit-foto')
@include('admin.paket-foto.partials.modal-delete-foto')
@empty
<tr>
<td colspan="6" class="text-center p-4">
<div class="alert alert-light-secondary color-secondary">
<i class="bi bi-exclamation-circle"></i> Belum ada data paket foto.
</div>
</td>
</tr>
@endforelse
</tbody> </tbody>
</table> </table>
</div> </div>
@ -77,28 +110,44 @@
<table class="table table-striped" id="riwayat-foto"> <table class="table table-striped" id="riwayat-foto">
<thead> <thead>
<tr> <tr>
<th>No.</th> <th style="width: 5%">No.</th>
<th>Nama Additional</th> <th style="width: 65%">Nama Additional</th>
<th>Harga</th> <th style="width: 15%" class="text-nowrap">Harga</th>
<th class="text-center">Aksi</th> <th style="width: 15%" class="text-center">Aksi</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> @forelse ($additional as $ad)
<td>Graiden</td> <tr>
<td>vehicula.aliquet@semconsequat.co.uk</td> <td style="width: 5%">{{ $loop->iteration }}</td>
<td>076 4820 8838</td> <td style="width: 65%">{{ $ad->nama }}</td>
<td class="col-auto text-center"> <td style="width: 15%">Rp {{ number_format($ad->harga, 0, ',', '.') }}</td>
<a href="#" class="btn icon btn-warning btn-action" data-bs-toggle="modal" <td class="col-auto text-center" style="width: 15%">
data-bs-target="#editAdd"> <a href="#" class="btn icon btn-warning btn-action"
<i class="bi bi-pencil"></i> data-bs-toggle="modal"
</a> data-bs-target="#editFoto{{ $ad->id_additional }}">
<a href="#" class="btn icon btn-danger btn-action" data-bs-toggle="modal" <i class="bi bi-pencil"></i>
data-bs-target="#deleteAdd"> </a>
<i class="bi bi-trash"></i>
</a> <a href="#" class="btn icon btn-danger btn-action"
</td> data-bs-toggle="modal"
</tr> data-bs-target="#deleteFoto{{ $ad->id_additional }}">
<i class="bi bi-trash"></i>
</a>
</td>
</tr>
{{-- @include('admin.produk-buket.partials.modal-show')
@include('admin.produk-buket.partials.modal-edit')
@include('admin.produk-buket.partials.modal-delete') --}}
@empty
<tr>
<td colspan="6" class="text-center p-4">
<div class="alert alert-light-secondary color-secondary">
<i class="bi bi-exclamation-circle"></i> Belum ada data additional.
</div>
</td>
</tr>
@endforelse
</tbody> </tbody>
</table> </table>
</div> </div>
@ -115,5 +164,60 @@
@include('admin.paket-foto.partials.modal-create-additional') @include('admin.paket-foto.partials.modal-create-additional')
@include('admin.paket-foto.partials.modal-edit-additional') @include('admin.paket-foto.partials.modal-edit-additional')
@include('admin.paket-foto.partials.modal-delete-additional') @include('admin.paket-foto.partials.modal-delete-additional')
@push('scripts')
<script>
// FUNGSI PREVIEW UNIVERSAL (Bisa untuk Create & Edit)
function previewImage(input, previewId, placeholderId) {
const preview = document.getElementById(previewId);
const placeholder = document.getElementById(placeholderId);
const file = input.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.classList.remove('d-none');
if (placeholder) placeholder.classList.add('d-none');
}
reader.readAsDataURL(file);
}
}
document.addEventListener('DOMContentLoaded', function() {
function clearBackdrop() {
const backdrops = document.querySelectorAll('.modal-backdrop');
backdrops.forEach(b => b.remove());
document.body.classList.remove('modal-open');
document.body.style.paddingRight = '';
}
// Logic Modal Error tetap sama seperti milikmu
@if (session('error_modal'))
clearBackdrop();
var modalId = "{{ session('error_modal') }}";
var targetElement = document.getElementById(modalId);
if (targetElement) {
new bootstrap.Modal(targetElement).show();
}
@endif
@if (session('error_id'))
clearBackdrop();
var errorId = "{{ session('error_id') }}";
var editModalId = document.getElementById("editFoto" + errorId) ? "editFoto" + errorId : "editAdd" +
errorId;
var targetEdit = document.getElementById(editModalId);
if (targetEdit) {
new bootstrap.Modal(targetEdit).show();
}
@endif
});
function showImage(src) {
var modalImg = document.getElementById('img-preview-target');
modalImg.src = src;
new bootstrap.Modal(document.getElementById('modalImagePreview')).show();
}
</script>
@endpush
@endsection @endsection

View File

@ -1,32 +1,40 @@
<div class="modal fade" id="createFoto" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="createFoto" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-md"> <div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Tambah Paket Foto</h5> <button type="button" class="btn-close" <h5 class="modal-title">Tambah Paket Foto</h5> <button type="button" class="btn-close"
data-bs-dismiss="modal" aria-label="Close"></button> data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body">
<form action="#" method="POST" enctype="multipart/form-data"> <form action="{{ route('admin.paket-foto.store') }}" method="POST" enctype="multipart/form-data">
<div class="modal-body"> @csrf
<div class="row gx-3"> <div class="row gx-3">
<div class="col-12 col-md-7"> <div class="col-12 col-md-7">
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Nama Paket</label> <label class="form-label">Nama Paket</label>
<input type="text" class="form-control" style="font-size: 13px;" <input type="text" class="form-control @error('nama') is-invalid @enderror"
placeholder="Masukkan Nama Paket"> style="font-size: 14px;" placeholder="Masukkan Nama Paket"
value="{{ old('nama') }}">
@error('nama')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div> </div>
<div class="row gx-1"> <div class="row gx-1">
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Harga Paket</label> <label class="form-label">Harga Paket</label>
<input type="number" class="form-control" style="font-size: 13px;" <input type="number" class="form-control @error('nama') is-invalid @enderror"
placeholder="Harga Paket"> style="font-size: 14px;"
placeholder="Harga Paket"value="{{ old('nama') }}">
<p class="mb-0"><small class="text-muted mb-0">Dalam Rupiah</small> <p class="mb-0"><small class="text-muted mb-0">Dalam Rupiah</small>
</p> </p>
@error('nama')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div> </div>
</div> </div>
@ -34,95 +42,82 @@
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Durasi</label> <label class="form-label">Durasi</label>
<input type="number" class="form-control" style="font-size: 13px;" <input type="number" class="form-control @error('nama') is-invalid @enderror"
placeholder="Durasi Paket"> style="font-size: 14px;"
placeholder="Durasi Paket"value="{{ old('nama') }}">
<p class="mb-0"><small class="text-muted mb-0">Dalam Menit</small> <p class="mb-0"><small class="text-muted mb-0">Dalam Menit</small>
</p> </p>
@error('nama')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div> </div>
</div> </div>
</div> </div>
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Deskripsi Paket</label> <label class="form-label">Deskripsi Paket</label>
<textarea class="form-control" rows="4" style="font-size: 13px;" placeholder="Masukkan Deskripsi Paket"></textarea> <textarea class="form-control @error('nama') is-invalid @enderror" rows="5" style="font-size: 14px;"
placeholder="Masukkan Deskripsi Paket">{{ old('deskripsi') }}</textarea>
@error('deskripsi')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</textarea>
</div> </div>
</div> </div>
<div class="col-12 col-md-5"> <div class="col-12 col-md-5">
<div class="mb-3">
<label class="form-label">Upload Foto Buket</label>
<div class="mb-2"> {{-- Area Upload Custom --}}
<label class="form-label">Upload Foto Paket</label> <div class="upload-area p-4 text-centerd d-flex flex-column align-items-center justify-content-center"
<div class="upload-area p-2 text-center d-flex flex-column align-items-center justify-content-center" onclick="document.getElementById('createFotoInput').click()"
onclick="document.getElementById('fileInput').click()"> style="cursor: pointer;">
<i class="bi bi-file-earmark-arrow-up fs-5 text-secondary mb-3"></i> <i class="bi bi-file-earmark-arrow-up fs-5 text-secondary mb-3"></i>
<span class="fw-semibold text-dark">Upload Foto Paket</span> <span class="fw-semibold text-dark">Klik untuk Upload</span>
<small class="text-muted">Max. 2 MB</small> <small class="text-muted">Max 2MB (JPG/PNG)</small>
<input type="file" id="fileInput" class="d-none" name="foto"> {{-- Input File Tersembunyi (ID UNIK: createFotoInput) --}}
<input type="file" id="createFotoInput" class="d-none" name="foto"
accept="image/*"
onchange="previewImage(this, 'createImgPreview', 'createPlaceholder')">
</div> </div>
@error('foto')
<div class="text-danger mt-1" style="font-size: 12px;">{{ $message }}</div>
@enderror
</div> </div>
<div class="mb-2"> {{-- 7. Preview Foto (Fixed Container) --}}
<label class="form-label">Preview Foto</label> <div class="mb-3">
<label class="form-label">Preview</label>
<div class="border rounded d-flex justify-content-center align-items-center position-relative overflow-hidden"
style="height: 120px; background-color: #f8f9fa;">
<div class="border rounded d-flex justify-content-center align-items-center position-relative" {{-- Placeholder Teks (ID UNIK: createPlaceholder) --}}
style="height: 120px; background-color: #f8f9fa; overflow: hidden;"> <div id="createPlaceholder" class="text-center text-muted">
<i class="bi bi-image fs-4"></i>
<div id="placeholder-text" class="text-center text-muted"> <p class="mb-0" style="font-size: 12px;">Belum ada foto dipilih</p>
<i class="bi bi-image fs-5 mb-2"></i>
<p class="mb-0 fw-medium" style="font-size: 0.65rem;">Gambarmu akan muncul
di
sini</p>
</div> </div>
<img id="img-preview" src="#" class="img-fluid w-100 h-100 d-none" {{-- Gambar Preview (ID UNIK: createImgPreview) --}}
style="object-fit: cover; position: absolute; top: 0; left: 0;"> <img id="createImgPreview" src="#" class="d-none w-100 h-100"
style="object-fit: contain;">
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="modal-footer justify-content-end border-top-0 pt-0"> <div class="modal-footer justify-content-end border-top-0 pt-0">
<button type="submit" class="btn btn-primary rounded-pill terima px-3 py-2"> <button type="submit" class="btn btn-primary rounded-pill terima px-3 py-2">
Simpan Simpan
</button> </button>
</div> </div>
</form> </form>
</div>
</div> </div>
</div> </div>
</div> </div>
<script>
// Target elemen berdasarkan ID
const fileInput = document.getElementById('fileInput');
const imgPreview = document.getElementById('img-preview');
const placeholder = document.getElementById('placeholder-text');
fileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
// Jika ada file, baca gambarnya
const reader = new FileReader();
reader.onload = function(e) {
imgPreview.src = e.target.result; // Masukkan data gambar
// TUKAR TAMPILAN:
imgPreview.classList.remove('d-none'); // Munculkan gambar
placeholder.classList.add('d-none'); // Sembunyikan teks
}
reader.readAsDataURL(file);
} else {
// Jika user membatalkan upload (cancel), reset ke awal
imgPreview.src = "#";
imgPreview.classList.add('d-none');
placeholder.classList.remove('d-none');
}
});
</script>

View File

@ -1,15 +1,17 @@
<div class="modal fade" id="deleteFoto" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="deleteFoto{{ $f->id_paket }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered "> <div class="modal-dialog modal-dialog-centered ">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Hapus Paket Foto</h5> <button type="button" class="btn-close" <h5 class="modal-title">Hapus {{ $f->nama }}</h5> <button type="button" class="btn-close"
data-bs-dismiss="modal" aria-label="Close"></button> data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<form action="#" method="POST" enctype="multipart/form-data"> <form action="{{ route('admin.paket-foto.destroy', $f->id_paket) }}" method="POST">
@csrf
@method('DELETE')
<div class="modal-body"> <div class="modal-body">
<p>Apakah anda yakin ingin menghapus nama paket?</p> <p>Apakah anda yakin ingin menghapus {{ $f->nama }}?</p>
</div> </div>
<div class="modal-footer justify-content-end border-top-0 pt-0"> <div class="modal-footer justify-content-end border-top-0 pt-0">
<button type="submit" class="btn btn-danger rounded-pill tolak px-3 py-2"> <button type="submit" class="btn btn-danger rounded-pill tolak px-3 py-2">
@ -21,33 +23,3 @@
</div> </div>
</div> </div>
</div> </div>
<script>
// Target elemen berdasarkan ID
const fileInput = document.getElementById('fileInput');
const imgPreview = document.getElementById('img-preview');
const placeholder = document.getElementById('placeholder-text');
fileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
// Jika ada file, baca gambarnya
const reader = new FileReader();
reader.onload = function(e) {
imgPreview.src = e.target.result; // Masukkan data gambar
// TUKAR TAMPILAN:
imgPreview.classList.remove('d-none'); // Munculkan gambar
placeholder.classList.add('d-none'); // Sembunyikan teks
}
reader.readAsDataURL(file);
} else {
// Jika user membatalkan upload (cancel), reset ke awal
imgPreview.src = "#";
imgPreview.classList.add('d-none');
placeholder.classList.remove('d-none');
}
});
</script>

View File

@ -1,14 +1,17 @@
<div class="modal fade" id="editFoto" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="editFoto{{ $f->id_paket }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Edit Paket Foto</h5> <button type="button" class="btn-close" <h5 class="modal-title">Edit {{ $f->nama }}</h5> <button type="button" class="btn-close"
data-bs-dismiss="modal" aria-label="Close"></button> data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<form action="#" method="POST" enctype="multipart/form-data"> <form action="{{ route('admin.paket-foto.update', $f->id_paket) }}" method="POST"
enctype="multipart/form-data">
@csrf
@method('PUT')
<div class="modal-body"> <div class="modal-body">
<div class="row gx-3"> <div class="row gx-3">
@ -16,53 +19,72 @@
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Nama Paket</label> <label class="form-label">Nama Paket</label>
<input type="text" class="form-control" style="font-size: 13px;" <input type="text" name="nama"
placeholder="Masukkan Nama Paket"> class="form-control @error('nama') is-invalid @enderror" style="font-size: 14px;"
placeholder="Masukkan Nama Paket"value="{{ old('nama', $f->nama) }}">
@error('nama')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div> </div>
<div class="row gx-1"> <div class="row gx-1">
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Harga Paket</label> <label class="form-label">Harga Paket</label>
<input type="number" class="form-control" style="font-size: 13px;" <input type="number" name="harga"
placeholder="Harga Paket"> class="form-control @error('harga') is-invalid @enderror"
style="font-size: 14px;"
placeholder="Harga Paket"value="{{ old('harga', $f->harga) }}">
<p class="mb-0"><small class="text-muted mb-0">Dalam Rupiah</small> <p class="mb-0"><small class="text-muted mb-0">Dalam Rupiah</small>
</p> </p>
@error('harga')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div> </div>
</div>
</div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Durasi</label> <label class="form-label">Durasi</label>
<input type="number" class="form-control" style="font-size: 13px;" <input type="number" name="durasi"
placeholder="Durasi Paket"> class="form-control @error('durasi') is-invalid @enderror"
style="font-size: 14px;"
placeholder="Durasi Paket"value="{{ old('durasi', $f->durasi) }}">
<p class="mb-0"><small class="text-muted mb-0">Dalam Menit</small> <p class="mb-0"><small class="text-muted mb-0">Dalam Menit</small>
</p> </p>
@error('durasi')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div> </div>
</div> </div>
</div> </div>
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Deskripsi Paket</label> <label class="form-label">Deskripsi Paket</label>
<textarea class="form-control" rows="4" style="font-size: 13px;" placeholder="Masukkan Deskripsi Paket"></textarea> <textarea class="form-control @error('deskripsi') is-invalid @enderror" rows="5" name="deskripsi"
style="font-size: 14px;" placeholder="Masukkan Deskripsi Paket"> {{ old('deskripsi', $f->deskripsi) }}</textarea>
@error('deskripsi')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div> </div>
</div> </div>
<div class="col-12 col-md-5"> <div class="col-12 col-md-5">
<div class="mb-2"> <div class="mb-2">
<label class="form-label">Upload Foto Paket</label> <label class="form-label">Upload Foto Paket</label>
<div class="upload-area p-2 text-center d-flex flex-column align-items-center justify-content-center" <div class="upload-area p-2 text-center d-flex flex-column align-items-center justify-content-center"
onclick="document.getElementById('fileInput').click()"> onclick="document.getElementById('editFotoInput{{ $f->id_paket }}').click()">
<i class="bi bi-file-earmark-arrow-up fs-5 text-secondary mb-3"></i> <i class="bi bi-file-earmark-arrow-up fs-5 text-secondary mb-3"></i>
<span class="fw-semibold text-dark">Upload Foto Paket</span> <span class="fw-semibold text-dark">Upload Foto Paket</span>
<small class="text-muted">Max. 2 MB</small> <small class="text-muted">Max. 2 MB</small>
<input type="file" id="fileInput" class="d-none" name="foto"> <input type="file" id="editFotoInput{{ $f->id_paket }}" class="d-none"
name="foto" accept="image/*"
onchange="previewImage(this, 'editImgPreview{{ $f->id_paket }}', 'placeholder-edit-{{ $f->id_paket }}')">
</div> </div>
@error('foto')
<div class="text-danger mt-1" style="font-size: 12px;">{{ $message }}</div>
@enderror
</div> </div>
<div class="mb-2"> <div class="mb-2">
@ -71,15 +93,16 @@
<div class="border rounded d-flex justify-content-center align-items-center position-relative" <div class="border rounded d-flex justify-content-center align-items-center position-relative"
style="height: 120px; background-color: #f8f9fa; overflow: hidden;"> style="height: 120px; background-color: #f8f9fa; overflow: hidden;">
<div id="placeholder-text" class="text-center text-muted"> <div id="placeholder-edit-{{ $f->id_paket }}"
class="text-center text-muted d-none">
<i class="bi bi-image fs-5 mb-2"></i> <i class="bi bi-image fs-5 mb-2"></i>
<p class="mb-0 fw-medium" style="font-size: 0.65rem;">Gambarmu akan muncul di <p class="mb-0 fw-medium" style="font-size: 0.65rem;">Gambarmu akan muncul di
sini</p> sini</p>
</div> </div>
<img id="img-preview" src="#" class="img-fluid w-100 h-100 d-none" <img id="editImgPreview{{ $f->id_paket }}" src="{{ asset($f->foto) }}"
class="img-fluid w-100 h-100"
style="object-fit: cover; position: absolute; top: 0; left: 0;"> style="object-fit: cover; position: absolute; top: 0; left: 0;">
</div> </div>
</div> </div>
@ -95,33 +118,3 @@
</div> </div>
</div> </div>
</div> </div>
<script>
// Target elemen berdasarkan ID
const fileInput = document.getElementById('fileInput');
const imgPreview = document.getElementById('img-preview');
const placeholder = document.getElementById('placeholder-text');
fileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
// Jika ada file, baca gambarnya
const reader = new FileReader();
reader.onload = function(e) {
imgPreview.src = e.target.result; // Masukkan data gambar
// TUKAR TAMPILAN:
imgPreview.classList.remove('d-none'); // Munculkan gambar
placeholder.classList.add('d-none'); // Sembunyikan teks
}
reader.readAsDataURL(file);
} else {
// Jika user membatalkan upload (cancel), reset ke awal
imgPreview.src = "#";
imgPreview.classList.add('d-none');
placeholder.classList.remove('d-none');
}
});
</script>

View File

@ -1,29 +1,35 @@
<div class="modal fade" id="foto" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="foto{{ $f->id_paket }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg"> <div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Detail Nama Paket Foto</h5> <button type="button" class="btn-close" <h5 class="modal-title">Detail {{ $f->nama }}</h5> <button type="button" class="btn-close"
data-bs-dismiss="modal" aria-label="Close"></button> data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="row gx-3"> <div class="row gx-3">
<div class="col-12 col-md-4"> <div class="col-12 col-sm-4">
<div class="buket-img-wrapper text-align-center h-100" @if ($f->foto)
onclick="showImage('{{ asset('img/invoice.jpg') }}')"> {{-- Langsung img tanpa wrapper --}}
<img src="{{ asset('img/invoice.jpg') }}" class="proof-img"> <img src="{{ asset($f->foto) }}" class="custom-img-box-foto"
</div> onclick="showImage('{{ asset($f->foto) }}')">
@else
{{-- Div pengganti kalau tidak ada foto --}}
<div
class="custom-img-box-foto d-flex align-items-center justify-content-center text-muted">
<small>Tidak ada foto</small>
</div>
@endif
</div> </div>
<div class="col-12 col-md-8"> <div class="col-12 col-sm-8">
<div class="detail-buket ps-md-3 h-100"> <div class="detail-buket ps-md-3 h-100">
<div class="row mb-2"> <div class="row mb-2">
<div class="col-4 col-sm-3"> <div class="col-4 col-sm-3">
<span class="detail-buket-label">Nama Paket</span> <span class="detail-buket-label">Nama Paket</span>
</div> </div>
<div class="col-8 col-sm-9"> <div class="col-8 col-sm-9">
<span class="detail-buket-value">Single</span> <span class="detail-buket-value">{{ $f->nama }}</span>
</div> </div>
</div> </div>
@ -32,7 +38,16 @@
<span class="detail-buket-label">Harga Paket</span> <span class="detail-buket-label">Harga Paket</span>
</div> </div>
<div class="col-8 col-sm-9"> <div class="col-8 col-sm-9">
<span class="detail-buket-value">Rp 130.000</span> <span class="detail-buket-value">Rp
{{ number_format($f->harga, 0, ',', '.') }}</span>
</div>
</div>
<div class="row mb-2">
<div class="col-4 col-sm-3">
<span class="detail-buket-label">Durasi Paket</span>
</div>
<div class="col-8 col-sm-9">
<span class="detail-buket-value">{{ $f->durasi }} Menit</span>
</div> </div>
</div> </div>
@ -42,21 +57,10 @@
</div> </div>
<div class="col-8 col-sm-9"> <div class="col-8 col-sm-9">
<p class="detail-buket-value text-justify mb-0" style="line-height: 1.6;"> <p class="detail-buket-value text-justify mb-0" style="line-height: 1.6;">
Untuk 1 menit {{ $f->deskripsi }}
10 menit sesi foto sepuasnya
5 menit sesi pilih foto (jika ada yang diprint)
</p> </p>
</div> </div>
</div> </div>
<div class="row mb-2">
<div class="col-4 col-sm-3">
<span class="detail-buket-label text-nowrap">Request Khusus</span>
</div>
<div class="col-8 col-sm-9">
<span class="detail-buket-value">Wrapping, Tone Warna</span>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -65,33 +69,16 @@
</div> </div>
</div> </div>
</div> </div>
<script>
// Target elemen berdasarkan ID
const fileInput = document.getElementById('fileInput');
const imgPreview = document.getElementById('img-preview');
const placeholder = document.getElementById('placeholder-text');
fileInput.addEventListener('change', function(event) { <div class="modal fade" id="modalImagePreview" tabindex="-1" aria-hidden="true">
const file = event.target.files[0]; <div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content bg-transparent border-0 shadow-none">
if (file) { <div class="modal-body p-0 text-center">
// Jika ada file, baca gambarnya <img id="img-preview-target" src="" class="img-fluid rounded shadow-lg"
const reader = new FileReader(); style="max-height: 85vh;">
</div>
reader.onload = function(e) { </div>
imgPreview.src = e.target.result; // Masukkan data gambar </div>
</div>
// TUKAR TAMPILAN:
imgPreview.classList.remove('d-none'); // Munculkan gambar
placeholder.classList.add('d-none'); // Sembunyikan teks
}
reader.readAsDataURL(file);
} else {
// Jika user membatalkan upload (cancel), reset ke awal
imgPreview.src = "#";
imgPreview.classList.add('d-none');
placeholder.classList.remove('d-none');
}
});
</script>