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\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class BookingFotoController extends Controller
|
class BookingFotoController extends Controller
|
||||||
{
|
{
|
||||||
|
|
@ -157,39 +158,46 @@ public function store(Request $request)
|
||||||
'nama' => 'required|string|min:3|max:100',
|
'nama' => 'required|string|min:3|max:100',
|
||||||
'no_wa' => 'required|numeric|digits_between:10,15',
|
'no_wa' => 'required|numeric|digits_between:10,15',
|
||||||
'bukti_bayar' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
'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()) {
|
DB::beginTransaction(); // Mulai Transaksi Database
|
||||||
return back()->withErrors($validator)->withInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
\Illuminate\Support\Facades\DB::beginTransaction(); // Mulai Transaksi Database
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2. Ambil Data Paket & Hitung Waktu (Termasuk Tambahan Menit dari Addons)
|
// 2. Ambil Data Paket & Hitung Waktu
|
||||||
$paket = \App\Models\PaketFoto::findOrFail($request->id_paket);
|
$paket = PaketFoto::findOrFail($request->id_paket);
|
||||||
$totalDurasi = $paket->durasi;
|
|
||||||
|
// 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;
|
$grandTotal = $paket->harga;
|
||||||
$listAdditional = [];
|
$listAdditional = [];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ public function index(Request $request)
|
||||||
$buket = $query->paginate(8)->withQueryString();
|
$buket = $query->paginate(8)->withQueryString();
|
||||||
|
|
||||||
if ($request->ajax()) {
|
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'));
|
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
|
// Membuat nama file unik berdasarkan waktu agar tidak tertimpa
|
||||||
$namaFile = 'bukti_' . time() . $file->getClientOriginalExtension();
|
$namaFile = 'bukti_' . time() . $file->getClientOriginalExtension();
|
||||||
// Pindahkan ke folder public/img/payment
|
// 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([
|
$transaksi = TransaksiBuket::create([
|
||||||
|
|
@ -126,7 +126,7 @@ public function store(Request $request)
|
||||||
'tgl_ambil' => $request->tgl_ambil . ' ' . $request->waktu_ambil,
|
'tgl_ambil' => $request->tgl_ambil . ' ' . $request->waktu_ambil,
|
||||||
'request' => $request->request_khusus, // Ubah dari request_khusus ke request
|
'request' => $request->request_khusus, // Ubah dari request_khusus ke request
|
||||||
'ucapan' => $request->ucapan,
|
'ucapan' => $request->ucapan,
|
||||||
'bukti_bayar' => 'img/payment/' . $namaFile,
|
'bukti_bayar' => 'img/payment/buket' . $namaFile,
|
||||||
'status_transaksi' => 'menunggu_verifikasi', // Ubah dari status ke status_transaksi
|
'status_transaksi' => 'menunggu_verifikasi', // Ubah dari status ke status_transaksi
|
||||||
'total_bayar' => $buket->harga, // Tambahkan ini karena total_bayar wajib di fillable
|
'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
|
'no_invoice' => 'INV-BUKET-' . strtoupper(\Illuminate\Support\Str::random(6)), // Tambahkan invoice sederhana
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ class Buket extends Model
|
||||||
'ucapan',
|
'ucapan',
|
||||||
'harga',
|
'harga',
|
||||||
'foto',
|
'foto',
|
||||||
'kategori', // Enum
|
'kategori',
|
||||||
'ukuran', // Enum
|
'ukuran',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function transaksi()
|
public function transaksi()
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="bookingfoto-card position-relative">
|
<div class="bookingfoto-card position-relative">
|
||||||
<div class="img-wrapper mb-3">
|
<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 }}">
|
alt="{{ $f->nama }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
<div class="col-6 col-md-4 col-lg-3">
|
<div class="col-6 col-md-4 col-lg-3">
|
||||||
<div class="katalogbuket-card h-100 position-relative">
|
<div class="katalogbuket-card h-100 position-relative">
|
||||||
<div class="img-wrapper mb-3">
|
<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>
|
</div>
|
||||||
<h6 class="katalogbuket-product-title">{{ $b->nama }}</h6>
|
<h6 class="katalogbuket-product-title">{{ $b->nama }}</h6>
|
||||||
<p class="katalogbuket-product-price">Rp {{ number_format($b->harga, 0, ',', '.') }}</p>
|
<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="col-lg-5 mb-5 mb-lg-0">
|
||||||
<div class="detailbuket-img-frame">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<div class="row gx-3">
|
<div class="row gx-3">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<div class="detailfoto-card p-2">
|
<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">
|
class="detailfoto-thumb rounded-4">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
<h4 class="formulirbuket-section-title mb-3">Rincian Pesanan</h4>
|
<h4 class="formulirbuket-section-title mb-3">Rincian Pesanan</h4>
|
||||||
<div class="formulirbuket-card d-flex flex-column gap-2">
|
<div class="formulirbuket-card d-flex flex-column gap-2">
|
||||||
<div class="formulirbuket-product-summary d-flex align-items-center gap-3">
|
<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>
|
<div>
|
||||||
<h6 class=" mb-1">{{ $buket->nama }}</h6>
|
<h6 class=" mb-1">{{ $buket->nama }}</h6>
|
||||||
<p class="text-teal mb-0">Rp {{ number_format($buket->harga, 0, ',', '.') }}</p>
|
<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>
|
</section>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/15.7.1/nouislider.min.js"></script>
|
<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 src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
let timeout = null;
|
let timeout = null;
|
||||||
|
|
@ -175,10 +176,12 @@ function fetchBuket(url = "{{ route('pesan.buket') }}") {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event live Search
|
$(document).on('input', '#input-search', function() {
|
||||||
$('#input-search').on('keyup', function() {
|
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
let keyword = $(this).val(); // Ambil value saat ini
|
||||||
|
|
||||||
timeout = setTimeout(function() {
|
timeout = setTimeout(function() {
|
||||||
|
// Opsional: Cek jika keyword kosong atau berubah
|
||||||
fetchBuket();
|
fetchBuket();
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue