refactor: update file handling to use Storage facade and improve image path references
This commit is contained in:
parent
192af00f2f
commit
cd8a6b6a6e
|
|
@ -11,6 +11,7 @@
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class BookingFotoController extends Controller
|
||||
{
|
||||
|
|
@ -157,39 +158,46 @@ public function store(Request $request)
|
|||
'nama' => 'required|string|min:3|max:100',
|
||||
'no_wa' => 'required|numeric|digits_between:10,15',
|
||||
'bukti_bayar' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
||||
], [
|
||||
// Detail Pesan Kustom
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'min' => ':attribute terlalu pendek, minimal :min karakter.',
|
||||
'max' => ':attribute terlalu panjang, maksimal :max karakter.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'digits_between' => ':attribute harus antara :min sampai :max digit.',
|
||||
'date' => 'Format tanggal pada :attribute tidak valid.',
|
||||
'after_or_equal' => ':attribute tidak boleh tanggal yang sudah lewat.',
|
||||
'image' => ':attribute harus berupa file gambar.',
|
||||
'mimes' => 'Format :attribute harus jpeg, png, atau jpg.',
|
||||
'max.file' => 'Ukuran :attribute maksimal adalah 2MB.',
|
||||
], [
|
||||
// Alias Atribut agar lebih ramah
|
||||
'id_paket' => 'paket foto',
|
||||
'nama' => 'nama pemesan',
|
||||
'no_wa' => 'nomor WhatsApp',
|
||||
'tgl_booking' => 'tanggal booking',
|
||||
'jam_mulai' => 'jam mulai',
|
||||
'bukti_bayar' => 'bukti pembayaran',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return back()->withErrors($validator)->withInput();
|
||||
}
|
||||
|
||||
\Illuminate\Support\Facades\DB::beginTransaction(); // Mulai Transaksi Database
|
||||
DB::beginTransaction(); // Mulai Transaksi Database
|
||||
|
||||
try {
|
||||
// 2. Ambil Data Paket & Hitung Waktu (Termasuk Tambahan Menit dari Addons)
|
||||
$paket = \App\Models\PaketFoto::findOrFail($request->id_paket);
|
||||
$totalDurasi = $paket->durasi;
|
||||
// 2. Ambil Data Paket & Hitung Waktu
|
||||
$paket = PaketFoto::findOrFail($request->id_paket);
|
||||
|
||||
// Asumsi durasi default 20 menit (atau ambil dari database jika ada kolom durasi)
|
||||
$durasiMenit = $paket->durasi;
|
||||
$jamMulai = \Carbon\Carbon::createFromFormat('H:i', $request->jam_mulai);
|
||||
$jamSelesai = $jamMulai->copy()->addMinutes($durasiMenit);
|
||||
|
||||
// 3. Cek Slot Sekali Lagi (Mencegah Race Condition)
|
||||
$isTaken = BookingFoto::where('tgl_booking', $request->tgl_booking)
|
||||
->where('jam_mulai', $request->jam_mulai)
|
||||
->whereIn('status_booking', ['menunggu_verifikasi', 'diterima', 'selesai'])
|
||||
->exists();
|
||||
|
||||
if ($isTaken) {
|
||||
return back()->with('error', 'Mohon maaf, slot waktu ini baru saja diambil orang lain.');
|
||||
}
|
||||
|
||||
// 4. Simpan/Update Data Pelanggan
|
||||
$pelanggan = Pelanggan::firstOrCreate(
|
||||
['no_wa' => $request->no_wa],
|
||||
['nama' => $request->nama]
|
||||
);
|
||||
|
||||
// 5. Upload Bukti Bayar
|
||||
$pathBukti = null;
|
||||
if ($request->hasFile('bukti_bayar')) {
|
||||
$file = $request->file('bukti_bayar');
|
||||
$namaFile = 'bukti_' . time() . '_' . Str::random(5) . '.' . $file->getClientOriginalExtension();
|
||||
$file->move(public_path('img/payment/foto'), $namaFile);
|
||||
$pathBukti = $file->storeAs('img/payment/foto', $namaFile, 'public');
|
||||
}
|
||||
|
||||
// 6. Hitung Grand Total (Paket + Additional)
|
||||
// Kita hitung ulang di server agar aman dari manipulasi inspect element
|
||||
$grandTotal = $paket->harga;
|
||||
$listAdditional = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public function index(Request $request)
|
|||
$buket = $query->paginate(8)->withQueryString();
|
||||
|
||||
if ($request->ajax()) {
|
||||
return view('user._list', compact('buket'))->render();
|
||||
return view('user.components._list', compact('buket'))->render();
|
||||
}
|
||||
|
||||
return view('user/pesan-buket', compact('buket'));
|
||||
|
|
@ -117,7 +117,7 @@ public function store(Request $request)
|
|||
// Membuat nama file unik berdasarkan waktu agar tidak tertimpa
|
||||
$namaFile = 'bukti_' . time() . $file->getClientOriginalExtension();
|
||||
// Pindahkan ke folder public/img/payment
|
||||
$file->move(public_path('img/payment/buket'), $namaFile);
|
||||
$pathBukti = $file->storeAs('img/payment/buket', $namaFile, 'public');
|
||||
}
|
||||
|
||||
$transaksi = TransaksiBuket::create([
|
||||
|
|
@ -126,7 +126,7 @@ public function store(Request $request)
|
|||
'tgl_ambil' => $request->tgl_ambil . ' ' . $request->waktu_ambil,
|
||||
'request' => $request->request_khusus, // Ubah dari request_khusus ke request
|
||||
'ucapan' => $request->ucapan,
|
||||
'bukti_bayar' => 'img/payment/' . $namaFile,
|
||||
'bukti_bayar' => 'img/payment/buket' . $namaFile,
|
||||
'status_transaksi' => 'menunggu_verifikasi', // Ubah dari status ke status_transaksi
|
||||
'total_bayar' => $buket->harga, // Tambahkan ini karena total_bayar wajib di fillable
|
||||
'no_invoice' => 'INV-BUKET-' . strtoupper(\Illuminate\Support\Str::random(6)), // Tambahkan invoice sederhana
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ class Buket extends Model
|
|||
'ucapan',
|
||||
'harga',
|
||||
'foto',
|
||||
'kategori', // Enum
|
||||
'ukuran', // Enum
|
||||
'kategori',
|
||||
'ukuran',
|
||||
];
|
||||
|
||||
public function transaksi()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<div class="col">
|
||||
<div class="bookingfoto-card position-relative">
|
||||
<div class="img-wrapper mb-3">
|
||||
<img src="{{ asset($f->foto) }}" class="img-fluid rounded-4"
|
||||
<img src="{{ asset('storage/' . $f->foto) }}" class="img-fluid rounded-4"
|
||||
alt="{{ $f->nama }}">
|
||||
</div>
|
||||
<div class="text-left">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
<div class="col-6 col-md-4 col-lg-3">
|
||||
<div class="katalogbuket-card h-100 position-relative">
|
||||
<div class="img-wrapper mb-3">
|
||||
<img src="{{ asset($b->foto) }}" class="img-fluid rounded-4" alt="{{ $b->nama }}">
|
||||
<img src="{{ asset('storage/' . $b->foto) }}" class="img-fluid rounded-4"
|
||||
alt="{{ $b->nama }}">
|
||||
</div>
|
||||
<h6 class="katalogbuket-product-title">{{ $b->nama }}</h6>
|
||||
<p class="katalogbuket-product-price">Rp {{ number_format($b->harga, 0, ',', '.') }}</p>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<div class="col-lg-5 mb-5 mb-lg-0">
|
||||
<div class="detailbuket-img-frame">
|
||||
<img src="{{ asset($buket->foto) }}" class="img-fluid" alt="{{ $buket->nama }}">
|
||||
<img src="{{ asset('storage/' . $buket->foto) }}" class="img-fluid" alt="{{ $buket->nama }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
<div class="row gx-3">
|
||||
<div class="col-auto">
|
||||
<div class="detailfoto-card p-2">
|
||||
<img src="{{ asset($foto->foto) }}" alt="{{ $foto->nama }}"
|
||||
<img src="{{ asset('storage/' . $foto->foto) }}" alt="{{ $foto->nama }}"
|
||||
class="detailfoto-thumb rounded-4">
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@
|
|||
<h4 class="formulirbuket-section-title mb-3">Rincian Pesanan</h4>
|
||||
<div class="formulirbuket-card d-flex flex-column gap-2">
|
||||
<div class="formulirbuket-product-summary d-flex align-items-center gap-3">
|
||||
<img src="{{ asset($buket->foto) }}" alt="{{ $buket->nama }}" class="rounded-3">
|
||||
<img src="{{ asset('storage/' . $buket->foto) }}" alt="{{ $buket->nama }}"
|
||||
class="rounded-3">
|
||||
<div>
|
||||
<h6 class=" mb-1">{{ $buket->nama }}</h6>
|
||||
<p class="text-teal mb-0">Rp {{ number_format($buket->harga, 0, ',', '.') }}</p>
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ class="page-number text-decoration-none">{{ $page }}</a>
|
|||
</section>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
let timeout = null;
|
||||
|
|
@ -175,10 +176,12 @@ function fetchBuket(url = "{{ route('pesan.buket') }}") {
|
|||
});
|
||||
}
|
||||
|
||||
// Event live Search
|
||||
$('#input-search').on('keyup', function() {
|
||||
$(document).on('input', '#input-search', function() {
|
||||
clearTimeout(timeout);
|
||||
let keyword = $(this).val(); // Ambil value saat ini
|
||||
|
||||
timeout = setTimeout(function() {
|
||||
// Opsional: Cek jika keyword kosong atau berubah
|
||||
fetchBuket();
|
||||
}, 500);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue