commit
0ceeaca884
|
|
@ -122,7 +122,7 @@ public function update(Request $request, string $id)
|
||||||
|
|
||||||
if ($request->hasFile('foto')) {
|
if ($request->hasFile('foto')) {
|
||||||
// 1. Hapus foto lama jika ada
|
// 1. Hapus foto lama jika ada
|
||||||
if ($buket->foto && file_exists(public_path($buket->foto))) {
|
if ($buket->foto) {
|
||||||
File::delete(public_path($buket->foto));
|
File::delete(public_path($buket->foto));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ public function destroy(string $id)
|
||||||
{
|
{
|
||||||
$buket = Buket::findOrFail($id);
|
$buket = Buket::findOrFail($id);
|
||||||
|
|
||||||
if ($buket->foto && File::exists(public_path($buket->foto))) {
|
if ($buket->foto) {
|
||||||
File::delete(public_path($buket->foto));
|
File::delete(public_path($buket->foto));
|
||||||
}
|
}
|
||||||
$buket->delete();
|
$buket->delete();
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public function store(Request $request)
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'nama' => 'required|string|min:3|max:100',
|
'nama' => 'required|string|min:3|max:100',
|
||||||
'harga' => 'required|numeric|min:0',
|
'harga' => 'required|numeric|min:0',
|
||||||
'durasi' => 'required|integer|min:1',
|
'durasi' => 'required|integer|min:0',
|
||||||
'deskripsi' => 'required|string|min:10',
|
'deskripsi' => 'required|string|min:10',
|
||||||
'foto' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
'foto' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
||||||
], [
|
], [
|
||||||
|
|
@ -84,7 +84,7 @@ public function update(Request $request, string $id)
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'nama' => 'required|string|min:3|max:100',
|
'nama' => 'required|string|min:3|max:100',
|
||||||
'harga' => 'required|numeric|min:0',
|
'harga' => 'required|numeric|min:0',
|
||||||
'durasi' => 'required|integer|min:1',
|
'durasi' => 'required|integer|min:0',
|
||||||
'deskripsi' => 'required|string|min:10',
|
'deskripsi' => 'required|string|min:10',
|
||||||
'foto' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
'foto' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||||
], [
|
], [
|
||||||
|
|
@ -115,7 +115,7 @@ public function update(Request $request, string $id)
|
||||||
|
|
||||||
if ($request->hasFile('foto')) {
|
if ($request->hasFile('foto')) {
|
||||||
// Hapus file lama jika ada foto baru yang diunggah
|
// Hapus file lama jika ada foto baru yang diunggah
|
||||||
if ($paket->foto && File::exists(public_path($paket->foto))) {
|
if ($paket->foto) {
|
||||||
File::delete(public_path($paket->foto));
|
File::delete(public_path($paket->foto));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,7 +136,7 @@ public function destroy(string $id)
|
||||||
$paket = PaketFoto::where('id_paket', $id)->firstOrFail();
|
$paket = PaketFoto::where('id_paket', $id)->firstOrFail();
|
||||||
|
|
||||||
// 1. Cek dan hapus file foto dari folder public
|
// 1. Cek dan hapus file foto dari folder public
|
||||||
if ($paket->foto && File::exists(public_path($paket->foto))) {
|
if ($paket->foto) {
|
||||||
File::delete(public_path($paket->foto));
|
File::delete(public_path($paket->foto));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public function index()
|
||||||
public function detail($id)
|
public function detail($id)
|
||||||
{
|
{
|
||||||
$foto = PaketFoto::findOrFail($id);
|
$foto = PaketFoto::findOrFail($id);
|
||||||
|
\Carbon\Carbon::setLocale('id');
|
||||||
// Jika add-ons disimpan di DB, ambil juga:
|
// Jika add-ons disimpan di DB, ambil juga:
|
||||||
$additionals = Additional::all();
|
$additionals = Additional::all();
|
||||||
// Logika Tanggal
|
// Logika Tanggal
|
||||||
|
|
@ -33,23 +33,25 @@ public function detail($id)
|
||||||
$prevMonth = $start->copy()->subMonth();
|
$prevMonth = $start->copy()->subMonth();
|
||||||
$nextMonth = $start->copy()->addMonth();
|
$nextMonth = $start->copy()->addMonth();
|
||||||
|
|
||||||
$currentMonthLabel = $start->format('F Y');
|
// translatedFormat akan mengikuti locale 'id' di config Anda
|
||||||
|
$currentMonthLabel = $start->isoFormat('MMMM YYYY');
|
||||||
return view('user.detail-foto', compact('foto', 'additionals', 'start', 'end', 'currentMonthLabel', 'prevMonth', 'nextMonth'));
|
return view('user.detail-foto', compact('foto', 'additionals', 'start', 'end', 'currentMonthLabel', 'prevMonth', 'nextMonth'));
|
||||||
}
|
}
|
||||||
public function loadCalendar(Request $request)
|
public function loadCalendar(Request $request)
|
||||||
{
|
{
|
||||||
|
\Carbon\Carbon::setLocale('id');
|
||||||
// Ambil bulan & tahun dari request AJAX, atau default sekarang
|
// Ambil bulan & tahun dari request AJAX, atau default sekarang
|
||||||
|
// $month = $request->month ?? date('m');
|
||||||
|
// $year = $request->year ?? date('Y');
|
||||||
$month = $request->month ?? date('m');
|
$month = $request->month ?? date('m');
|
||||||
$year = $request->year ?? date('Y');
|
$year = $request->year ?? date('Y');
|
||||||
|
|
||||||
$start = \Carbon\Carbon::createFromDate($year, $month, 1);
|
$start = \Carbon\Carbon::createFromDate($year, $month, 1);
|
||||||
|
|
||||||
// Data Navigasi
|
// Data Navigasi
|
||||||
$prevMonth = $start->copy()->subMonth();
|
$prevMonth = $start->copy()->subMonth();
|
||||||
$nextMonth = $start->copy()->addMonth();
|
$nextMonth = $start->copy()->addMonth();
|
||||||
$currentMonthLabel = $start->format('F Y');
|
// translatedFormat akan mengikuti locale 'id' di config Anda
|
||||||
|
$currentMonthLabel = $start->isoFormat('MMMM YYYY'); // Return hanya potongan HTML (Partial), bukan halaman full
|
||||||
// Return hanya potongan HTML (Partial), bukan halaman full
|
|
||||||
$html = view('user.components.calendar-grid', compact(
|
$html = view('user.components.calendar-grid', compact(
|
||||||
'start',
|
'start',
|
||||||
'prevMonth',
|
'prevMonth',
|
||||||
|
|
@ -74,31 +76,48 @@ public function formulir(Request $request)
|
||||||
{
|
{
|
||||||
// 1. Ambil Data Paket
|
// 1. Ambil Data Paket
|
||||||
$foto = PaketFoto::findOrFail($request->id_paket);
|
$foto = PaketFoto::findOrFail($request->id_paket);
|
||||||
|
$durasiDasar = $foto->durasi; // Ambil durasi paket asli
|
||||||
|
|
||||||
// 2. Hitung Total Add-ons (Jika ada)
|
// 2. Hitung Total Add-ons & Tambahan Menit
|
||||||
$addonsDetails = [];
|
$addonsDetails = [];
|
||||||
$totalAddon = 0;
|
$totalAddon = 0;
|
||||||
|
$tambahanMenit = 0; // Inisialisasi tambahan waktu
|
||||||
|
|
||||||
if ($request->has('addons')) {
|
if ($request->has('addons')) {
|
||||||
foreach ($request->addons as $id => $qty) {
|
foreach ($request->addons as $id => $qty) {
|
||||||
if ($qty > 0) {
|
if ($qty > 0) {
|
||||||
$add = Additional::find($id);
|
$add = Additional::find($id);
|
||||||
|
if ($add) {
|
||||||
$subtotal = $add->harga * $qty;
|
$subtotal = $add->harga * $qty;
|
||||||
$totalAddon += $subtotal;
|
$totalAddon += $subtotal;
|
||||||
|
|
||||||
|
// LOGIKA TAMBAHAN WAKTU BERDASARKAN ID
|
||||||
|
if ($id == 4) { // Tambah waktu/5 menit
|
||||||
|
$tambahanMenit += (5 * $qty);
|
||||||
|
} elseif ($id == 6) { // Tambah 10 menit sesi Spotlight
|
||||||
|
$tambahanMenit += (10 * $qty);
|
||||||
|
}
|
||||||
|
|
||||||
$addonsDetails[] = [
|
$addonsDetails[] = [
|
||||||
'nama' => $add->nama,
|
'nama' => $add->nama,
|
||||||
'qty' => $qty,
|
'qty' => $qty,
|
||||||
'subtotal' => $subtotal,
|
'subtotal' => $subtotal,
|
||||||
'id' => $id // Simpan ID untuk dikirim lagi nanti
|
'id' => $id
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Hitung Grand Total
|
// 3. Hitung Ulang Jam Selesai
|
||||||
|
$totalDurasi = $durasiDasar + $tambahanMenit;
|
||||||
|
$jamMulai = \Carbon\Carbon::createFromFormat('H:i', $request->jam_mulai);
|
||||||
|
$jamSelesaiBaru = $jamMulai->copy()->addMinutes($totalDurasi)->format('H:i');
|
||||||
|
|
||||||
|
// 4. Hitung Grand Total
|
||||||
$grandTotal = $foto->harga + $totalAddon;
|
$grandTotal = $foto->harga + $totalAddon;
|
||||||
// 1. Cek apakah sudah ada deadline di session? Kalau belum, buat baru (2 jam dari sekarang)
|
|
||||||
|
// 5. Logika Deadline Pembayaran (Tetap sama)
|
||||||
if (!session()->has('payment_deadline')) {
|
if (!session()->has('payment_deadline')) {
|
||||||
$deadline = now()->addHours(2);
|
$deadline = now()->addHours(2);
|
||||||
session()->put('payment_deadline', $deadline);
|
session()->put('payment_deadline', $deadline);
|
||||||
|
|
@ -106,21 +125,21 @@ public function formulir(Request $request)
|
||||||
$deadline = session('payment_deadline');
|
$deadline = session('payment_deadline');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Hitung sisa waktu dalam detik
|
$sisaWaktu = now()->diffInSeconds($deadline, false);
|
||||||
$sisaWaktu = now()->diffInSeconds($deadline, false); // false = biar bisa negatif kalau lewat
|
|
||||||
|
|
||||||
// 3. Jika waktu habis (negatif), hapus session dan tendang user
|
|
||||||
if ($sisaWaktu <= 0) {
|
if ($sisaWaktu <= 0) {
|
||||||
session()->forget(['payment_deadline', 'addons']); // Bersihkan session
|
session()->forget(['payment_deadline', 'addons']);
|
||||||
return redirect()->route('booking.foto')->with('error', 'Waktu pembayaran telah habis. Silakan ulang pemesanan.');
|
return redirect()->route('booking.foto')->with('error', 'Waktu pembayaran telah habis.');
|
||||||
}
|
}
|
||||||
// 4. Kirim semua data ke View Pembayaran untuk ditampilkan
|
|
||||||
|
// 6. Kirim jam_selesai yang sudah diperbarui ke View
|
||||||
return view('user.pembayaran-foto', compact(
|
return view('user.pembayaran-foto', compact(
|
||||||
'foto',
|
'foto',
|
||||||
'request', // Kirim request agar tgl & jam bisa diakses di blade
|
'request',
|
||||||
'addonsDetails',
|
'addonsDetails',
|
||||||
'grandTotal',
|
'grandTotal',
|
||||||
'sisaWaktu'
|
'sisaWaktu',
|
||||||
|
'jamSelesaiBaru' // Variabel baru untuk ditampilkan di blade
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
public function cancelBooking()
|
public function cancelBooking()
|
||||||
|
|
@ -130,55 +149,47 @@ public function cancelBooking()
|
||||||
}
|
}
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
|
// 1. Validasi Input menggunakan Format Validator
|
||||||
// 1. Validasi Input
|
$validator = \Illuminate\Support\Facades\Validator::make($request->all(), [
|
||||||
$request->validate([
|
|
||||||
'id_paket' => 'required|exists:paket_fotos,id_paket',
|
'id_paket' => 'required|exists:paket_fotos,id_paket',
|
||||||
'tgl_booking' => 'required|date|after_or_equal:today',
|
'tgl_booking' => 'required|date|after_or_equal:today',
|
||||||
'jam_mulai' => 'required',
|
'jam_mulai' => 'required',
|
||||||
'nama' => 'required|string|max:255',
|
'nama' => 'required|string|min:3|max:100',
|
||||||
'no_wa' => 'required|numeric',
|
'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',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
DB::beginTransaction(); // Mulai Transaksi Database
|
if ($validator->fails()) {
|
||||||
|
return back()->withErrors($validator)->withInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
\Illuminate\Support\Facades\DB::beginTransaction(); // Mulai Transaksi Database
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 2. Ambil Data Paket & Hitung Waktu
|
// 2. Ambil Data Paket & Hitung Waktu (Termasuk Tambahan Menit dari Addons)
|
||||||
$paket = PaketFoto::findOrFail($request->id_paket);
|
$paket = \App\Models\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'), $namaFile);
|
|
||||||
$pathBukti = 'img/payment/' . $namaFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = [];
|
||||||
|
|
||||||
|
|
@ -190,32 +201,65 @@ public function store(Request $request)
|
||||||
$subtotal = $add->harga * $qty;
|
$subtotal = $add->harga * $qty;
|
||||||
$grandTotal += $subtotal;
|
$grandTotal += $subtotal;
|
||||||
|
|
||||||
|
// Logika Tambah Waktu berdasarkan ID yang sebelumnya kita bahas
|
||||||
|
if ($idAddon == 4) $totalDurasi += (5 * $qty); // Tambah 5 menit
|
||||||
|
if ($idAddon == 6) $totalDurasi += (10 * $qty); // Tambah 10 menit
|
||||||
|
|
||||||
$listAdditional[] = [
|
$listAdditional[] = [
|
||||||
'id_additional' => $idAddon,
|
'id_additional' => $idAddon,
|
||||||
'qty' => $qty,
|
'qty' => $qty,
|
||||||
'subtotal' => $subtotal
|
'subtotal' => $subtotal,
|
||||||
|
'nama_barang' => $add->nama
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Simpan Booking Utama
|
$jamMulai = \Carbon\Carbon::createFromFormat('H:i', $request->jam_mulai);
|
||||||
$booking = BookingFoto::create([
|
$jamSelesai = $jamMulai->copy()->addMinutes($totalDurasi);
|
||||||
'no_invoice' => 'INV-FOTO-' . strtoupper(Str::random(6)),
|
|
||||||
|
// 3. Cek Slot Sekali Lagi (Mencegah Race Condition)
|
||||||
|
$isTaken = \App\Models\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 = \App\Models\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() . $file->getClientOriginalExtension();
|
||||||
|
$file->move(public_path('img/payment'), $namaFile);
|
||||||
|
$pathBukti = 'img/payment/foto' . $namaFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Simpan Booking Utama
|
||||||
|
$booking = \App\Models\BookingFoto::create([
|
||||||
|
'no_invoice' => 'INV-FOTO-' . strtoupper(\Illuminate\Support\Str::random(6)),
|
||||||
'id_pelanggan' => $pelanggan->id_pelanggan,
|
'id_pelanggan' => $pelanggan->id_pelanggan,
|
||||||
'id_paket' => $paket->id_paket,
|
'id_paket' => $paket->id_paket,
|
||||||
'tgl_booking' => $request->tgl_booking,
|
'tgl_booking' => $request->tgl_booking,
|
||||||
'jam_mulai' => $request->jam_mulai, // Format "09:00"
|
'jam_mulai' => $request->jam_mulai,
|
||||||
'jam_selesai' => $jamSelesai->format('H:i'),
|
'jam_selesai' => $jamSelesai->format('H:i'),
|
||||||
'total_bayar' => $grandTotal,
|
'total_bayar' => $grandTotal,
|
||||||
'bukti_bayar' => $pathBukti,
|
'bukti_bayar' => $pathBukti,
|
||||||
'status_booking' => 'menunggu_verifikasi'
|
'status_booking' => 'menunggu_verifikasi'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 8. Simpan Detail Additional (Jika ada)
|
// 7. Simpan Detail Additional
|
||||||
foreach ($listAdditional as $item) {
|
foreach ($listAdditional as $item) {
|
||||||
DetailAdditional::create([
|
\App\Models\DetailAdditional::create([
|
||||||
'id_booking' => $booking->id_booking,
|
'id_booking' => $booking->id_booking,
|
||||||
'id_additional' => $item['id_additional'],
|
'id_additional' => $item['id_additional'],
|
||||||
'qty' => $item['qty'],
|
'qty' => $item['qty'],
|
||||||
|
|
@ -223,13 +267,28 @@ public function store(Request $request)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::commit();
|
\Illuminate\Support\Facades\DB::commit();
|
||||||
// 9. Redirect ke WhatsApp atau Halaman Sukses
|
$txtAddons = "";
|
||||||
$pesan = "Halo Admin Flo.do! Saya pesan foto paket *{$paket->nama}*.\n" .
|
if (count($listAdditional) > 0) {
|
||||||
"Tanggal: {$request->tgl_booking}\n" .
|
$txtAddons = "*Additional:*"; // Judul
|
||||||
"Jam: {$request->jam_mulai}\n" .
|
foreach ($listAdditional as $item) {
|
||||||
|
// Ambil nama yang tadi kita titip
|
||||||
|
$txtAddons .= "\n- " . $item['nama_barang'] . " (" . $item['qty'] . "x)";
|
||||||
|
}
|
||||||
|
$txtAddons .= "\n"; // Kasih jarak baris
|
||||||
|
}
|
||||||
|
// 8. Redirect ke WhatsApp
|
||||||
|
$pesan = "Halo Admin Flo.do! Saya sudah melakukan pembayaran untuk invoice {$booking->no_invoice}:" .
|
||||||
|
"*Data Pemesan:*\n" .
|
||||||
|
"Nama: {$pelanggan->nama}\n" .
|
||||||
|
"WA: {$pelanggan->no_wa}\n\n" .
|
||||||
|
"*Detail Booking:*\n" .
|
||||||
|
"Nama Paket: {$request->nama}\n" .
|
||||||
|
$txtAddons .
|
||||||
|
"Tanggal: " . \Carbon\Carbon::parse($request->tgl_booking)->translatedFormat('l, d F Y') . "\n" .
|
||||||
|
"Jam: {$request->jam_mulai} - {$jamSelesai->format('H:i')} WIB\n" .
|
||||||
"Total: Rp " . number_format($grandTotal, 0, ',', '.') . "\n" .
|
"Total: Rp " . number_format($grandTotal, 0, ',', '.') . "\n" .
|
||||||
"Mohon diverifikasi ya.";
|
"Mohon segera diproses, ya! Terima kasih.";
|
||||||
|
|
||||||
$urlWA = "https://wa.me/6289673668516?text=" . urlencode($pesan);
|
$urlWA = "https://wa.me/6289673668516?text=" . urlencode($pesan);
|
||||||
session()->forget('payment_deadline');
|
session()->forget('payment_deadline');
|
||||||
|
|
@ -239,12 +298,8 @@ public function store(Request $request)
|
||||||
'waUrl' => $urlWA
|
'waUrl' => $urlWA
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollBack();
|
\Illuminate\Support\Facades\DB::rollBack();
|
||||||
// Teks Debugging Lengkap
|
return back()->with('error', 'Error: ' . $e->getMessage())->withInput();
|
||||||
$errorMsg = "Error: " . $e->getMessage() . " | Baris: " . $e->getLine() . " | File: " . basename($e->getFile());
|
|
||||||
|
|
||||||
// Kirim pesan error lengkap ke layar
|
|
||||||
return back()->with('error', $errorMsg)->withInput();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,9 @@ public function store(Request $request)
|
||||||
if ($request->hasFile('bukti_bayar')) {
|
if ($request->hasFile('bukti_bayar')) {
|
||||||
$file = $request->file('bukti_bayar');
|
$file = $request->file('bukti_bayar');
|
||||||
// Membuat nama file unik berdasarkan waktu agar tidak tertimpa
|
// Membuat nama file unik berdasarkan waktu agar tidak tertimpa
|
||||||
$namaFile = time() . '_' . $file->getClientOriginalName();
|
$namaFile = 'bukti_' . time() . $file->getClientOriginalExtension();
|
||||||
// Pindahkan ke folder public/img/payment
|
// Pindahkan ke folder public/img/payment
|
||||||
$file->move(public_path('img/payment'), $namaFile);
|
$file->move(public_path('img/payment/buket'), $namaFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
$transaksi = TransaksiBuket::create([
|
$transaksi = TransaksiBuket::create([
|
||||||
|
|
@ -129,13 +129,13 @@ public function store(Request $request)
|
||||||
'bukti_bayar' => 'img/payment/' . $namaFile,
|
'bukti_bayar' => 'img/payment/' . $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-' . time(), // Tambahkan invoice sederhana
|
'no_invoice' => 'INV-BUKET-' . strtoupper(\Illuminate\Support\Str::random(6)), // Tambahkan invoice sederhana
|
||||||
]);
|
]);
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
||||||
// 5. Membuat Pesan WhatsApp Otomatis
|
// 5. Membuat Pesan WhatsApp Otomatis
|
||||||
$pesan = "Halo Admin Flo.do! Saya sudah melakukan pembayaran:\n\n" .
|
$pesan = "Halo Admin Flo.do! Saya sudah melakukan pembayaran untuk invoice {$transaksi->no_invoice}:\n\n" .
|
||||||
"*Data Pemesan:*\n" .
|
"*Data Pemesan:*\n" .
|
||||||
"Nama: {$pelanggan->nama}\n" .
|
"Nama: {$pelanggan->nama}\n" .
|
||||||
"WA: {$pelanggan->no_wa}\n\n" .
|
"WA: {$pelanggan->no_wa}\n\n" .
|
||||||
|
|
@ -147,7 +147,7 @@ public function store(Request $request)
|
||||||
$urlWA = "https://wa.me/6289673668516?text=" . urlencode($pesan);
|
$urlWA = "https://wa.me/6289673668516?text=" . urlencode($pesan);
|
||||||
|
|
||||||
return redirect()->route('pesan.buket')->with([
|
return redirect()->route('pesan.buket')->with([
|
||||||
'success' => 'Pesanan berhasil dikirim!',
|
'success' => 'Pesanan Berhasil Dibuat!',
|
||||||
'waUrl' => $urlWA
|
'waUrl' => $urlWA
|
||||||
]);
|
]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
|
@ -17,8 +18,12 @@ public function register(): void
|
||||||
/**
|
/**
|
||||||
* Bootstrap any application services.
|
* Bootstrap any application services.
|
||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot()
|
||||||
{
|
{
|
||||||
//
|
// Set locale Carbon secara global ke Indonesia
|
||||||
|
Carbon::setLocale('id');
|
||||||
|
|
||||||
|
// Opsional: Set timezone jika diperlukan
|
||||||
|
date_default_timezone_set('Asia/Jakarta');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,49 +21,49 @@ public function run(): void
|
||||||
'nama' => 'Couple',
|
'nama' => 'Couple',
|
||||||
'harga' => 45000,
|
'harga' => 45000,
|
||||||
'deskripsi' => 'Untuk 2 orang, 15 menit sesi foto sepuasnya, 5 menit sesi pilih foto, 1 lembar print out foto ukutan 4R.',
|
'deskripsi' => 'Untuk 2 orang, 15 menit sesi foto sepuasnya, 5 menit sesi pilih foto, 1 lembar print out foto ukutan 4R.',
|
||||||
'foto' => 'img/foto/grup.jpeg',
|
'foto' => 'img/foto/couple.jpeg',
|
||||||
'durasi' => '15',
|
'durasi' => '15',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'nama' => 'Group',
|
'nama' => 'Group',
|
||||||
'harga' => 80000,
|
'harga' => 80000,
|
||||||
'deskripsi' => 'Untuk 3-5 orang, 15 menit sesi foto sepuasnya, 5 menit sesi pilih foto, 3 lembar print out foto ukutan 4R.',
|
'deskripsi' => 'Untuk 3-5 orang, 15 menit sesi foto sepuasnya, 5 menit sesi pilih foto, 3 lembar print out foto ukutan 4R.',
|
||||||
'foto' => 'img/foto/pas-foto.jpg',
|
'foto' => 'img/foto/1767368465_grup.jpeg',
|
||||||
'durasi' => '15',
|
'durasi' => '15',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'nama' => 'Pas Foto Paket 1',
|
'nama' => 'Pas Foto Paket 1',
|
||||||
'harga' => 25000,
|
'harga' => 25000,
|
||||||
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak: ukuran 2x3 (12 lembar).',
|
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak: ukuran 2x3 (12 lembar).',
|
||||||
'foto' => 'img/foto/pas-foto.jpg',
|
'foto' => 'img/foto/1767368572_pas-foto.jpg',
|
||||||
'durasi' => '0',
|
'durasi' => '0',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'nama' => 'Pas Foto Paket 2',
|
'nama' => 'Pas Foto Paket 2',
|
||||||
'harga' => 25000,
|
'harga' => 25000,
|
||||||
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak: ukuran 3x4 (8 lembar).',
|
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak: ukuran 3x4 (8 lembar).',
|
||||||
'foto' => 'img/foto/pas-foto.jpg',
|
'foto' => 'img/foto/1767368572_pas-foto.jpg',
|
||||||
'durasi' => '0',
|
'durasi' => '0',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'nama' => 'Pas Foto Paket 3',
|
'nama' => 'Pas Foto Paket 3',
|
||||||
'harga' => 25000,
|
'harga' => 25000,
|
||||||
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak: ukuran 4x6 (4 lembar).',
|
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak: ukuran 4x6 (4 lembar).',
|
||||||
'foto' => 'img/foto/pas-foto.jpg',
|
'foto' => 'img/foto/1767368572_pas-foto.jpg',
|
||||||
'durasi' => '0',
|
'durasi' => '0',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'nama' => 'Pas Foto Paket 4',
|
'nama' => 'Pas Foto Paket 4',
|
||||||
'harga' => 25000,
|
'harga' => 25000,
|
||||||
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak campur: ukuran 4x6 (2 lembar), 3x4 (3 lembar), dan 2x3 (4 lembar).',
|
'deskripsi' => 'Sesi pas foto untuk 1 orang dengan 8x shoot fotografer. Termasuk 1 file foto edit dan bebas request warna background. Paket cetak campur: ukuran 4x6 (2 lembar), 3x4 (3 lembar), dan 2x3 (4 lembar).',
|
||||||
'foto' => 'img/foto/pas-foto.jpg',
|
'foto' => 'img/foto/1767368572_pas-foto.jpg',
|
||||||
'durasi' => '0',
|
'durasi' => '0',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'nama' => 'Background Biru',
|
'nama' => 'Background Biru',
|
||||||
'harga' => 95000,
|
'harga' => 95000,
|
||||||
'deskripsi' => 'Paket foto untuk 2 orang. Termasuk: Max 10x shoot fotografer (formal), 10 menit self-photo (bebas), dan semua file dikirim via Google Drive. Cetak 2 Foto (4R) terdiri dari: (2x3) 8 lembar, (3x4) 6 lembar, dan (4x6) 4 lembar.',
|
'deskripsi' => 'Paket foto untuk 2 orang. Termasuk: Max 10x shoot fotografer (formal), 10 menit self-photo (bebas), dan semua file dikirim via Google Drive. Cetak 2 Foto (4R) terdiri dari: (2x3) 8 lembar, (3x4) 6 lembar, dan (4x6) 4 lembar.',
|
||||||
'foto' => 'img/foto/latar-biru.jpeg',
|
'foto' => 'img/foto/1766833654_latar biru 1.jpeg',
|
||||||
'durasi' => '20',
|
'durasi' => '20',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -870,8 +870,8 @@ .footer-full {
|
||||||
background-color: var(--bs-primary);
|
background-color: var(--bs-primary);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: var(--fs-small);
|
font-size: var(--fs-small);
|
||||||
padding: 1rem;
|
padding: .5rem;
|
||||||
margin-top: 2rem;
|
margin-top: 1rem;
|
||||||
margin-left: -2rem;
|
margin-left: -2rem;
|
||||||
margin-right: -2rem;
|
margin-right: -2rem;
|
||||||
margin-bottom: -2rem;
|
margin-bottom: -2rem;
|
||||||
|
|
@ -2313,9 +2313,12 @@ .dataTable-table>tbody {
|
||||||
vertical-align: inherit
|
vertical-align: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
.table>thead,
|
.table>thead>tr>th,
|
||||||
.dataTable-table>thead {
|
.dataTable-table>thead>tr>th {
|
||||||
vertical-align: bottom
|
text-align: center !important;
|
||||||
|
/* Meniru perilaku .text-center */
|
||||||
|
vertical-align: middle;
|
||||||
|
/* Opsional: agar teks berada di tengah secara vertikal */
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-group-divider {
|
.table-group-divider {
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ class="detailbuket-spec-val">{{ ucfirst(str_replace('_', ' ', $buket->kategori))
|
||||||
<div class="d-flex gap-3 mt-5">
|
<div class="d-flex gap-3 mt-5">
|
||||||
<a href="{{ route('formulir.buket', ['id' => $buket->id_buket]) }}"
|
<a href="{{ route('formulir.buket', ['id' => $buket->id_buket]) }}"
|
||||||
class="btn btn-detailbuket-primary flex-fill">
|
class="btn btn-detailbuket-primary flex-fill">
|
||||||
Beli Sekarang
|
Pesan Sekarang
|
||||||
</a>
|
</a>
|
||||||
@php
|
@php
|
||||||
$waText =
|
$waText =
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
@csrf
|
@csrf
|
||||||
{{-- Kotak simpan data sementara di dalam form --}}
|
{{-- Kotak simpan data sementara di dalam form --}}
|
||||||
<input type="hidden" name="tgl_booking" id="input_tgl_booking">
|
<input type="hidden" name="tgl_booking" id="input_tgl_booking">
|
||||||
<input type="hidden" name="jam_awal" id="input_jam_awal">
|
<input type="hidden" name="jam_mulai" id="input_jam_mulai">
|
||||||
<input type="hidden" name="jam_selesai" id="input_jam_selesai">
|
<input type="hidden" name="jam_selesai" id="input_jam_selesai">
|
||||||
<div id="data-paket" data-durasi="15"></div>
|
<div id="data-paket" data-durasi="15"></div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
@ -35,8 +35,16 @@ class="detailfoto-thumb rounded-4">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="detailfoto-card p-3 h-100">
|
<div class="detailfoto-card p-3 h-100">
|
||||||
<h3 class="detailfoto-product-title">{{ $foto->nama }}</h3>
|
<h3 class="detailfoto-product-title">{{ $foto->nama }}</h3>
|
||||||
<h4 class="detailfoto-product-price">Rp {{ number_format($foto->harga, 0, ',', '.') }}
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<h4 class="detailfoto-product-price mb-0">
|
||||||
|
Rp {{ number_format($foto->harga, 0, ',', '.') }}
|
||||||
</h4>
|
</h4>
|
||||||
|
<span class="text-muted">|</span>
|
||||||
|
<span class="badge rounded-pill"
|
||||||
|
style="background-color: #e6f0f0; color: #3B8181; font-weight: 500;">
|
||||||
|
<i class="bi bi-clock me-1"></i> {{ $foto->durasi }} Menit Sesi
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<p class="detailfoto-product-desc mb-0 text-justify">{{ $foto->deskripsi }}</p>
|
<p class="detailfoto-product-desc mb-0 text-justify">{{ $foto->deskripsi }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -78,10 +86,12 @@ class="detailfoto-thumb rounded-4">
|
||||||
<span class="timer-badge" id="booking-timer">10:00</span>
|
<span class="timer-badge" id="booking-timer">10:00</span>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="schedule-title">Pilih Tanggal dan Waktu</h4>
|
<h4 class="schedule-title">Pilih Tanggal dan Waktu</h4>
|
||||||
|
<p class="small">Slot abu-abu berarti jadwal sudah penuh. Silakan pilih waktu lain yang
|
||||||
|
tersedia.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-md-9 border-end pe-md-4" id="calendar-wrapper">
|
<div class="col-md-9 border-end pe-md-4" id="calendar-grid-wrapper">
|
||||||
@include('user.components.calendar-grid')
|
@include('user.components.calendar-grid')
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -89,13 +99,16 @@ class="detailfoto-thumb rounded-4">
|
||||||
<h6 class="mb-3 small">Jam Tersedia</h6>
|
<h6 class="mb-3 small">Jam Tersedia</h6>
|
||||||
<div class="time-slot-container">
|
<div class="time-slot-container">
|
||||||
@for ($hour = 9; $hour <= 20; $hour++)
|
@for ($hour = 9; $hour <= 20; $hour++)
|
||||||
|
{{-- Slot Jam Genap (contoh: 09:00) --}}
|
||||||
<button type="button" class="btn-time"
|
<button type="button" class="btn-time"
|
||||||
data-time="{{ sprintf('%02d:00', $hour) }}">
|
data-time="{{ sprintf('%02d:00', $hour) }}">
|
||||||
{{ sprintf('%02d:00', $hour) }} {{ $hour < 12 ? 'AM' : 'PM' }}
|
{{ sprintf('%02d:00', $hour) }} WIB
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{{-- Slot Jam Setengah (contoh: 09:30) --}}
|
||||||
<button type="button" class="btn-time"
|
<button type="button" class="btn-time"
|
||||||
data-time="{{ sprintf('%02d:30', $hour) }}">
|
data-time="{{ sprintf('%02d:30', $hour) }}">
|
||||||
{{ sprintf('%02d:30', $hour) }} {{ $hour < 12 ? 'AM' : 'PM' }}
|
{{ sprintf('%02d:30', $hour) }} WIB
|
||||||
</button>
|
</button>
|
||||||
@endfor
|
@endfor
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -113,12 +126,13 @@ class="detailfoto-thumb rounded-4">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
<script>
|
<script>
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 1. VARIABLE GLOBAL & UTILITIES
|
// 1. VARIABLE GLOBAL & UTILITIES
|
||||||
// ============================================================
|
// ============================================================
|
||||||
let countdown; // Variabel untuk menyimpan timer
|
let countdown; // Variabel untuk menyimpan timer
|
||||||
|
let isTimeUp = false;
|
||||||
const durasiPaket = parseInt(document.getElementById('data-paket')?.dataset.durasi || 15);
|
const durasiPaket = parseInt(document.getElementById('data-paket')?.dataset.durasi || 15);
|
||||||
|
|
||||||
// Fungsi Update Counter (+/-) untuk Additional
|
// Fungsi Update Counter (+/-) untuk Additional
|
||||||
|
|
@ -146,7 +160,6 @@ function showCalendar() {
|
||||||
calendarCol.scrollIntoView({
|
calendarCol.scrollIntoView({
|
||||||
behavior: 'smooth'
|
behavior: 'smooth'
|
||||||
});
|
});
|
||||||
|
|
||||||
startTimer(10 * 60);
|
startTimer(10 * 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +182,7 @@ function hideCalendar() {
|
||||||
|
|
||||||
// Reset pilihan tanggal & jam di input hidden
|
// Reset pilihan tanggal & jam di input hidden
|
||||||
document.getElementById('input_tgl_booking').value = "";
|
document.getElementById('input_tgl_booking').value = "";
|
||||||
document.getElementById('input_jam_awal').value = "";
|
document.getElementById('input_jam_mulai').value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fungsi Timer Mundur
|
// Fungsi Timer Mundur
|
||||||
|
|
@ -177,8 +190,9 @@ function startTimer(duration) {
|
||||||
let timer = duration,
|
let timer = duration,
|
||||||
minutes, seconds;
|
minutes, seconds;
|
||||||
const display = document.querySelector('#booking-timer');
|
const display = document.querySelector('#booking-timer');
|
||||||
|
const submitBtn = document.querySelector('.btn-action-submit'); // Pastikan class tombol benar
|
||||||
|
|
||||||
// Reset timer lama jika ada
|
isTimeUp = false; // Reset status setiap timer mulai
|
||||||
if (countdown) clearInterval(countdown);
|
if (countdown) clearInterval(countdown);
|
||||||
|
|
||||||
countdown = setInterval(function() {
|
countdown = setInterval(function() {
|
||||||
|
|
@ -192,8 +206,25 @@ function startTimer(duration) {
|
||||||
|
|
||||||
if (--timer < 0) {
|
if (--timer < 0) {
|
||||||
clearInterval(countdown);
|
clearInterval(countdown);
|
||||||
Swal.fire('Waktu Habis!', 'Sesi booking Anda telah berakhir. Silakan pilih ulang.', 'warning')
|
isTimeUp = true; // Kunci status
|
||||||
.then(() => location.reload());
|
|
||||||
|
// Matikan tombol submit secara visual
|
||||||
|
if (submitBtn) {
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.classList.add('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tampilkan alert
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Waktu Habis!',
|
||||||
|
text: 'Sesi booking Anda telah berakhir. Silakan pilih ulang jadwal.',
|
||||||
|
icon: 'warning',
|
||||||
|
confirmButtonText: 'Oke',
|
||||||
|
confirmButtonColor: '#3B8181',
|
||||||
|
allowOutsideClick: false
|
||||||
|
}).then(() => {
|
||||||
|
location.reload(); // Refresh halaman
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
@ -271,8 +302,29 @@ function checkSlotAvailability(tgl) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Kosongkan input jam (karena user ganti tanggal)
|
// 2. Kosongkan input jam (karena user ganti tanggal)
|
||||||
document.getElementById('input_jam_awal').value = "";
|
document.getElementById('input_jam_mulai').value = "";
|
||||||
|
const sekarang = new Date();
|
||||||
|
const jamSekarang = sekarang.getHours();
|
||||||
|
const menitSekarang = sekarang.getMinutes();
|
||||||
|
const totalMenitSekarang = (jamSekarang * 60) + menitSekarang;
|
||||||
|
|
||||||
|
// Format tanggal hari ini (YYYY-MM-DD) untuk perbandingan
|
||||||
|
const hariIni = sekarang.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
// 3. LOGIKA JAM LEWAT: Jika tanggal yang diklik adalah hari ini
|
||||||
|
if (tgl === hariIni) {
|
||||||
|
allTimeBtns.forEach(btn => {
|
||||||
|
const [jamBtn, menitBtn] = btn.dataset.time.split(':').map(Number);
|
||||||
|
const totalMenitBtn = (jamBtn * 60) + menitBtn;
|
||||||
|
|
||||||
|
// Jika waktu pada tombol sudah lewat dari waktu sekarang
|
||||||
|
if (totalMenitBtn <= totalMenitSekarang) {
|
||||||
|
btn.classList.add('disabled', 'full');
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.title = "Waktu sudah terlewat";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
// 3. Panggil Server
|
// 3. Panggil Server
|
||||||
fetch(`/cek-slot-foto?tanggal=${tgl}`)
|
fetch(`/cek-slot-foto?tanggal=${tgl}`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
|
|
@ -300,7 +352,7 @@ function checkSlotAvailability(tgl) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
// 1. Jalankan Listener Tanggal (Untuk kalender awal)
|
// 1. Jalankan Listener Tanggal (Untuk kalender mulai)
|
||||||
reinitDateListeners();
|
reinitDateListeners();
|
||||||
|
|
||||||
// 2. Pasang Listener Tombol Jam (Cukup sekali)
|
// 2. Pasang Listener Tombol Jam (Cukup sekali)
|
||||||
|
|
@ -318,7 +370,7 @@ function checkSlotAvailability(tgl) {
|
||||||
|
|
||||||
// Logic: Simpan Jam
|
// Logic: Simpan Jam
|
||||||
const startTime = this.dataset.time;
|
const startTime = this.dataset.time;
|
||||||
document.getElementById('input_jam_awal').value = startTime;
|
document.getElementById('input_jam_mulai').value = startTime;
|
||||||
|
|
||||||
// Logic: Hitung Jam Selesai
|
// Logic: Hitung Jam Selesai
|
||||||
const endTime = calculateEndTime(startTime, durasiPaket);
|
const endTime = calculateEndTime(startTime, durasiPaket);
|
||||||
|
|
@ -326,15 +378,24 @@ function checkSlotAvailability(tgl) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 3. Validasi Submit Form
|
||||||
// 3. Validasi Submit Form
|
// 3. Validasi Submit Form
|
||||||
const bookingForm = document.getElementById('form-booking');
|
const bookingForm = document.getElementById('form-booking');
|
||||||
if (bookingForm) {
|
if (bookingForm) {
|
||||||
bookingForm.addEventListener('submit', function(e) {
|
bookingForm.addEventListener('submit', function(e) {
|
||||||
const tgl = document.getElementById('input_tgl_booking').value;
|
const tgl = document.getElementById('input_tgl_booking').value;
|
||||||
const jam = document.getElementById('input_jam_awal').value;
|
const jam = document.getElementById('input_jam_mulai').value;
|
||||||
|
|
||||||
|
// CEK APAKAH WAKTU HABIS
|
||||||
|
if (isTimeUp) {
|
||||||
|
e.preventDefault();
|
||||||
|
Swal.fire('Maaf!', 'Waktu booking sudah habis, halaman akan dimuat ulang.', 'error')
|
||||||
|
.then(() => location.reload());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tgl || !jam) {
|
if (!tgl || !jam) {
|
||||||
e.preventDefault(); // Stop kirim
|
e.preventDefault();
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Jadwal Belum Dipilih!',
|
title: 'Jadwal Belum Dipilih!',
|
||||||
text: 'Silakan pilih tanggal dan jam pemotretan terlebih dahulu.',
|
text: 'Silakan pilih tanggal dan jam pemotretan terlebih dahulu.',
|
||||||
|
|
|
||||||
|
|
@ -116,12 +116,12 @@ class="form-control formulirbuket-input @error('waktu_ambil') is-invalid @enderr
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-0 small bank-num">1234567890</p>
|
<p class="mb-0 small bank-num">6860305231</p>
|
||||||
<p class="mb-0 x-small bank-name">a.n Flo.do</p>
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
</div>
|
</div>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-secondary py-1 px-3 x-small btn-copy"
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
data-clipboard-text="1234567890">salin</button>
|
data-clipboard-text="6860305231" style="font-size: 12px">salin</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -132,17 +132,50 @@ class="btn btn-sm btn-outline-secondary py-1 px-3 x-small btn-copy"
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-0 small bank-num">0987654321</p>
|
<p class="mb-0 small bank-num">005601002337563</p>
|
||||||
<p class="mb-0 x-small bank-name">a.n Flo.do</p>
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
</div>
|
</div>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-secondary py-1 px-3 x-small btn-copy"
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
data-clipboard-text="0987654321">salin</button>
|
data-clipboard-text="005601002337563"
|
||||||
|
style="font-size: 12px">salin</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="formulirbuket-bank-card">
|
||||||
|
<div class="d-flex align-items-center gap-2 mb-2">
|
||||||
|
<i class="bi bi-bank text-secondary"></i> <span
|
||||||
|
class="small ">ShopeePay</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 small bank-num">082133958469</p>
|
||||||
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
|
data-clipboard-text="082133958469" style="font-size: 12px">salin</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="formulirbuket-bank-card">
|
||||||
|
<div class="d-flex align-items-center gap-2 mb-2">
|
||||||
|
<i class="bi bi-bank text-secondary"></i> <span class="small ">Dana</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 small bank-num">082133958469</p>
|
||||||
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
|
data-clipboard-text="082133958469" style="font-size: 12px">salin</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="small mb-2">Upload Bukti Pembayaran</p>
|
<p class="small mb-2">Upload Bukti Pembayaran</p>
|
||||||
<div class="formulirbuket-upload-area mb-2 text-center position-relative">
|
<div class="formulirbuket-upload-area mb-2 text-center position-relative">
|
||||||
<input type="file" name="bukti_bayar" accept="image/*"
|
<input type="file" name="bukti_bayar" accept="image/*"
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,6 @@
|
||||||
<h2 class="formulirfoto-page-title">Formulir Pemesanan</h2>
|
<h2 class="formulirfoto-page-title">Formulir Pemesanan</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if ($errors->any())
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<ul>
|
|
||||||
@foreach ($errors->all() as $error)
|
|
||||||
<li>{{ $error }}</li>
|
|
||||||
@endforeach
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
<form action="{{ route('transaksi.foto.store') }}" method="POST" enctype="multipart/form-data">
|
<form action="{{ route('transaksi.foto.store') }}" method="POST" enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
{{-- DATA DARI HALAMAN SEBELUMNYA (WAJIB ADA) --}}
|
{{-- DATA DARI HALAMAN SEBELUMNYA (WAJIB ADA) --}}
|
||||||
|
|
@ -41,22 +32,41 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="form-label small">Nama Lengkap</label>
|
<label class="form-label small">Nama Lengkap</label>
|
||||||
<input type="text" name="nama" class="form-control formulirfoto-input"
|
<input type="text" name="nama"
|
||||||
placeholder="Masukkan Nama Lengkap" required>
|
class="form-control formulirbuket-input @error('nama') is-invalid @enderror"
|
||||||
|
placeholder="Masukkan Nama Lengkap" value="{{ old('nama') }}">
|
||||||
|
|
||||||
|
@error('nama')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="form-label small">Nomor WhatsApp</label>
|
<label class="form-label small">Nomor WhatsApp</label>
|
||||||
<input type="number" name="no_wa" class="form-control formulirfoto-input"
|
<input type="number" name="no_wa"
|
||||||
placeholder="Masukkan Nomor WhatsApp" required>
|
class="form-control formulirbuket-input @error('no_wa') is-invalid @enderror"
|
||||||
|
placeholder="Masukkan Nomor WhatsApp" value="{{ old('no_wa') }}">
|
||||||
|
@error('no_wa')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||||
<span class="timer-alert-text">Selesaikan pembayaran dalam</span>
|
<span class="timer-alert-text">Selesaikan pembayaran dalam</span>
|
||||||
<span class="timer-badge" id="countdown-timer">00:58:58</span>
|
@php
|
||||||
|
// Hitung format jam:menit:detik langsung di HTML agar tidak flash 58:58
|
||||||
|
$jam = floor($sisaWaktu / 3600);
|
||||||
|
$menit = floor(($sisaWaktu / 60) % 60);
|
||||||
|
$detik = $sisaWaktu % 60;
|
||||||
|
$tampilanAwal = sprintf('%02d:%02d:%02d', $jam, $menit, $detik);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<span class="timer-badge" id="countdown-timer">{{ $tampilanAwal }}</span>
|
||||||
|
{{-- <span class="timer-badge" id="countdown-timer"></span> --}}
|
||||||
</div>
|
</div>
|
||||||
<h4 class="formulirfoto-section-title mb-3">Informasi Pembayaran</h4>
|
<h4 class="formulirfoto-section-title mb-3">Informasi Pembayaran</h4>
|
||||||
|
|
||||||
|
|
@ -64,9 +74,14 @@
|
||||||
<div class="summary-item">
|
<div class="summary-item">
|
||||||
<span class="summary-label">Jadwal Booking:</span>
|
<span class="summary-label">Jadwal Booking:</span>
|
||||||
<div class="summary-row">
|
<div class="summary-row">
|
||||||
<span
|
{{-- Menampilkan Hari, Tanggal Bulan Tahun dalam Bahasa Indonesia --}}
|
||||||
class="summary-subtext">{{ \Carbon\Carbon::parse($request->tgl_booking)->translatedFormat('l, d F Y') }}</span>
|
<span class="summary-subtext">
|
||||||
<span class="summary-value">{{ $request->jam_mulai }}</span>
|
{{ \Carbon\Carbon::parse($request->tgl_booking)->translatedFormat('l, d F Y') }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{{-- Menampilkan Jam di bagian kanan --}}
|
||||||
|
<span class="summary-value">
|
||||||
|
{{ $request->jam_mulai }} - {{ $jamSelesaiBaru }} WIB </span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="summary-item">
|
<div class="summary-item">
|
||||||
|
|
@ -100,18 +115,18 @@ class="summary-subtext">{{ \Carbon\Carbon::parse($request->tgl_booking)->transla
|
||||||
<p class="small mb-2">Transfer ke Rekening Berikut:</p>
|
<p class="small mb-2">Transfer ke Rekening Berikut:</p>
|
||||||
<div class="row g-2 mb-4">
|
<div class="row g-2 mb-4">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="formulirfoto-bank-card">
|
<div class="formulirbuket-bank-card">
|
||||||
<div class="d-flex align-items-center gap-2 mb-2">
|
<div class="d-flex align-items-center gap-2 mb-2">
|
||||||
<i class="bi bi-bank text-secondary"></i> <span class="small">BCA</span>
|
<i class="bi bi-bank text-secondary"></i> <span class="small ">BCA</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-0 small bank-num">1234567890</p>
|
<p class="mb-0 small bank-num">6860305231</p>
|
||||||
<p class="mb-0 x-small bank-name">a.n Flo.do</p>
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
</div>
|
</div>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-secondary py-1 px-3 x-small btn-copy"
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
data-clipboard-text="1234567890">salin</button>
|
data-clipboard-text="6860305231" style="font-size: 12px">salin</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -122,12 +137,44 @@ class="btn btn-sm btn-outline-secondary py-1 px-3 x-small btn-copy"
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-end">
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-0 small bank-num">0987654321</p>
|
<p class="mb-0 small bank-num">005601002337563</p>
|
||||||
<p class="mb-0 x-small bank-name">a.n Flo.do</p>
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
</div>
|
</div>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-outline-secondary py-1 px-3 x-small btn-copy"
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
data-clipboard-text="0987654321">salin</button>
|
data-clipboard-text="005601002337563" style="font-size: 12px">salin</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="formulirbuket-bank-card">
|
||||||
|
<div class="d-flex align-items-center gap-2 mb-2">
|
||||||
|
<i class="bi bi-bank text-secondary"></i> <span class="small ">ShopeePay</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 small bank-num">082133958469</p>
|
||||||
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
|
data-clipboard-text="082133958469" style="font-size: 12px">salin</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="formulirbuket-bank-card">
|
||||||
|
<div class="d-flex align-items-center gap-2 mb-2">
|
||||||
|
<i class="bi bi-bank text-secondary"></i> <span class="small ">Dana</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-end">
|
||||||
|
<div>
|
||||||
|
<p class="mb-0 small bank-num">082133958469</p>
|
||||||
|
<p class="mb-0 x-small bank-name">a.n Deborah Febryana</p>
|
||||||
|
</div>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-outline-secondary py-1 px-2 btn-copy"
|
||||||
|
data-clipboard-text="082133958469" style="font-size: 12px">salin</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -135,16 +182,18 @@ class="btn btn-sm btn-outline-secondary py-1 px-3 x-small btn-copy"
|
||||||
|
|
||||||
<p class="small mb-2">Upload Bukti Pembayaran</p>
|
<p class="small mb-2">Upload Bukti Pembayaran</p>
|
||||||
<div class="formulirbuket-upload-area mb-2 text-center position-relative">
|
<div class="formulirbuket-upload-area mb-2 text-center position-relative">
|
||||||
<input type="file"
|
<input type="file" name="bukti_bayar" accept="image/*"
|
||||||
class="position-absolute w-100 h-100 opacity-0 start-0 top-0 cursor-pointer"
|
class="position-absolute w-100 h-100 opacity-0 start-0 top-0 cursor-pointer @error('bukti_bayar') is-invalid @enderror"
|
||||||
name="bukti_bayar" accept="image/*" id="fileUpload">
|
id="fileUpload" value="{{ old('bukti_bayar') }}">
|
||||||
<div class="py-4" id="uploadPlaceholder">
|
<div class="py-4" id="uploadPlaceholder">
|
||||||
<i class="bi bi-file-earmark-arrow-up fs-3 text-secondary"></i>
|
<i class="bi bi-file-earmark-arrow-up fs-3 text-secondary"></i>
|
||||||
<p class="mb-0 small text-muted">Upload Bukti Pembayaran</p>
|
<p class="mb-0 small text-muted">Upload Bukti Pembayaran</p>
|
||||||
<p class="mb-0 x-small text-muted">Max. 2 MB</p>
|
<p class="mb-0 x-small text-muted">Max. 2 MB</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@error('bukti_bayar')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
<p class="x-small text-muted mb-4">
|
<p class="x-small text-muted mb-4">
|
||||||
*Note: Pastikan pembayaran sudah dilakukan sebelum mengunggah bukti, ya!
|
*Note: Pastikan pembayaran sudah dilakukan sebelum mengunggah bukti, ya!
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -165,11 +214,14 @@ class="btn formulirfoto-btn-cancel flex-fill">Batalkan</a> --}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 1. VARIABLE PENGUNCI
|
||||||
|
let isPaymentTimeUp = false; //
|
||||||
|
const submitBtn = document.querySelector('.formulirfoto-btn-submit');
|
||||||
|
|
||||||
// 1. FITUR COPY NO REK
|
// 2. FITUR COPY NO REK (Tetap sama)
|
||||||
document.querySelectorAll('.btn-copy').forEach(btn => {
|
document.querySelectorAll('.btn-copy').forEach(btn => {
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', function() {
|
||||||
navigator.clipboard.writeText(this.getAttribute('data-clipboard-text'));
|
navigator.clipboard.writeText(this.getAttribute('data-clipboard-text'));
|
||||||
|
|
@ -181,7 +233,7 @@ class="btn formulirfoto-btn-cancel flex-fill">Batalkan</a> --}}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. FITUR PREVIEW UPLOAD
|
// 3. FITUR PREVIEW UPLOAD (Tetap sama)
|
||||||
const fileInput = document.getElementById('fileUpload');
|
const fileInput = document.getElementById('fileUpload');
|
||||||
if (fileInput) {
|
if (fileInput) {
|
||||||
fileInput.addEventListener('change', function() {
|
fileInput.addEventListener('change', function() {
|
||||||
|
|
@ -193,14 +245,13 @@ class="btn formulirfoto-btn-cancel flex-fill">Batalkan</a> --}}
|
||||||
<p class="mb-0 small text-success fw-bold">${file.name}</p>
|
<p class="mb-0 small text-success fw-bold">${file.name}</p>
|
||||||
<p class="mb-0 x-small text-muted">Klik lagi untuk ganti file</p>
|
<p class="mb-0 x-small text-muted">Klik lagi untuk ganti file</p>
|
||||||
`;
|
`;
|
||||||
// Optional: Kasih border hijau biar makin jelas
|
|
||||||
placeholder.parentElement.style.borderColor = "#198754";
|
placeholder.parentElement.style.borderColor = "#198754";
|
||||||
placeholder.parentElement.style.backgroundColor = "#e8f5e9";
|
placeholder.parentElement.style.backgroundColor = "#e8f5e9";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. FITUR TIMER MUNDUR
|
// 4. FITUR TIMER MUNDUR
|
||||||
let sisaDetik = {{ $sisaWaktu }};
|
let sisaDetik = {{ $sisaWaktu }};
|
||||||
let display = document.querySelector('#countdown-timer');
|
let display = document.querySelector('#countdown-timer');
|
||||||
|
|
||||||
|
|
@ -221,9 +272,24 @@ function startTimer(duration) {
|
||||||
|
|
||||||
if (--timer < 0) {
|
if (--timer < 0) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
alert("Waktu pembayaran habis!");
|
isPaymentTimeUp = true; // Kunci status
|
||||||
window.location.href =
|
|
||||||
"{{ route('booking.cancel') }}"; // Redirect ke cancel agar session bersih
|
if (submitBtn) {
|
||||||
|
submitBtn.disabled = true; // Matikan tombol secara visual
|
||||||
|
submitBtn.classList.add('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gunakan SweetAlert agar pasti muncul
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Waktu Pembayaran Habis!',
|
||||||
|
text: 'Maaf, batas waktu pembayaran Anda telah berakhir, jadwal booking akan dibatalkan.',
|
||||||
|
confirmButtonColor: '#3B8181',
|
||||||
|
icon: 'error',
|
||||||
|
confirmButtonText: 'Kembali ke Katalog',
|
||||||
|
allowOutsideClick: false
|
||||||
|
}).then(() => {
|
||||||
|
window.location.href = "{{ route('booking.cancel') }}";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
@ -232,9 +298,19 @@ function startTimer(duration) {
|
||||||
if (sisaDetik > 0) {
|
if (sisaDetik > 0) {
|
||||||
startTimer(sisaDetik);
|
startTimer(sisaDetik);
|
||||||
} else {
|
} else {
|
||||||
// Jaga-jaga kalau sisaDetik 0 pas load (redirect langsung)
|
|
||||||
window.location.href = "{{ route('booking.cancel') }}";
|
window.location.href = "{{ route('booking.cancel') }}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5. VALIDASI TAMBAHAN SAAT SUBMIT (Mencegah submit jika waktu habis)
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
if (form) {
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
if (isPaymentTimeUp) {
|
||||||
|
e.preventDefault(); // Stop kirim data
|
||||||
|
Swal.fire('Gagal!', 'Waktu pembayaran sudah habis.', 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
||||||
|
|
@ -1,407 +1 @@
|
||||||
<!DOCTYPE html>
|
tes
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Beranda Admin | Flo.do</title>
|
|
||||||
|
|
||||||
<link rel="icon" href="{{ asset('favicon.ico') }}" type="image/x-icon">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ asset('css/app-dark.css') }}">
|
|
||||||
<link rel="stylesheet" href="{{ asset('css/iconly.css') }}">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="app">
|
|
||||||
<div id="sidebar">
|
|
||||||
<div class="sidebar-wrapper active">
|
|
||||||
<div class="sidebar-header position-relative">
|
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
|
||||||
<div class="logo">
|
|
||||||
<img src="{{ asset('img/logo.png') }}" alt="Logo"></img>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-menu">
|
|
||||||
<ul class="menu">
|
|
||||||
<li class="sidebar-item active ">
|
|
||||||
<a href="index.html" class='sidebar-link'>
|
|
||||||
<i class="bi bi-grid-fill"></i>
|
|
||||||
<span>Beranda</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="sidebar-title">Kelola Pesanan</li>
|
|
||||||
|
|
||||||
<li class="sidebar-item ">
|
|
||||||
<a href="form-layout.html" class='sidebar-link'>
|
|
||||||
<i class="bi bi-flower1"></i>
|
|
||||||
<span>Pesanan Bunga</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="sidebar-item ">
|
|
||||||
<a href="form-layout.html" class='sidebar-link'>
|
|
||||||
<i class="bi bi-camera-fill"></i>
|
|
||||||
<span>Pesanan Foto</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="sidebar-item ">
|
|
||||||
<a href="form-layout.html" class='sidebar-link'>
|
|
||||||
<i class="bi bi-clock-fill"></i>
|
|
||||||
<span>Riwayat Pesanan</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li class="sidebar-title">Kelola Produk</li>
|
|
||||||
|
|
||||||
<li class="sidebar-item ">
|
|
||||||
<a href="form-layout.html" class='sidebar-link'>
|
|
||||||
<i class="bi bi-flower1"></i>
|
|
||||||
<span>Produk Bunga</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="sidebar-item ">
|
|
||||||
<a href="form-layout.html" class='sidebar-link'>
|
|
||||||
<i class="bi bi-camera-fill"></i>
|
|
||||||
<span>Paket Foto</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="main">
|
|
||||||
<header class="mb-3">
|
|
||||||
<a href="#" class="burger-btn d-block d-xl-none">
|
|
||||||
<i class="bi bi-justify fs-3"></i>
|
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="page-heading">
|
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
|
||||||
|
|
||||||
<h3>Beranda</h3>
|
|
||||||
|
|
||||||
<div class="dropdown">
|
|
||||||
<a href="#" id="profileDropdown" data-bs-toggle="dropdown" aria-expanded="false"><i
|
|
||||||
class="bi bi-person-circle fs-3 text-secondary"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="profileDropdown"
|
|
||||||
style="min-width: 200px;">
|
|
||||||
<li class="dropdown-header text-center">
|
|
||||||
<h6 class="mb-0 text-dark">Nama Admin</h6>
|
|
||||||
<small class="text-muted">Administrator</small>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<hr class="dropdown-divider">
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item d-flex align-items-center" href="#"> Profil Saya
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item d-flex align-items-center text-danger" href="#"> Logout
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="page-content">
|
|
||||||
<section class="row">
|
|
||||||
<div class="col-12 col-lg-9">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6 col-lg-3 col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body px-4 py-4-5">
|
|
||||||
<div class="row">
|
|
||||||
<div
|
|
||||||
class="col-md-4 col-lg-12 col-xl-12 col-xxl-5 d-flex justify-content-start ">
|
|
||||||
<div class="stats-icon purple mb-2">
|
|
||||||
<i class="iconly-boldShow"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8 col-lg-12 col-xl-12 col-xxl-7">
|
|
||||||
<h6 class="text-muted font-semibold">Profile Views</h6>
|
|
||||||
<h6 class="font-extrabold mb-0">112.000</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-6 col-lg-3 col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body px-4 py-4-5">
|
|
||||||
<div class="row">
|
|
||||||
<div
|
|
||||||
class="col-md-4 col-lg-12 col-xl-12 col-xxl-5 d-flex justify-content-start ">
|
|
||||||
<div class="stats-icon blue mb-2">
|
|
||||||
<i class="iconly-boldProfile"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8 col-lg-12 col-xl-12 col-xxl-7">
|
|
||||||
<h6 class="text-muted font-semibold">Followers</h6>
|
|
||||||
<h6 class="font-extrabold mb-0">183.000</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-6 col-lg-3 col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body px-4 py-4-5">
|
|
||||||
<div class="row">
|
|
||||||
<div
|
|
||||||
class="col-md-4 col-lg-12 col-xl-12 col-xxl-5 d-flex justify-content-start ">
|
|
||||||
<div class="stats-icon green mb-2">
|
|
||||||
<i class="iconly-boldAdd-User"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8 col-lg-12 col-xl-12 col-xxl-7">
|
|
||||||
<h6 class="text-muted font-semibold">Following</h6>
|
|
||||||
<h6 class="font-extrabold mb-0">80.000</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-6 col-lg-3 col-md-6">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body px-4 py-4-5">
|
|
||||||
<div class="row">
|
|
||||||
<div
|
|
||||||
class="col-md-4 col-lg-12 col-xl-12 col-xxl-5 d-flex justify-content-start ">
|
|
||||||
<div class="stats-icon red mb-2">
|
|
||||||
<i class="iconly-boldBookmark"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8 col-lg-12 col-xl-12 col-xxl-7">
|
|
||||||
<h6 class="text-muted font-semibold">Saved Post</h6>
|
|
||||||
<h6 class="font-extrabold mb-0">112</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h4>Profile Visit</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div id="chart-profile-visit"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-xl-4">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h4>Profile Visit</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-7">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<svg class="bi text-primary" width="32" height="32"
|
|
||||||
fill="blue" style="width:10px">
|
|
||||||
<use
|
|
||||||
xlink:href="static/images/bootstrap-icons.svg#circle-fill" />
|
|
||||||
</svg>
|
|
||||||
<h5 class="mb-0 ms-3">Europe</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-5">
|
|
||||||
<h5 class="mb-0 text-end">862</h5>
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<div id="chart-europe"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-7">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<svg class="bi text-success" width="32" height="32"
|
|
||||||
fill="blue" style="width:10px">
|
|
||||||
<use
|
|
||||||
xlink:href="static/images/bootstrap-icons.svg#circle-fill" />
|
|
||||||
</svg>
|
|
||||||
<h5 class="mb-0 ms-3">America</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-5">
|
|
||||||
<h5 class="mb-0 text-end">375</h5>
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<div id="chart-america"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-7">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<svg class="bi text-danger" width="32" height="32"
|
|
||||||
fill="blue" style="width:10px">
|
|
||||||
<use
|
|
||||||
xlink:href="static/images/bootstrap-icons.svg#circle-fill" />
|
|
||||||
</svg>
|
|
||||||
<h5 class="mb-0 ms-3">Indonesia</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-5">
|
|
||||||
<h5 class="mb-0 text-end">1025</h5>
|
|
||||||
</div>
|
|
||||||
<div class="col-12">
|
|
||||||
<div id="chart-indonesia"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-xl-8">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h4>Latest Comments</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-lg">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Comment</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td class="col-3">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="avatar avatar-md">
|
|
||||||
<img src="./jpg/5.jpg">
|
|
||||||
</div>
|
|
||||||
<p class="font-bold ms-3 mb-0">Si Cantik</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="col-auto">
|
|
||||||
<p class=" mb-0">Congratulations on your graduation!</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="col-3">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="avatar avatar-md">
|
|
||||||
<img src="./jpg/2.jpg">
|
|
||||||
</div>
|
|
||||||
<p class="font-bold ms-3 mb-0">Si Ganteng</p>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="col-auto">
|
|
||||||
<p class=" mb-0">Wow amazing design! Can you make another
|
|
||||||
tutorial for
|
|
||||||
this design?</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-lg-3">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body py-4 px-4">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="avatar avatar-xl">
|
|
||||||
<img src="./jpg/1.jpg" alt="Face 1">
|
|
||||||
</div>
|
|
||||||
<div class="ms-3 name">
|
|
||||||
<h5 class="font-bold">John Duck</h5>
|
|
||||||
<h6 class="text-muted mb-0">@johnducky</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h4>Recent Messages</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-content pb-4">
|
|
||||||
<div class="recent-message d-flex px-4 py-3">
|
|
||||||
<div class="avatar avatar-lg">
|
|
||||||
<img src="./jpg/4.jpg">
|
|
||||||
</div>
|
|
||||||
<div class="name ms-4">
|
|
||||||
<h5 class="mb-1">Hank Schrader</h5>
|
|
||||||
<h6 class="text-muted mb-0">@johnducky</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="recent-message d-flex px-4 py-3">
|
|
||||||
<div class="avatar avatar-lg">
|
|
||||||
<img src="./jpg/5.jpg">
|
|
||||||
</div>
|
|
||||||
<div class="name ms-4">
|
|
||||||
<h5 class="mb-1">Dean Winchester</h5>
|
|
||||||
<h6 class="text-muted mb-0">@imdean</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="recent-message d-flex px-4 py-3">
|
|
||||||
<div class="avatar avatar-lg">
|
|
||||||
<img src="./jpg/1.jpg">
|
|
||||||
</div>
|
|
||||||
<div class="name ms-4">
|
|
||||||
<h5 class="mb-1">John Dodol</h5>
|
|
||||||
<h6 class="text-muted mb-0">@dodoljohn</h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="px-4">
|
|
||||||
<button class='btn btn-block btn-xl btn-outline-primary font-bold mt-3'>Start
|
|
||||||
Conversation</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h4>Visitors Profile</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div id="chart-visitors-profile"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<div class="footer clearfix mb-0 text-muted">
|
|
||||||
<div class="float-start">
|
|
||||||
<p>2023 © Mazer</p>
|
|
||||||
</div>
|
|
||||||
<div class="float-end">
|
|
||||||
<p>Crafted with <span class="text-danger"><i class="bi bi-heart-fill icon-mid"></i></span>
|
|
||||||
by <a href="https://saugi.me">Saugi</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="static/js/components/dark.js"></script>
|
|
||||||
<script src="extensions/perfect-scrollbar/perfect-scrollbar.min.js"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script src="{{ asset('js/app.js') }}"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Need: Apexcharts -->
|
|
||||||
<script src="extensions/apexcharts/apexcharts.min.js"></script>
|
|
||||||
<script src="static/js/pages/dashboard.js"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue