add logic in foto controller and paket foto section for crud paket foto
This commit is contained in:
parent
42c6961eb2
commit
f9eb2397af
|
|
@ -3,7 +3,12 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Additional;
|
||||
use App\Models\PaketFoto;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
|
||||
class FotoController extends Controller
|
||||
{
|
||||
|
|
@ -12,54 +17,132 @@ class FotoController extends Controller
|
|||
*/
|
||||
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)
|
||||
{
|
||||
//
|
||||
$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)
|
||||
{
|
||||
//
|
||||
$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)
|
||||
{
|
||||
//
|
||||
// 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!');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,21 @@
|
|||
@section('title', 'Paket Foto')
|
||||
|
||||
@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">
|
||||
<div class="d-flex gap-2 pb-3">
|
||||
|
||||
|
|
@ -39,36 +54,54 @@
|
|||
<table class="table table-striped" id="table1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No.</th>
|
||||
<th>Nama Paket</th>
|
||||
<th>Deskripsi</th>
|
||||
<th>Harga</th>
|
||||
<th>Foto</th>
|
||||
<th class="text-center">Aksi</th>
|
||||
<th style="width: 5%" class="text-center">No.</th>
|
||||
<th style="width: 20%">Nama Paket</th>
|
||||
<th style="width: 35%">Deskripsi</th>
|
||||
<th style="width: 15%" class="text-nowrap">Harga</th>
|
||||
<th style="width: 10%" class="text-center">Foto</th>
|
||||
<th style="width: 15%" class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Graiden</td>
|
||||
<td>vehicula.aliquet@semconsequat.co.uk</td>
|
||||
<td>076 4820 8838</td>
|
||||
<td>Offenburg</td>
|
||||
<td>Offenburg</td>
|
||||
<td class="col-auto text-center">
|
||||
<a href="#" class="btn icon btn-primary btn-action" data-bs-toggle="modal"
|
||||
data-bs-target="#foto">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a href="#" class="btn icon btn-warning btn-action" data-bs-toggle="modal"
|
||||
data-bs-target="#editFoto">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a href="#" class="btn icon btn-danger btn-action" data-bs-toggle="modal"
|
||||
data-bs-target="#deleteFoto">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@forelse ($foto as $f)
|
||||
<tr>
|
||||
<td style="width: 5%" class="text-center">{{ $loop->iteration }}</td>
|
||||
<td style="width: 20%">{{ $f->nama }}</td>
|
||||
<td style="width: 35%">{{ Str::limit($f->deskripsi, 50) }}</td>
|
||||
<td style="width: 15%">Rp {{ number_format($f->harga, 0, ',', '.') }}</td>
|
||||
<td style="width:10%" class="text-center">
|
||||
<img src="{{ asset($f->foto) }}" alt="Foto Produk" class="rounded"
|
||||
style="width: 50px; height: 50px; object-fit: cover;">
|
||||
</td>
|
||||
<td class="col-auto text-center" style="width: 15%">
|
||||
<a href="#" class="btn icon btn-primary btn-action" data-bs-toggle="modal"
|
||||
data-bs-target="#foto{{ $f->id_paket }}">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
|
||||
<a href="#" class="btn icon btn-warning btn-action" data-bs-toggle="modal"
|
||||
data-bs-target="#editFoto{{ $f->id_paket }}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
|
||||
<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>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -77,28 +110,44 @@
|
|||
<table class="table table-striped" id="riwayat-foto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No.</th>
|
||||
<th>Nama Additional</th>
|
||||
<th>Harga</th>
|
||||
<th class="text-center">Aksi</th>
|
||||
<th style="width: 5%">No.</th>
|
||||
<th style="width: 65%">Nama Additional</th>
|
||||
<th style="width: 15%" class="text-nowrap">Harga</th>
|
||||
<th style="width: 15%" class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Graiden</td>
|
||||
<td>vehicula.aliquet@semconsequat.co.uk</td>
|
||||
<td>076 4820 8838</td>
|
||||
<td class="col-auto text-center">
|
||||
<a href="#" class="btn icon btn-warning btn-action" data-bs-toggle="modal"
|
||||
data-bs-target="#editAdd">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<a href="#" class="btn icon btn-danger btn-action" data-bs-toggle="modal"
|
||||
data-bs-target="#deleteAdd">
|
||||
<i class="bi bi-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@forelse ($additional as $ad)
|
||||
<tr>
|
||||
<td style="width: 5%">{{ $loop->iteration }}</td>
|
||||
<td style="width: 65%">{{ $ad->nama }}</td>
|
||||
<td style="width: 15%">Rp {{ number_format($ad->harga, 0, ',', '.') }}</td>
|
||||
<td class="col-auto text-center" style="width: 15%">
|
||||
<a href="#" class="btn icon btn-warning btn-action"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editFoto{{ $ad->id_additional }}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
|
||||
<a href="#" class="btn icon btn-danger btn-action"
|
||||
data-bs-toggle="modal"
|
||||
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>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -115,5 +164,60 @@
|
|||
@include('admin.paket-foto.partials.modal-create-additional')
|
||||
@include('admin.paket-foto.partials.modal-edit-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
|
||||
|
|
|
|||
|
|
@ -1,32 +1,40 @@
|
|||
<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-header">
|
||||
<h5 class="modal-title">Tambah Paket Foto</h5> <button type="button" class="btn-close"
|
||||
data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
|
||||
<form action="#" method="POST" enctype="multipart/form-data">
|
||||
<div class="modal-body">
|
||||
<div class="modal-body">
|
||||
<form action="{{ route('admin.paket-foto.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="row gx-3">
|
||||
|
||||
<div class="col-12 col-md-7">
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Nama Paket</label>
|
||||
<input type="text" class="form-control" style="font-size: 13px;"
|
||||
placeholder="Masukkan Nama Paket">
|
||||
<input type="text" class="form-control @error('nama') is-invalid @enderror"
|
||||
style="font-size: 14px;" placeholder="Masukkan Nama Paket"
|
||||
value="{{ old('nama') }}">
|
||||
@error('nama')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="row gx-1">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Harga Paket</label>
|
||||
<input type="number" class="form-control" style="font-size: 13px;"
|
||||
placeholder="Harga Paket">
|
||||
<input type="number" class="form-control @error('nama') is-invalid @enderror"
|
||||
style="font-size: 14px;"
|
||||
placeholder="Harga Paket"value="{{ old('nama') }}">
|
||||
<p class="mb-0"><small class="text-muted mb-0">Dalam Rupiah</small>
|
||||
</p>
|
||||
@error('nama')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -34,95 +42,82 @@
|
|||
<div class="col-12 col-md-6">
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Durasi</label>
|
||||
<input type="number" class="form-control" style="font-size: 13px;"
|
||||
placeholder="Durasi Paket">
|
||||
<input type="number" class="form-control @error('nama') is-invalid @enderror"
|
||||
style="font-size: 14px;"
|
||||
placeholder="Durasi Paket"value="{{ old('nama') }}">
|
||||
<p class="mb-0"><small class="text-muted mb-0">Dalam Menit</small>
|
||||
</p>
|
||||
@error('nama')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<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 class="col-12 col-md-5">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Upload Foto Buket</label>
|
||||
|
||||
<div class="mb-2">
|
||||
<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"
|
||||
onclick="document.getElementById('fileInput').click()">
|
||||
{{-- Area Upload Custom --}}
|
||||
<div class="upload-area p-4 text-centerd d-flex flex-column align-items-center justify-content-center"
|
||||
onclick="document.getElementById('createFotoInput').click()"
|
||||
style="cursor: pointer;">
|
||||
|
||||
<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>
|
||||
<small class="text-muted">Max. 2 MB</small>
|
||||
<span class="fw-semibold text-dark">Klik untuk Upload</span>
|
||||
<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>
|
||||
@error('foto')
|
||||
<div class="text-danger mt-1" style="font-size: 12px;">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Preview Foto</label>
|
||||
{{-- 7. Preview Foto (Fixed Container) --}}
|
||||
<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"
|
||||
style="height: 120px; background-color: #f8f9fa; overflow: hidden;">
|
||||
|
||||
<div id="placeholder-text" class="text-center text-muted">
|
||||
<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>
|
||||
{{-- Placeholder Teks (ID UNIK: createPlaceholder) --}}
|
||||
<div id="createPlaceholder" class="text-center text-muted">
|
||||
<i class="bi bi-image fs-4"></i>
|
||||
<p class="mb-0" style="font-size: 12px;">Belum ada foto dipilih</p>
|
||||
</div>
|
||||
|
||||
<img id="img-preview" src="#" class="img-fluid w-100 h-100 d-none"
|
||||
style="object-fit: cover; position: absolute; top: 0; left: 0;">
|
||||
|
||||
{{-- Gambar Preview (ID UNIK: createImgPreview) --}}
|
||||
<img id="createImgPreview" src="#" class="d-none w-100 h-100"
|
||||
style="object-fit: contain;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<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">
|
||||
Simpan
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -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-content">
|
||||
|
||||
<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>
|
||||
</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">
|
||||
<p>Apakah anda yakin ingin menghapus nama paket?</p>
|
||||
<p>Apakah anda yakin ingin menghapus {{ $f->nama }}?</p>
|
||||
</div>
|
||||
<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">
|
||||
|
|
@ -21,33 +23,3 @@
|
|||
</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>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
<div class="modal fade" id="editFoto" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal fade" id="editFoto{{ $f->id_paket }}" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
|
||||
<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>
|
||||
</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="row gx-3">
|
||||
|
||||
|
|
@ -16,53 +19,72 @@
|
|||
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Nama Paket</label>
|
||||
<input type="text" class="form-control" style="font-size: 13px;"
|
||||
placeholder="Masukkan Nama Paket">
|
||||
<input type="text" name="nama"
|
||||
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 class="row gx-1">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Harga Paket</label>
|
||||
<input type="number" class="form-control" style="font-size: 13px;"
|
||||
placeholder="Harga Paket">
|
||||
<input type="number" name="harga"
|
||||
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>
|
||||
|
||||
@error('harga')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="mb-2">
|
||||
<label class="form-label">Durasi</label>
|
||||
<input type="number" class="form-control" style="font-size: 13px;"
|
||||
placeholder="Durasi Paket">
|
||||
<input type="number" name="durasi"
|
||||
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>
|
||||
@error('durasi')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<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 class="col-12 col-md-5">
|
||||
|
||||
<div class="mb-2">
|
||||
<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"
|
||||
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>
|
||||
<span class="fw-semibold text-dark">Upload Foto Paket</span>
|
||||
<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>
|
||||
@error('foto')
|
||||
<div class="text-danger mt-1" style="font-size: 12px;">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
|
|
@ -71,15 +93,16 @@
|
|||
<div class="border rounded d-flex justify-content-center align-items-center position-relative"
|
||||
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>
|
||||
<p class="mb-0 fw-medium" style="font-size: 0.65rem;">Gambarmu akan muncul di
|
||||
sini</p>
|
||||
</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;">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -95,33 +118,3 @@
|
|||
</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>
|
||||
|
|
|
|||
|
|
@ -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-content">
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="row gx-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="buket-img-wrapper text-align-center h-100"
|
||||
onclick="showImage('{{ asset('img/invoice.jpg') }}')">
|
||||
<img src="{{ asset('img/invoice.jpg') }}" class="proof-img">
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-4">
|
||||
@if ($f->foto)
|
||||
{{-- Langsung img tanpa wrapper --}}
|
||||
<img src="{{ asset($f->foto) }}" class="custom-img-box-foto"
|
||||
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 class="col-12 col-md-8">
|
||||
<div class="col-12 col-sm-8">
|
||||
<div class="detail-buket ps-md-3 h-100">
|
||||
<div class="row mb-2">
|
||||
<div class="col-4 col-sm-3">
|
||||
<span class="detail-buket-label">Nama Paket</span>
|
||||
</div>
|
||||
<div class="col-8 col-sm-9">
|
||||
<span class="detail-buket-value">Single</span>
|
||||
<span class="detail-buket-value">{{ $f->nama }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -32,7 +38,16 @@
|
|||
<span class="detail-buket-label">Harga Paket</span>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
|
|
@ -42,21 +57,10 @@
|
|||
</div>
|
||||
<div class="col-8 col-sm-9">
|
||||
<p class="detail-buket-value text-justify mb-0" style="line-height: 1.6;">
|
||||
Untuk 1 menit
|
||||
10 menit sesi foto sepuasnya
|
||||
5 menit sesi pilih foto (jika ada yang diprint)
|
||||
{{ $f->deskripsi }}
|
||||
</p>
|
||||
</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>
|
||||
|
|
@ -65,33 +69,16 @@
|
|||
</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];
|
||||
<div class="modal fade" id="modalImagePreview" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content bg-transparent border-0 shadow-none">
|
||||
|
||||
if (file) {
|
||||
// Jika ada file, baca gambarnya
|
||||
const reader = new FileReader();
|
||||
<div class="modal-body p-0 text-center">
|
||||
<img id="img-preview-target" src="" class="img-fluid rounded shadow-lg"
|
||||
style="max-height: 85vh;">
|
||||
</div>
|
||||
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue