commit
b89c909a95
|
|
@ -3,68 +3,24 @@
|
|||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Additional;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\Admin\AdditionalRequest; // Gunakan FormRequest baru
|
||||
|
||||
class AdditionalController extends Controller
|
||||
{
|
||||
// Store: Simpan Data Baru
|
||||
public function store(Request $request)
|
||||
public function store(AdditionalRequest $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
], [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'min' => ':attribute minimal harus :min karakter/nilai.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
], [
|
||||
'nama' => 'nama additional',
|
||||
'harga' => 'harga additional',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_modal', 'createAdd');
|
||||
}
|
||||
Additional::create($request->only(['nama', 'harga']));
|
||||
Additional::create($request->validated());
|
||||
return redirect()->back()->with('success', 'Additional berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
// Update: Simpan Perubahan
|
||||
public function update(Request $request, string $id)
|
||||
public function update(AdditionalRequest $request, string $id)
|
||||
{
|
||||
$additional = Additional::findOrFail($id);
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
], [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'min' => ':attribute minimal harus :min karakter/nilai.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
], [
|
||||
'nama' => 'nama additional',
|
||||
'harga' => 'harga additional',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_id_add', $id);
|
||||
}
|
||||
$additional->update($request->only(['nama', 'harga']));
|
||||
$additional->update($request->validated());
|
||||
return redirect()->back()->with('success', 'Additional berhasil diperbarui!');
|
||||
}
|
||||
|
||||
// Destroy: Hapus Data
|
||||
public function destroy(string $id)
|
||||
{
|
||||
$additional = Additional::findOrFail($id);
|
||||
|
|
|
|||
|
|
@ -7,55 +7,66 @@
|
|||
use App\Models\BookingFoto;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Services\DashboardService;
|
||||
|
||||
class BerandaController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$now = Carbon::now();
|
||||
$lastMonth = Carbon::now()->subMonth();
|
||||
$today = Carbon::today();
|
||||
$getGrowth = function ($current, $previous) {
|
||||
if ($previous <= 0) return $current > 0 ? 100 : 0;
|
||||
return round((($current - $previous) / $previous) * 100, 1);
|
||||
};
|
||||
$currPendapatan = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_transaksi', 'diterima')->sum('total_bayar') +
|
||||
BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_booking', 'diterima')->sum('total_bayar');
|
||||
|
||||
$currMasuk = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->count() +
|
||||
BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->count();
|
||||
|
||||
$currSelesai = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_transaksi', 'selesai')->count() +
|
||||
BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_booking', 'selesai')->count();
|
||||
|
||||
$currBatal = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_transaksi', 'ditolak')->count() +
|
||||
BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_booking', 'ditolak')->count();
|
||||
$prevPendapatan = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_transaksi', 'diterima')->sum('total_bayar') +
|
||||
BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_booking', 'diterima')->sum('total_bayar');
|
||||
|
||||
$prevMasuk = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->count() +
|
||||
BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->count();
|
||||
|
||||
$prevSelesai = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_transaksi', 'selesai')->count() +
|
||||
BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_booking', 'selesai')->count();
|
||||
|
||||
$prevBatal = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_transaksi', 'ditolak')->count() +
|
||||
BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_booking', 'ditolak')->count();
|
||||
$stat = [
|
||||
'pendapatan' => $currPendapatan,
|
||||
'pendapatan_grow' => $getGrowth($currPendapatan, $prevPendapatan),
|
||||
'masuk_count' => $currMasuk,
|
||||
'masuk_grow' => $getGrowth($currMasuk, $prevMasuk),
|
||||
'selesai_count' => $currSelesai,
|
||||
'selesai_grow' => $getGrowth($currSelesai, $prevSelesai),
|
||||
'batal_count' => $currBatal,
|
||||
'batal_grow' => $getGrowth($currBatal, $prevBatal),
|
||||
];
|
||||
$buketToday = TransaksiBuket::with(['pelanggan', 'buket'])->whereDate('tgl_ambil', $today)->where('status_transaksi', 'diterima')->get();
|
||||
$fotoToday = BookingFoto::with(['pelanggan', 'paketFoto'])->whereDate('tgl_booking', $today)->where('status_booking', 'diterima')->orderBy('jam_mulai', 'asc')->get();
|
||||
$pesananBuket = TransaksiBuket::with(['pelanggan', 'buket'])->where('status_transaksi', 'menunggu_verifikasi')->latest()->get();
|
||||
$pesananFoto = BookingFoto::with(['pelanggan', 'paketFoto'])->where('status_booking', 'menunggu_verifikasi')->latest()->get();
|
||||
|
||||
$stat = DashboardService::getStats();
|
||||
$buketToday = TransaksiBuket::with(['pelanggan', 'buket'])->whereDate('tgl_ambil', $today)->where('status_transaksi', 'diterima')->get();
|
||||
$fotoToday = BookingFoto::with(['pelanggan', 'paketFoto'])->whereDate('tgl_booking', $today)->where('status_booking', 'diterima')->orderBy('jam_mulai', 'asc')->get();
|
||||
$pesananBuket = TransaksiBuket::where('status_transaksi', 'menunggu_verifikasi')->latest()->get();
|
||||
$pesananFoto = BookingFoto::where('status_booking', 'menunggu_verifikasi')->latest()->get();
|
||||
return view('admin.beranda.index', compact('stat', 'buketToday', 'fotoToday', 'pesananBuket', 'pesananFoto'));
|
||||
}
|
||||
// public function index()
|
||||
// {
|
||||
// $now = Carbon::now();
|
||||
// $lastMonth = Carbon::now()->subMonth();
|
||||
// $today = Carbon::today();
|
||||
// $getGrowth = function ($current, $previous) {
|
||||
// if ($previous <= 0) return $current > 0 ? 100 : 0;
|
||||
// return round((($current - $previous) / $previous) * 100, 1);
|
||||
// };
|
||||
// $currPendapatan = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_transaksi', 'diterima')->sum('total_bayar') +
|
||||
// BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_booking', 'diterima')->sum('total_bayar');
|
||||
|
||||
// $currMasuk = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->count() +
|
||||
// BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->count();
|
||||
|
||||
// $currSelesai = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_transaksi', 'selesai')->count() +
|
||||
// BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_booking', 'selesai')->count();
|
||||
|
||||
// $currBatal = TransaksiBuket::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_transaksi', 'ditolak')->count() +
|
||||
// BookingFoto::whereMonth('created_at', $now->month)->whereYear('created_at', $now->year)->where('status_booking', 'ditolak')->count();
|
||||
// $prevPendapatan = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_transaksi', 'diterima')->sum('total_bayar') +
|
||||
// BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_booking', 'diterima')->sum('total_bayar');
|
||||
|
||||
// $prevMasuk = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->count() +
|
||||
// BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->count();
|
||||
|
||||
// $prevSelesai = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_transaksi', 'selesai')->count() +
|
||||
// BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_booking', 'selesai')->count();
|
||||
|
||||
// $prevBatal = TransaksiBuket::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_transaksi', 'ditolak')->count() +
|
||||
// BookingFoto::whereMonth('created_at', $lastMonth->month)->whereYear('created_at', $lastMonth->year)->where('status_booking', 'ditolak')->count();
|
||||
// $stat = [
|
||||
// 'pendapatan' => $currPendapatan,
|
||||
// 'pendapatan_grow' => $getGrowth($currPendapatan, $prevPendapatan),
|
||||
// 'masuk_count' => $currMasuk,
|
||||
// 'masuk_grow' => $getGrowth($currMasuk, $prevMasuk),
|
||||
// 'selesai_count' => $currSelesai,
|
||||
// 'selesai_grow' => $getGrowth($currSelesai, $prevSelesai),
|
||||
// 'batal_count' => $currBatal,
|
||||
// 'batal_grow' => $getGrowth($currBatal, $prevBatal),
|
||||
// ];
|
||||
// $buketToday = TransaksiBuket::with(['pelanggan', 'buket'])->whereDate('tgl_ambil', $today)->where('status_transaksi', 'diterima')->get();
|
||||
// $fotoToday = BookingFoto::with(['pelanggan', 'paketFoto'])->whereDate('tgl_booking', $today)->where('status_booking', 'diterima')->orderBy('jam_mulai', 'asc')->get();
|
||||
// $pesananBuket = TransaksiBuket::with(['pelanggan', 'buket'])->where('status_transaksi', 'menunggu_verifikasi')->latest()->get();
|
||||
// $pesananFoto = BookingFoto::with(['pelanggan', 'paketFoto'])->where('status_booking', 'menunggu_verifikasi')->latest()->get();
|
||||
|
||||
// return view('admin.beranda.index', compact('stat', 'buketToday', 'fotoToday', 'pesananBuket', 'pesananFoto'));
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Buket;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\Admin\BuketRequest; // Panggil Request baru kamu
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class BuketController extends Controller
|
||||
|
|
@ -16,104 +15,29 @@ public function index()
|
|||
return view('admin.produk-buket.index', compact('buket'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(BuketRequest $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'ukuran' => 'required|in:S,M,L',
|
||||
'kategori' => 'required|in:single,fresh,premium_fresh,artificial',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
'request_khusus' => 'nullable|string|max:255',
|
||||
'deskripsi' => 'required|string|min:10',
|
||||
'foto' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
||||
], [
|
||||
'required' => 'Kolom :attribute tidak boleh kosong.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'min' => ':attribute terlalu pendek, minimal harus :min karakter.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'in' => 'Pilihan :attribute tidak sesuai dengan data yang tersedia.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'image' => ':attribute harus berupa file gambar (foto).',
|
||||
'mimes' => 'Format :attribute tidak didukung. Gunakan format: jpeg, png, atau jpg.',
|
||||
'foto.max' => 'Ukuran :attribute terlalu besar, maksimal adalah 2MB.',
|
||||
], [
|
||||
'nama' => 'nama buket',
|
||||
'ukuran' => 'ukuran buket',
|
||||
'kategori' => 'kategori buket',
|
||||
'harga' => 'harga',
|
||||
'request_khusus' => 'request khusus',
|
||||
'deskripsi' => 'deskripsi produk',
|
||||
'foto' => 'foto produk',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_modal', 'create');
|
||||
}
|
||||
$path = null;
|
||||
$data = $request->validated();
|
||||
if ($request->hasFile('foto')) {
|
||||
$file = $request->file('foto');
|
||||
$filename = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs('img/buket', $filename, 'public');
|
||||
$data['foto'] = $file->storeAs('img/buket', $filename, 'public');
|
||||
}
|
||||
Buket::create([
|
||||
'nama' => $request->nama,
|
||||
'ukuran' => $request->ukuran,
|
||||
'kategori' => $request->kategori,
|
||||
'harga' => $request->harga,
|
||||
'request_khusus' => $request->request_khusus,
|
||||
'deskripsi' => $request->deskripsi,
|
||||
'foto' => $path,
|
||||
]);
|
||||
Buket::create($data);
|
||||
return redirect()->back()->with('success', 'Produk buket berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
public function update(BuketRequest $request, string $id)
|
||||
{
|
||||
$buket = Buket::findOrFail($id);
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'ukuran' => 'required|in:S,M,L',
|
||||
'kategori' => 'required|in:single,fresh,premium_fresh,artificial',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
'request_khusus' => 'nullable|string|max:255',
|
||||
'deskripsi' => 'required|string|min:10',
|
||||
'foto' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||
], [
|
||||
'required' => 'Kolom :attribute tidak boleh kosong.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'min' => ':attribute terlalu pendek, minimal harus :min karakter.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'in' => 'Pilihan :attribute tidak sesuai dengan data yang tersedia.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'image' => ':attribute harus berupa file gambar (foto).',
|
||||
'mimes' => 'Format :attribute tidak didukung. Gunakan format: jpeg, png, atau jpg.',
|
||||
'foto.max' => 'Ukuran :attribute terlalu besar, maksimal adalah 2MB.',
|
||||
], [
|
||||
'nama' => 'nama buket',
|
||||
'ukuran' => 'ukuran buket',
|
||||
'kategori' => 'kategori buket',
|
||||
'harga' => 'harga',
|
||||
'request_khusus' => 'request khusus',
|
||||
'deskripsi' => 'deskripsi produk',
|
||||
'foto' => 'foto produk',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_id', $id);
|
||||
}
|
||||
$data = $request->only(['nama', 'ukuran', 'kategori', 'harga', 'request_khusus', 'deskripsi']);
|
||||
$data = $request->validated();
|
||||
if ($request->hasFile('foto')) {
|
||||
if ($buket->foto) {
|
||||
Storage::disk('public')->delete($buket->foto);
|
||||
}
|
||||
$file = $request->file('foto');
|
||||
$filename = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs('img/buket', $filename, 'public');
|
||||
$data['foto'] = $path;
|
||||
$data['foto'] = $file->storeAs('img/buket', $filename, 'public');
|
||||
}
|
||||
$buket->update($data);
|
||||
return redirect()->back()->with('success', 'Produk buket berhasil diperbarui!');
|
||||
|
|
@ -122,12 +46,10 @@ public function update(Request $request, string $id)
|
|||
public function destroy(string $id)
|
||||
{
|
||||
$buket = Buket::findOrFail($id);
|
||||
|
||||
if ($buket->foto) {
|
||||
Storage::disk('public')->delete($buket->foto);
|
||||
}
|
||||
$buket->delete();
|
||||
|
||||
return redirect()->back()->with('success', 'Produk dan foto berhasil dihapus permanen!');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,11 @@
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Additional;
|
||||
use App\Models\PaketFoto;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\Admin\FotoRequest; // Gunakan Request baru
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class FotoController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$foto = PaketFoto::latest()->get();
|
||||
|
|
@ -21,100 +17,30 @@ public function index()
|
|||
return view('admin.paket-foto.index', compact('foto', 'additional'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(FotoRequest $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
'durasi' => 'required|integer|min:0',
|
||||
'deskripsi' => 'required|string|min:10',
|
||||
'foto' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
||||
], [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'integer' => 'Input :attribute harus berupa angka valid.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'image' => ':attribute harus berupa file gambar (foto).',
|
||||
'mimes' => 'Format :attribute tidak didukung. Gunakan format: jpeg, png, atau jpg.',
|
||||
'foto.max' => 'Ukuran :attribute maksimal 2MB.',
|
||||
'min' => ':attribute minimal harus :min karakter/nilai.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
|
||||
], [
|
||||
'nama' => 'nama paket',
|
||||
'harga' => 'harga paket',
|
||||
'durasi' => 'durasi paket',
|
||||
'foto' => 'foto paket',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_modal', 'createFoto');
|
||||
$data = $request->validated();
|
||||
if ($request->hasFile('foto')) {
|
||||
$file = $request->file('foto');
|
||||
$filename = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs('img/foto', $filename, 'public');
|
||||
$data['foto'] = $path;
|
||||
}
|
||||
if (!$request->hasFile('foto')) {
|
||||
return redirect()->back()
|
||||
->with('error', 'File foto tidak ditemukan!')
|
||||
->withInput();
|
||||
}
|
||||
$file = $request->file('foto');
|
||||
$filename = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs('img/foto', $filename, 'public');
|
||||
if (!$path) {
|
||||
return redirect()->back()
|
||||
->with('error', 'Gagal upload file!')
|
||||
->withInput();
|
||||
}
|
||||
PaketFoto::create([
|
||||
'nama' => $request->nama,
|
||||
'harga' => $request->harga,
|
||||
'durasi' => $request->durasi,
|
||||
'deskripsi' => $request->deskripsi,
|
||||
'foto' => $path,
|
||||
]);
|
||||
PaketFoto::create($data);
|
||||
return redirect()->back()->with('success', 'Paket foto baru berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
public function update(Request $request, string $id)
|
||||
public function update(FotoRequest $request, string $id)
|
||||
{
|
||||
$paket = PaketFoto::findOrFail($id);
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
'durasi' => 'required|integer|min:0',
|
||||
'deskripsi' => 'required|string|min:10',
|
||||
'foto' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||
], [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'integer' => 'Input :attribute harus berupa angka valid.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'image' => ':attribute harus berupa file gambar (foto).',
|
||||
'mimes' => 'Format :attribute tidak didukung. Gunakan format: jpeg, png, atau jpg.',
|
||||
'foto.max' => 'Ukuran :attribute maksimal 2MB.',
|
||||
'min' => ':attribute minimal harus :min karakter/nilai.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
], [
|
||||
'nama' => 'nama paket',
|
||||
'harga' => 'harga paket',
|
||||
'durasi' => 'durasi paket',
|
||||
'foto' => 'foto paket',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_id_foto', $id);
|
||||
}
|
||||
$data = $request->only(['nama', 'harga', 'durasi', 'deskripsi']);
|
||||
$data = $request->validated();
|
||||
if ($request->hasFile('foto')) {
|
||||
if ($paket->foto) {
|
||||
Storage::disk('public')->delete($paket->foto);
|
||||
}
|
||||
$file = $request->file('foto');
|
||||
$filename = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs('img/foto', $filename, 'public');
|
||||
$data['foto'] = $path;
|
||||
$data['foto'] = $file->storeAs('img/foto', $filename, 'public');
|
||||
}
|
||||
$paket->update($data);
|
||||
return redirect()->back()->with('success', 'Paket foto berhasil diperbarui!');
|
||||
|
|
@ -122,14 +48,11 @@ public function update(Request $request, string $id)
|
|||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
// Cari data berdasarkan primary key id_paket
|
||||
$paket = PaketFoto::where('id_paket', $id)->firstOrFail();
|
||||
|
||||
$paket = PaketFoto::findOrFail($id);
|
||||
if ($paket->foto) {
|
||||
Storage::disk('public')->delete($paket->foto);
|
||||
}
|
||||
$paket->delete();
|
||||
|
||||
return redirect()->back()->with('success', 'Paket foto dan filenya berhasil dihapus!');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,14 +4,11 @@
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator; // <--- WAJIB ADA INI
|
||||
use Illuminate\Routing\Controllers\HasMiddleware;
|
||||
use Illuminate\Routing\Controllers\Middleware;
|
||||
use App\Http\Requests\Admin\AdminRequest; // Panggil Request Baru
|
||||
use Illuminate\Support\Facades\{Auth, Hash};
|
||||
use Illuminate\Routing\Controllers\{HasMiddleware, Middleware};
|
||||
|
||||
class ManajemenAdminController extends Controller
|
||||
class ManajemenAdminController extends Controller implements HasMiddleware
|
||||
{
|
||||
public static function middleware(): array
|
||||
{
|
||||
|
|
@ -24,112 +21,38 @@ public static function middleware(): array
|
|||
}),
|
||||
];
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$admin = User::where('role', '!=', 'pemilik')
|
||||
->latest()
|
||||
->get();
|
||||
$admin = User::where('role', '!=', 'pemilik')->latest()->get();
|
||||
return view('admin.kelola-admin.index', compact('admin'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
public function store(AdminRequest $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:5|max:100',
|
||||
'username' => 'required|string|alpha_dash|max:50|unique:users,username',
|
||||
'email' => 'required|email:dns|max:255|unique:users,email,',
|
||||
'no_wa' => 'required|numeric|digits_between:10,15',
|
||||
'role' => 'required|in:admin_foto,admin_buket',
|
||||
'alamat' => 'required|string|max:255',
|
||||
], [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'unique' => ':attribute sudah terdaftar di sistem, gunakan yang lain.',
|
||||
'min' => ':attribute minimal harus berisi :min karakter.',
|
||||
'max' => ':attribute maksimal hanya boleh :max karakter.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'digits_between' => ':attribute harus berjumlah antara :min sampai :max digit.',
|
||||
'email' => 'Format :attribute tidak valid.',
|
||||
'alpha_dash' => ':attribute hanya boleh berisi huruf, angka, serta simbol - dan _',
|
||||
'in' => ':attribute yang dipilih tidak sesuai dengan pilihan yang tersedia.',
|
||||
], [
|
||||
'nama' => 'nama lengkap',
|
||||
'username' => 'username',
|
||||
'email' => 'alamat email',
|
||||
'no_wa' => 'nomor WA',
|
||||
'role' => 'peran admin',
|
||||
'alamat' => 'alamat lengkap',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_modal', 'create');
|
||||
}
|
||||
User::create([
|
||||
'nama' => $request->nama,
|
||||
'username' => $request->username,
|
||||
'email' => $request->email,
|
||||
'no_wa' => $request->no_wa,
|
||||
'role' => $request->role,
|
||||
'alamat' => $request->alamat,
|
||||
'password' => Hash::make($request->username),
|
||||
]);
|
||||
$data = $request->validated();
|
||||
$data['password'] = Hash::make($request->username);
|
||||
User::create($data);
|
||||
return redirect()->back()->with('success', 'Admin berhasil ditambahkan!');
|
||||
}
|
||||
|
||||
// --- Bagian UPDATE ---
|
||||
public function update(Request $request, string $id)
|
||||
public function update(AdminRequest $request, string $id)
|
||||
{
|
||||
$admin = User::findOrFail($id);
|
||||
$validator = Validator::make($request->all(), [
|
||||
'nama' => 'required|string|min:5|max:100',
|
||||
'username' => 'required|string|alpha_dash|max:50|unique:users,username,' . $id . ',id_user',
|
||||
'email' => 'required|email:dns|max:255|unique:users,email,' . $id . ',id_user',
|
||||
'no_wa' => 'required|numeric|digits_between:10,15',
|
||||
'role' => 'required|in:admin_foto,admin_buket',
|
||||
'alamat' => 'required|string|max:255',
|
||||
], [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'unique' => ':attribute sudah terdaftar di sistem, gunakan yang lain.',
|
||||
'min' => ':attribute minimal harus berisi :min karakter.',
|
||||
'max' => ':attribute maksimal hanya boleh :max karakter.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'digits_between' => ':attribute harus berjumlah antara :min sampai :max digit.',
|
||||
'email' => 'Format :attribute tidak valid.',
|
||||
'alpha_dash' => ':attribute hanya boleh berisi huruf, angka, serta simbol - dan _',
|
||||
'in' => ':attribute yang dipilih tidak sesuai dengan pilihan yang tersedia.',
|
||||
], [
|
||||
'nama' => 'nama lengkap',
|
||||
'username' => 'username',
|
||||
'email' => 'alamat email',
|
||||
'no_wa' => 'nomor WA',
|
||||
'role' => 'peran admin',
|
||||
'alamat' => 'alamat lengkap',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()
|
||||
->withErrors($validator)
|
||||
->withInput()
|
||||
->with('error_id', $id);
|
||||
}
|
||||
$admin->update($request->only(['nama', 'username', 'email', 'no_wa', 'role', 'alamat']));
|
||||
$admin->update($request->validated());
|
||||
return redirect()->back()->with('success', 'Data berhasil diperbarui!');
|
||||
}
|
||||
|
||||
public function destroy(string $id)
|
||||
{
|
||||
// 1. Cari data admin berdasarkan ID
|
||||
$admin = User::findOrFail($id);
|
||||
|
||||
// 2. Keamanan tambahan: Jangan biarkan admin menghapus dirinya sendiri (opsional)
|
||||
if (Auth::id() == $admin->id_user) {
|
||||
return redirect()->back()->with('error', 'Anda tidak bisa menghapus akun sendiri!');
|
||||
}
|
||||
|
||||
// 3. Eksekusi hapus
|
||||
$admin->delete();
|
||||
|
||||
// 4. Kembali dengan pesan sukses
|
||||
return redirect()->route('admin.kelola-admin.index')
|
||||
->with('success', 'Admin berhasil dihapus secara permanen.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\TransaksiBuket;
|
||||
use App\Services\WhatsAppService; // Import Service
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PesananBuketController extends Controller
|
||||
|
|
@ -19,61 +20,21 @@ public function index()
|
|||
public function updateStatus(Request $request, $id)
|
||||
{
|
||||
try {
|
||||
$pesanan = \App\Models\TransaksiBuket::with(['pelanggan', 'buket'])->findOrFail($id);
|
||||
if ($request->jenis === 'terima') {
|
||||
$status = 'diterima';
|
||||
session()->flash('success', "Pesanan {$pesanan->no_invoice} telah diterima!");
|
||||
} elseif ($request->jenis === 'selesai') {
|
||||
$status = 'selesai';
|
||||
session()->flash('success', "Pesanan {$pesanan->no_invoice} berhasil diselesaikan!");
|
||||
} else {
|
||||
$status = 'ditolak';
|
||||
session()->flash('error', "Pesanan {$pesanan->no_invoice} telah ditolak!");
|
||||
}
|
||||
$pesanan = TransaksiBuket::with(['pelanggan', 'buket'])->findOrFail($id);
|
||||
$status = $request->jenis === 'terima' ? 'diterima' : ($request->jenis === 'selesai' ? 'selesai' : 'ditolak');
|
||||
$labels = [
|
||||
'diterima' => 'telah diterima',
|
||||
'selesai' => 'berhasil diselesaikan',
|
||||
'ditolak' => 'telah ditolak'
|
||||
];
|
||||
$sessionKey = $status === 'ditolak' ? 'error' : 'success';
|
||||
$sessionMsg = "Pesanan {$pesanan->no_invoice} " . $labels[$status] . "!";
|
||||
session()->flash($sessionKey, $sessionMsg);
|
||||
$pesanan->update(['status_transaksi' => $status]);
|
||||
$nama = $pesanan->pelanggan->nama;
|
||||
$produk = $pesanan->buket->nama;
|
||||
$tgl_obj = \Carbon\Carbon::parse($pesanan->tgl_ambil)->locale('id');
|
||||
$tanggal = $tgl_obj->translatedFormat('l, d F Y');
|
||||
$waktu = $tgl_obj->format('H:i');
|
||||
$total = number_format($pesanan->total_bayar, 0, ',', '.');
|
||||
$req = $pesanan->request ?? '-';
|
||||
$ucapan = $pesanan->ucapan ?? '-';
|
||||
$invoice = $pesanan->no_invoice;
|
||||
$msg = null;
|
||||
if ($status === 'diterima') {
|
||||
$msg = "Halo Kak *{$nama}*,\n\n" .
|
||||
"Pesanan Anda dengan Nomor Invoice: *{$invoice}* telah kami *TERIMA* dan masuk dalam daftar proses pengerjaan.\n\n" .
|
||||
"*Rincian Pesanan:*\n" .
|
||||
"- *Produk:* {$produk}\n" .
|
||||
"- *Total Bayar:* Rp {$total}\n" .
|
||||
"- *Request:* {$req}\n" .
|
||||
"- *Ucapan:* \"{$ucapan}\"\n" .
|
||||
"- *Waktu Pengambilan:* {$tanggal} pukul {$waktu} WIB\n\n" .
|
||||
"Mohon simpan rincian ini dan ditunggu info selanjutnya ya Kak. Terima kasih!";
|
||||
} elseif ($status === 'ditolak') {
|
||||
$msg = "Halo Kak *{$nama}*,\n\n" .
|
||||
"Mengenai pesanan Anda dengan Nomor Invoice: *{$invoice}* terpaksa kami *TOLAK* dikarenakan:\n\n" .
|
||||
"*[TULIS ALASAN DI SINI]*\n\n" .
|
||||
"*Rincian Pesanan:*\n" .
|
||||
"- *Produk:* {$produk}\n" .
|
||||
"- *Total Bayar:* Rp {$total}\n" .
|
||||
"- *Waktu Pengambilan:* {$tanggal}\n\n" .
|
||||
"Admin kami akan segera menghubungi Kakak terkait proses pengembalian dana. Mohon maaf atas ketidaknyamanannya.";
|
||||
}
|
||||
$wa_url = null;
|
||||
if ($msg) {
|
||||
$no_wa = preg_replace('/[^0-9]/', '', $pesanan->pelanggan->no_wa);
|
||||
if (str_starts_with($no_wa, '0')) {
|
||||
$no_wa = '62' . substr($no_wa, 1);
|
||||
} elseif (str_starts_with($no_wa, '8')) {
|
||||
$no_wa = '62' . $no_wa;
|
||||
}
|
||||
$wa_url = "https://wa.me/{$no_wa}?text=" . urlencode($msg);
|
||||
}
|
||||
$waUrl = WhatsAppService::getBuketNotification($pesanan, $status);
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'wa_url' => $wa_url
|
||||
'wa_url' => $waUrl
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\BookingFoto;
|
||||
use App\Services\WhatsAppService; // Import Service
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PesananFotoController extends Controller
|
||||
|
|
@ -24,72 +25,21 @@ public function index()
|
|||
public function updateStatus(Request $request, $id)
|
||||
{
|
||||
try {
|
||||
$pesanan = \App\Models\BookingFoto::with(['pelanggan', 'paketFoto', 'detailAdditional.additional'])->findOrFail($id);
|
||||
if ($request->jenis === 'terima') {
|
||||
$status = 'diterima';
|
||||
session()->flash('success', "Booking {$pesanan->no_invoice} telah diterima!");
|
||||
} elseif ($request->jenis === 'selesai') {
|
||||
$status = 'selesai';
|
||||
session()->flash('success', "Booking {$pesanan->no_invoice} berhasil diselesaikan!");
|
||||
} else {
|
||||
$status = 'ditolak';
|
||||
session()->flash('error', "Booking {$pesanan->no_invoice} telah ditolak!");
|
||||
}
|
||||
$pesanan = BookingFoto::with(['pelanggan', 'paketFoto', 'detailAdditional.additional'])->findOrFail($id);
|
||||
$status = $request->jenis === 'terima' ? 'diterima' : ($request->jenis === 'selesai' ? 'selesai' : 'ditolak');
|
||||
$labels = [
|
||||
'diterima' => 'telah diterima',
|
||||
'selesai' => 'berhasil diselesaikan',
|
||||
'ditolak' => 'telah ditolak'
|
||||
];
|
||||
$sessionKey = $status === 'ditolak' ? 'error' : 'success';
|
||||
$sessionMsg = "Booking {$pesanan->no_invoice} " . $labels[$status] . "!";
|
||||
session()->flash($sessionKey, $sessionMsg);
|
||||
$pesanan->update(['status_booking' => $status]);
|
||||
$nama = $pesanan->pelanggan->nama;
|
||||
$paket = $pesanan->paketFoto->nama;
|
||||
$tgl_obj = \Carbon\Carbon::parse($pesanan->tgl_booking)->locale('id');
|
||||
$tanggal = $tgl_obj->translatedFormat('l, d F Y');
|
||||
$jam_mulai = \Carbon\Carbon::parse($pesanan->jam_mulai)->format('H:i');
|
||||
$jam_selesai = \Carbon\Carbon::parse($pesanan->jam_selesai)->format('H:i');
|
||||
$total = number_format($pesanan->total_bayar, 0, ',', '.');
|
||||
$invoice = $pesanan->no_invoice;
|
||||
$list_additional = "";
|
||||
if ($pesanan->detailAdditional->count() > 0) {
|
||||
foreach ($pesanan->detailAdditional as $item) {
|
||||
$list_additional .= "- " . $item->additional->nama . " (x" . $item->qty . ")\n";
|
||||
}
|
||||
} else {
|
||||
$list_additional = "- Tidak ada additional\n";
|
||||
}
|
||||
$msg = null;
|
||||
if ($status === 'diterima') {
|
||||
$msg = "Halo Kak *{$nama}* ,\n\n" .
|
||||
"Booking foto Anda dengan Invoice: *{$invoice}* telah kami *TERIMA*. \n\n" .
|
||||
"*Rincian Booking:*\n" .
|
||||
"- *Paket:* {$paket}\n" .
|
||||
"*Additional:*\n{$list_additional}" .
|
||||
"- *Total Bayar:* Rp {$total}\n\n" .
|
||||
"*Jadwal Sesi Foto:*\n" .
|
||||
" Tanggal: {$tanggal}\n" .
|
||||
" Jam: {$jam_mulai} - {$jam_selesai} WIB\n\n" .
|
||||
"Mohon datang 15 menit sebelum jadwal dimulai ya Kak. Sampai jumpa di studio! ";
|
||||
} elseif ($status === 'ditolak') {
|
||||
$msg = "Halo Kak *{$nama}*,\n\n" .
|
||||
"Mohon maaf, booking foto Anda dengan Invoice *{$invoice}* terpaksa kami *TOLAK* dikarenakan:\n\n" .
|
||||
"*[TULIS ALASAN DI SINI]*\n\n" .
|
||||
"*Rincian Booking:*\n" .
|
||||
"- *Paket:* {$paket}\n" .
|
||||
"*Additional:*\n{$list_additional}" .
|
||||
"- *Total Bayar:* Rp {$total}\n" .
|
||||
"*Jadwal Sesi Foto:*\n" .
|
||||
" Tanggal: {$tanggal}\n" .
|
||||
" Jam: {$jam_mulai} - {$jam_selesai} WIB\n\n" .
|
||||
"Admin kami akan segera menghubungi Kakak untuk info pengembalian dana atau penjadwalan ulang. Terima kasih. ";
|
||||
}
|
||||
$wa_url = null;
|
||||
if ($msg) {
|
||||
$no_wa = preg_replace('/[^0-9]/', '', $pesanan->pelanggan->no_wa);
|
||||
if (str_starts_with($no_wa, '0')) {
|
||||
$no_wa = '62' . substr($no_wa, 1);
|
||||
} elseif (str_starts_with($no_wa, '8')) {
|
||||
$no_wa = '62' . $no_wa;
|
||||
}
|
||||
$wa_url = "https://wa.me/{$no_wa}?text=" . urlencode($msg);
|
||||
}
|
||||
$waUrl = WhatsAppService::getFotoNotification($pesanan, $status);
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'wa_url' => $wa_url
|
||||
'wa_url' => $waUrl
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
|
||||
|
|
|
|||
|
|
@ -3,16 +3,13 @@
|
|||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Additional;
|
||||
use App\Models\BookingFoto;
|
||||
use App\Models\DetailAdditional;
|
||||
use App\Models\PaketFoto;
|
||||
use App\Models\Pelanggan;
|
||||
use App\Models\{Additional, BookingFoto, DetailAdditional, PaketFoto, Pelanggan};
|
||||
use App\Http\Requests\User\BookingFotoRequest;
|
||||
use App\Services\WhatsAppService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Carbon\Carbon;
|
||||
|
||||
|
||||
class BookingFotoController extends Controller
|
||||
|
|
@ -64,204 +61,87 @@ public function cekSlot(Request $request)
|
|||
}
|
||||
public function formulir(Request $request)
|
||||
{
|
||||
$foto = PaketFoto::findOrFail($request->id_paket);
|
||||
$durasiDasar = $foto->durasi;
|
||||
$addonsDetails = [];
|
||||
$totalAddon = 0;
|
||||
$tambahanMenit = 0;
|
||||
if ($request->has('addons')) {
|
||||
foreach ($request->addons as $id => $qty) {
|
||||
if ($qty > 0) {
|
||||
$add = Additional::find($id);
|
||||
if ($add) {
|
||||
$subtotal = $add->harga * $qty;
|
||||
$totalAddon += $subtotal;
|
||||
|
||||
if ($id == 4) {
|
||||
$tambahanMenit += (5 * $qty);
|
||||
} elseif ($id == 6) {
|
||||
$tambahanMenit += (10 * $qty);
|
||||
}
|
||||
|
||||
$addonsDetails[] = [
|
||||
'nama' => $add->nama,
|
||||
'qty' => $qty,
|
||||
'subtotal' => $subtotal,
|
||||
'id' => $id
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$totalDurasi = $durasiDasar + $tambahanMenit;
|
||||
$jamMulai = \Carbon\Carbon::createFromFormat('H:i', $request->jam_mulai);
|
||||
$jamSelesaiBaru = $jamMulai->copy()->addMinutes($totalDurasi)->format('H:i');
|
||||
$grandTotal = $foto->harga + $totalAddon;
|
||||
$details = $this->calculateDetails($request);
|
||||
if (!session()->has('payment_deadline')) {
|
||||
$deadline = now()->addHours(2);
|
||||
session()->put('payment_deadline', $deadline);
|
||||
} else {
|
||||
$deadline = session('payment_deadline');
|
||||
session()->put('payment_deadline', now()->addHours(2));
|
||||
}
|
||||
$sisaWaktu = now()->diffInSeconds($deadline, false);
|
||||
$sisaWaktu = now()->diffInSeconds(session('payment_deadline'), false);
|
||||
if ($sisaWaktu <= 0) {
|
||||
session()->forget(['payment_deadline', 'addons']);
|
||||
return redirect()->route('booking.foto')->with('error', 'Waktu pembayaran telah habis.');
|
||||
}
|
||||
return view('user.pembayaran-foto', compact(
|
||||
'foto',
|
||||
'request',
|
||||
'addonsDetails',
|
||||
'grandTotal',
|
||||
'sisaWaktu',
|
||||
'jamSelesaiBaru'
|
||||
));
|
||||
return view('user.pembayaran-foto', array_merge($details, [
|
||||
'sisaWaktu' => $sisaWaktu,
|
||||
'request' => $request
|
||||
]));
|
||||
}
|
||||
public function cancelBooking()
|
||||
{
|
||||
session()->forget(['payment_deadline', 'addons']);
|
||||
return redirect()->route('booking.foto');
|
||||
}
|
||||
public function store(Request $request)
|
||||
public function store(BookingFotoRequest $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'id_paket' => 'required|exists:paket_fotos,id_paket',
|
||||
'tgl_booking' => 'required|date|after_or_equal:today',
|
||||
'jam_mulai' => 'required',
|
||||
'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',
|
||||
], [
|
||||
'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.',
|
||||
'bukti_bayar.max' => 'Ukuran :attribute maksimal adalah 2MB.',
|
||||
], [
|
||||
'nama' => 'nama pemesan',
|
||||
'no_wa' => 'nomor WhatsApp',
|
||||
'bukti_bayar' => 'bukti pembayaran',
|
||||
]);
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$paket = PaketFoto::findOrFail($request->id_paket);
|
||||
$durasiMenit = $paket->durasi;
|
||||
$jamMulai = \Carbon\Carbon::createFromFormat('H:i', $request->jam_mulai);
|
||||
$jamSelesai = $jamMulai->copy()->addMinutes($durasiMenit);
|
||||
|
||||
$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.');
|
||||
$details = $this->calculateDetails($request);
|
||||
return DB::transaction(function () use ($request, $details) {
|
||||
if (BookingFoto::where(['tgl_booking' => $request->tgl_booking, 'jam_mulai' => $request->jam_mulai])
|
||||
->whereIn('status_booking', ['menunggu_verifikasi', 'diterima'])->exists()
|
||||
) {
|
||||
return back()->with('error', 'Slot baru saja diambil orang lain.');
|
||||
}
|
||||
|
||||
$pelanggan = Pelanggan::firstOrCreate(
|
||||
['no_wa' => $request->no_wa],
|
||||
['nama' => $request->nama]
|
||||
);
|
||||
$grandTotal = $paket->harga;
|
||||
$listAdditional = [];
|
||||
$totalDurasi = $durasiMenit;
|
||||
if ($request->has('addons')) {
|
||||
foreach ($request->addons as $idAddon => $qty) {
|
||||
if ($qty > 0) {
|
||||
$add = \App\Models\Additional::find($idAddon);
|
||||
if ($add) {
|
||||
$subtotal = $add->harga * $qty;
|
||||
$grandTotal += $subtotal;
|
||||
|
||||
if ($idAddon == 4) $totalDurasi += (5 * $qty);
|
||||
if ($idAddon == 6) $totalDurasi += (10 * $qty);
|
||||
|
||||
$listAdditional[] = [
|
||||
'id_additional' => $idAddon,
|
||||
'qty' => $qty,
|
||||
'subtotal' => $subtotal,
|
||||
'nama_barang' => $add->nama
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$jamMulai = \Carbon\Carbon::createFromFormat('H:i', $request->jam_mulai);
|
||||
$jamSelesai = $jamMulai->copy()->addMinutes($totalDurasi);
|
||||
$isTaken = \App\Models\BookingFoto::where('tgl_booking', $request->tgl_booking)
|
||||
->where('jam_mulai', $request->jam_mulai)
|
||||
->whereIn('status_booking', ['menunggu_verifikasi', 'diterima'])
|
||||
->exists();
|
||||
if ($isTaken) {
|
||||
return back()->with('error', 'Mohon maaf, slot waktu ini baru saja diambil orang lain.');
|
||||
}
|
||||
$pelanggan = \App\Models\Pelanggan::create([
|
||||
'nama' => $request->nama,
|
||||
'no_wa' => $request->no_wa
|
||||
]);
|
||||
$pathBukti = null;
|
||||
if ($request->hasFile('bukti_bayar')) {
|
||||
$file = $request->file('bukti_bayar');
|
||||
$namaFile = 'bukti_' . time() . '.' . $file->getClientOriginalExtension();
|
||||
$pathBukti = $file->storeAs('img/payment/foto', $namaFile, 'public');
|
||||
}
|
||||
$booking = \App\Models\BookingFoto::create([
|
||||
'no_invoice' => 'INV-FOTO-' . strtoupper(\Illuminate\Support\Str::random(6)),
|
||||
'id_pelanggan' => $pelanggan->id_pelanggan,
|
||||
'id_paket' => $paket->id_paket,
|
||||
'tgl_booking' => $request->tgl_booking,
|
||||
'jam_mulai' => $request->jam_mulai,
|
||||
'jam_selesai' => $jamSelesai->format('H:i'),
|
||||
'total_bayar' => $grandTotal,
|
||||
'bukti_bayar' => $pathBukti,
|
||||
$pelanggan = Pelanggan::firstOrCreate(['no_wa' => $request->no_wa], ['nama' => $request->nama]);
|
||||
$pathBukti = $request->hasFile('bukti_bayar')
|
||||
? $request->file('bukti_bayar')->store('img/payment/foto', 'public')
|
||||
: null;
|
||||
$booking = BookingFoto::create([
|
||||
'no_invoice' => 'INV-FOTO-' . strtoupper(Str::random(6)),
|
||||
'id_pelanggan' => $pelanggan->id_pelanggan,
|
||||
'id_paket' => $details['foto']->id_paket,
|
||||
'tgl_booking' => $request->tgl_booking,
|
||||
'jam_mulai' => $request->jam_mulai,
|
||||
'jam_selesai' => $details['jamSelesaiBaru'],
|
||||
'total_bayar' => $details['grandTotal'],
|
||||
'bukti_bayar' => $pathBukti,
|
||||
'status_booking' => 'menunggu_verifikasi'
|
||||
]);
|
||||
foreach ($listAdditional as $item) {
|
||||
\App\Models\DetailAdditional::create([
|
||||
'id_booking' => $booking->id_booking,
|
||||
'id_additional' => $item['id_additional'],
|
||||
'qty' => $item['qty'],
|
||||
'subtotal' => $item['subtotal']
|
||||
foreach ($details['addonsDetails'] as $item) {
|
||||
DetailAdditional::create([
|
||||
'id_booking' => $booking->id_booking,
|
||||
'id_additional' => $item['id'],
|
||||
'qty' => $item['qty'],
|
||||
'subtotal' => $item['subtotal']
|
||||
]);
|
||||
}
|
||||
\Illuminate\Support\Facades\DB::commit();
|
||||
$txtAddons = "";
|
||||
if (count($listAdditional) > 0) {
|
||||
$txtAddons = "*Additional:*";
|
||||
foreach ($listAdditional as $item) {
|
||||
$txtAddons .= "\n- " . $item['nama_barang'] . " (" . $item['qty'] . "x)";
|
||||
}
|
||||
$txtAddons .= "\n";
|
||||
}
|
||||
$pesan = "Halo Admin Flo.do! Saya sudah melakukan pembayaran untuk invoice {$booking->no_invoice}:\n\n" .
|
||||
"*Data Pemesan:*\n" .
|
||||
"Nama: {$request->nama}\n" .
|
||||
"WA: {$request->no_wa}\n\n" .
|
||||
"*Detail Booking:*\n" .
|
||||
"Nama Paket: {$paket->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\n" .
|
||||
"Mohon segera diproses, ya! Terima kasih.";
|
||||
|
||||
$urlWA = "https://wa.me/6282337687878?text=" . urlencode($pesan);
|
||||
$urlWA = WhatsAppService::getBookingFotoMessage($booking, $details['foto'], $details['addonsDetails'], $details['grandTotal'], $details['jamSelesaiBaru']);
|
||||
session()->forget('payment_deadline');
|
||||
|
||||
return redirect()->route('booking.foto')->with([
|
||||
'success' => 'Pesanan Berhasil Dibuat!',
|
||||
'waUrl' => $urlWA
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\Illuminate\Support\Facades\DB::rollBack();
|
||||
return back()->with('error', 'Error: ' . $e->getMessage())->withInput();
|
||||
return redirect()->route('booking.foto')->with(['success' => 'Pesanan Berhasil!', 'waUrl' => $urlWA]);
|
||||
});
|
||||
}
|
||||
private function calculateDetails($request)
|
||||
{
|
||||
$foto = PaketFoto::findOrFail($request->id_paket);
|
||||
$totalAddon = 0;
|
||||
$tambahanMenit = 0;
|
||||
$addonsDetails = [];
|
||||
|
||||
if ($request->has('addons')) {
|
||||
foreach ($request->addons as $id => $qty) {
|
||||
if ($qty > 0 && $add = Additional::find($id)) {
|
||||
$subtotal = $add->harga * $qty;
|
||||
$totalAddon += $subtotal;
|
||||
if ($id == 4) $tambahanMenit += (5 * $qty);
|
||||
if ($id == 6) $tambahanMenit += (10 * $qty);
|
||||
$addonsDetails[] = ['nama' => $add->nama, 'qty' => $qty, 'subtotal' => $subtotal, 'id' => $id];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [
|
||||
'foto' => $foto,
|
||||
'addonsDetails' => $addonsDetails,
|
||||
'grandTotal' => $foto->harga + $totalAddon,
|
||||
'jamSelesaiBaru' => Carbon::createFromFormat('H:i', $request->jam_mulai)
|
||||
->addMinutes($foto->durasi + $tambahanMenit)->format('H:i')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Http\Requests\User\PesanBuketRequest;
|
||||
use App\Services\WhatsAppService;
|
||||
|
||||
class PesanBuketController extends Controller
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
|
||||
class AdditionalRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true; // Izinkan akses request
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'min' => ':attribute minimal harus :min karakter/nilai.',
|
||||
'max' => ':attribute melebihi batas maksimal :max karakter.',
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'nama additional',
|
||||
'harga' => 'harga additional',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Mengatur respon jika validasi gagal agar modal tidak tertutup.
|
||||
*/
|
||||
protected function failedValidation(Validator $validator)
|
||||
{
|
||||
$redirect = redirect()->back()->withErrors($validator)->withInput();
|
||||
|
||||
if ($this->isMethod('post')) {
|
||||
// Untuk modal tambah additional
|
||||
$redirect->with('error_modal', 'createAdd');
|
||||
} else {
|
||||
// Untuk modal edit berdasarkan ID additional
|
||||
$redirect->with('error_id_add', $this->route('additional'));
|
||||
}
|
||||
|
||||
throw new HttpResponseException($redirect);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
|
||||
class AdminRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Pastikan ini true agar request diproses
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
// Mengambil ID dari route untuk pengecualian 'unique' saat update
|
||||
$adminId = $this->route('id');
|
||||
|
||||
return [
|
||||
'nama' => 'required|string|min:5|max:100',
|
||||
'username' => 'required|string|alpha_dash|max:50|unique:users,username,' . $adminId . ',id_user',
|
||||
'email' => 'required|email:dns|max:255|unique:users,email,' . $adminId . ',id_user',
|
||||
'no_wa' => 'required|numeric|digits_between:10,15',
|
||||
'role' => 'required|in:admin_foto,admin_buket',
|
||||
'alamat' => 'required|string|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'unique' => ':attribute sudah terdaftar di sistem, gunakan yang lain.',
|
||||
'min' => ':attribute minimal harus berisi :min karakter.',
|
||||
'max' => ':attribute maksimal hanya boleh :max karakter.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'digits_between' => ':attribute harus berjumlah antara :min sampai :max digit.',
|
||||
'email' => 'Format :attribute tidak valid.',
|
||||
'alpha_dash' => ':attribute hanya boleh berisi huruf, angka, serta simbol - dan _',
|
||||
'in' => ':attribute yang dipilih tidak sesuai pilihan yang tersedia.',
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'nama lengkap',
|
||||
'username' => 'username',
|
||||
'email' => 'alamat email',
|
||||
'no_wa' => 'nomor WA',
|
||||
'role' => 'peran admin',
|
||||
'alamat' => 'alamat lengkap',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Penanganan khusus agar Modal tetap terbuka saat validasi gagal.
|
||||
*/
|
||||
protected function failedValidation(Validator $validator)
|
||||
{
|
||||
$redirect = redirect()->back()->withErrors($validator)->withInput();
|
||||
|
||||
if ($this->isMethod('post')) {
|
||||
$redirect->with('error_modal', 'create');
|
||||
} else {
|
||||
$redirect->with('error_id', $this->route('id'));
|
||||
}
|
||||
|
||||
throw new HttpResponseException($redirect);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
|
||||
class BuketRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
// Ubah jadi true supaya request diizinkan
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'ukuran' => 'required|in:S,M,L',
|
||||
'kategori' => 'required|in:single,fresh,premium_fresh,artificial',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
'request_khusus' => 'nullable|string|max:255',
|
||||
'deskripsi' => 'required|string|min:10',
|
||||
// Dinamis: required jika store (POST), nullable jika update (PUT)
|
||||
'foto' => $this->isMethod('post')
|
||||
? 'required|image|mimes:jpeg,png,jpg|max:2048'
|
||||
: 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'required' => 'Kolom :attribute tidak boleh kosong.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'min' => ':attribute terlalu pendek, minimal harus :min karakter.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'in' => 'Pilihan :attribute tidak sesuai dengan data yang tersedia.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'image' => ':attribute harus berupa file gambar (foto).',
|
||||
'mimes' => 'Format :attribute tidak didukung. Gunakan format: jpeg, png, atau jpg.',
|
||||
'foto.max' => 'Ukuran :attribute terlalu besar, maksimal adalah 2MB.',
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'nama buket',
|
||||
'ukuran' => 'ukuran buket',
|
||||
'kategori' => 'kategori buket',
|
||||
'harga' => 'harga',
|
||||
'request_khusus' => 'request khusus',
|
||||
'deskripsi' => 'deskripsi produk',
|
||||
'foto' => 'foto produk',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Trik untuk Modal: Mengembalikan error ke modal yang tepat
|
||||
*/
|
||||
protected function failedValidation(Validator $validator)
|
||||
{
|
||||
$redirect = redirect()->back()->withErrors($validator)->withInput();
|
||||
|
||||
if ($this->isMethod('post')) {
|
||||
$redirect->with('error_modal', 'create');
|
||||
} else {
|
||||
// Mengambil ID dari parameter route untuk modal edit
|
||||
$redirect->with('error_id', $this->route('buket'));
|
||||
}
|
||||
|
||||
throw new HttpResponseException($redirect);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||
|
||||
class FotoRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true; // Izinkan request
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'harga' => 'required|numeric|min:0',
|
||||
'durasi' => 'required|integer|min:0',
|
||||
'deskripsi' => 'required|string|min:10',
|
||||
// Foto wajib saat Tambah (POST), opsional saat Edit (PUT)
|
||||
'foto' => $this->isMethod('post')
|
||||
? 'required|image|mimes:jpeg,png,jpg|max:2048'
|
||||
: 'nullable|image|mimes:jpeg,png,jpg|max:2048',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'integer' => 'Input :attribute harus berupa angka valid.',
|
||||
'numeric' => ':attribute harus berupa angka.',
|
||||
'min' => ':attribute minimal harus :min karakter/nilai.',
|
||||
'max' => ':attribute melebihi batas, maksimal :max karakter/KB.',
|
||||
'image' => ':attribute harus berupa file gambar (foto).',
|
||||
'mimes' => 'Format :attribute tidak didukung (jpeg, png, jpg).',
|
||||
'foto.max' => 'Ukuran :attribute maksimal 2MB.',
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'nama paket',
|
||||
'harga' => 'harga paket',
|
||||
'durasi' => 'durasi paket',
|
||||
'foto' => 'foto paket',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Penanganan khusus untuk Modal di Admin
|
||||
*/
|
||||
protected function failedValidation(Validator $validator)
|
||||
{
|
||||
$redirect = redirect()->back()->withErrors($validator)->withInput();
|
||||
|
||||
if ($this->isMethod('post')) {
|
||||
// Trigger modal tambah foto
|
||||
$redirect->with('error_modal', 'createFoto');
|
||||
} else {
|
||||
// Trigger modal edit berdasarkan ID paket
|
||||
$redirect->with('error_id_foto', $this->route('foto'));
|
||||
}
|
||||
|
||||
throw new HttpResponseException($redirect);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ProfilRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class BookingFotoRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'id_paket' => 'required|exists:paket_fotos,id_paket',
|
||||
'tgl_booking' => 'required|date|after_or_equal:today',
|
||||
'jam_mulai' => 'required',
|
||||
'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',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'required' => 'Kolom :attribute wajib diisi.',
|
||||
'image' => ':attribute harus berupa file gambar.',
|
||||
'max' => 'Ukuran :attribute maksimal adalah 2MB.',
|
||||
'string' => 'Input :attribute harus berupa teks valid.',
|
||||
'min' => ':attribute terlalu pendek, minimal :min 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.',
|
||||
'mimes' => 'Format :attribute harus jpeg, png, atau jpg.',
|
||||
'bukti_bayar.max' => 'Ukuran :attribute maksimal adalah 2MB.',
|
||||
|
||||
];
|
||||
}
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'nama pemesan',
|
||||
'no_wa' => 'nomor WhatsApp',
|
||||
'bukti_bayar' => 'bukti pembayaran',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\User;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PesanBuketRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true; // Pastikan ini true agar request diizinkan
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'id_buket' => 'required|exists:bukets,id_buket', // Tambahan validasi keamanan
|
||||
'nama' => 'required|string|min:3|max:100',
|
||||
'no_wa' => 'required|numeric|digits_between:10,15',
|
||||
'tgl_ambil' => 'required|date|after_or_equal:today',
|
||||
'waktu_ambil' => [
|
||||
'required',
|
||||
'date_format:H:i',
|
||||
'after_or_equal:09:00',
|
||||
'before_or_equal:21:00',
|
||||
],
|
||||
'bukti_bayar' => 'required|image|mimes:jpeg,png,jpg|max:2048',
|
||||
'request_khusus' => 'nullable|string|max:255',
|
||||
'ucapan' => 'nullable|string|max:500',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'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.',
|
||||
'bukti_bayar.max' => 'Ukuran :attribute maksimal adalah 2MB.',
|
||||
'waktu_ambil.after_or_equal' => 'Jam operasional kami mulai pukul 09:00.',
|
||||
'waktu_ambil.before_or_equal' => 'Jam operasional kami berakhir pukul 21:00.',
|
||||
];
|
||||
}
|
||||
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'nama' => 'nama pemesan',
|
||||
'no_wa' => 'nomor WhatsApp',
|
||||
'tgl_ambil' => 'tanggal pengambilan',
|
||||
'waktu_ambil' => 'waktu pengambilan',
|
||||
'bukti_bayar' => 'bukti pembayaran',
|
||||
'ucapan' => 'kartu ucapan',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\{TransaksiBuket, BookingFoto};
|
||||
use Carbon\Carbon;
|
||||
|
||||
class DashboardService
|
||||
{
|
||||
public static function getStats()
|
||||
{
|
||||
$now = Carbon::now();
|
||||
$prev = Carbon::now()->subMonth();
|
||||
|
||||
$curr = self::calculate($now);
|
||||
$past = self::calculate($prev);
|
||||
|
||||
return [
|
||||
'pendapatan' => $curr['income'],
|
||||
'pendapatan_grow' => self::growth($curr['income'], $past['income']),
|
||||
'masuk_count' => $curr['total'],
|
||||
'masuk_grow' => self::growth($curr['total'], $past['total']),
|
||||
'selesai_count' => $curr['done'],
|
||||
'selesai_grow' => self::growth($curr['done'], $past['done']),
|
||||
'batal_count' => $curr['cancel'],
|
||||
'batal_grow' => self::growth($curr['cancel'], $past['cancel']),
|
||||
];
|
||||
}
|
||||
|
||||
private static function calculate($date)
|
||||
{
|
||||
$m = $date->month;
|
||||
$y = $date->year;
|
||||
|
||||
// Gabungan query Buket & Foto
|
||||
return [
|
||||
'income' => TransaksiBuket::whereMonth('created_at', $m)->whereYear('created_at', $y)->where('status_transaksi', 'diterima')->sum('total_bayar') +
|
||||
BookingFoto::whereMonth('created_at', $m)->whereYear('created_at', $y)->where('status_booking', 'diterima')->sum('total_bayar'),
|
||||
'total' => TransaksiBuket::whereMonth('created_at', $m)->whereYear('created_at', $y)->count() +
|
||||
BookingFoto::whereMonth('created_at', $m)->whereYear('created_at', $y)->count(),
|
||||
'done' => TransaksiBuket::whereMonth('created_at', $m)->whereYear('created_at', $y)->where('status_transaksi', 'selesai')->count() +
|
||||
BookingFoto::whereMonth('created_at', $m)->whereYear('created_at', $y)->where('status_booking', 'selesai')->count(),
|
||||
'cancel' => TransaksiBuket::whereMonth('created_at', $m)->whereYear('created_at', $y)->where('status_transaksi', 'ditolak')->count() +
|
||||
BookingFoto::whereMonth('created_at', $m)->whereYear('created_at', $y)->where('status_booking', 'ditolak')->count(),
|
||||
];
|
||||
}
|
||||
|
||||
private static function growth($current, $previous)
|
||||
{
|
||||
if ($previous <= 0) return $current > 0 ? 100 : 0;
|
||||
return round((($current - $previous) / $previous) * 100, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
class WhatsAppService
|
||||
{
|
||||
public static function getBuketNotification($pesanan, $status)
|
||||
{
|
||||
if ($status === 'selesai') return null;
|
||||
|
||||
$nama = $pesanan->pelanggan->nama;
|
||||
$produk = $pesanan->buket->nama;
|
||||
$invoice = $pesanan->no_invoice;
|
||||
$total = number_format($pesanan->total_bayar, 0, ',', '.');
|
||||
$req = $pesanan->request ?? '-';
|
||||
$ucapan = $pesanan->ucapan ?? '-';
|
||||
$tgl_obj = Carbon::parse($pesanan->tgl_ambil)->locale('id');
|
||||
$tanggal = $tgl_obj->translatedFormat('l, d F Y');
|
||||
$waktu = $tgl_obj->format('H:i');
|
||||
$msg = self::getBuketTemplate($status, $nama, $invoice, $produk, $total, $req, $ucapan, $tanggal, $waktu);
|
||||
$no_wa = self::formatPhoneNumber($pesanan->pelanggan->no_wa);
|
||||
|
||||
return "https://wa.me/{$no_wa}?text=" . urlencode($msg);
|
||||
}
|
||||
|
||||
private static function getBuketTemplate($status, $nama, $invoice, $produk, $total, $req, $ucapan, $tanggal, $waktu)
|
||||
{
|
||||
if ($status === 'diterima') {
|
||||
return "Halo Kak *{$nama}*,\n\n" .
|
||||
"Pesanan Anda dengan Nomor Invoice: *{$invoice}* telah kami *TERIMA* dan masuk dalam daftar proses pengerjaan.\n\n" .
|
||||
"*Rincian Pesanan:*\n" .
|
||||
"- *Produk:* {$produk}\n" .
|
||||
"- *Total Bayar:* Rp {$total}\n" .
|
||||
"- *Request:* {$req}\n" .
|
||||
"- *Ucapan:* \"{$ucapan}\"\n" .
|
||||
"- *Waktu Pengambilan:* {$tanggal} pukul {$waktu} WIB\n\n" .
|
||||
"Mohon simpan rincian ini dan ditunggu info selanjutnya ya Kak. Terima kasih!";
|
||||
}
|
||||
|
||||
return "Halo Kak *{$nama}*,\n\n" .
|
||||
"Mengenai pesanan Anda dengan Nomor Invoice: *{$invoice}* terpaksa kami *TOLAK* dikarenakan:\n\n" .
|
||||
"*[TULIS ALASAN DI SINI]*\n\n" .
|
||||
"*Rincian Pesanan:*\n" .
|
||||
"- *Produk:* {$produk}\n" .
|
||||
"- *Total Bayar:* Rp {$total}\n" .
|
||||
"- *Waktu Pengambilan:* {$tanggal}\n\n" .
|
||||
"Admin kami akan segera menghubungi Kakak terkait proses pengembalian dana. Mohon maaf atas ketidaknyamanannya.";
|
||||
}
|
||||
|
||||
private static function formatPhoneNumber($number)
|
||||
{
|
||||
$number = preg_replace('/[^0-9]/', '', $number);
|
||||
if (str_starts_with($number, '0')) {
|
||||
return '62' . substr($number, 1);
|
||||
} elseif (str_starts_with($number, '8')) {
|
||||
return '62' . $number;
|
||||
}
|
||||
return $number;
|
||||
}
|
||||
public static function getFotoNotification($pesanan, $status)
|
||||
{
|
||||
if ($status === 'selesai') return null;
|
||||
|
||||
$nama = $pesanan->pelanggan->nama;
|
||||
$paket = $pesanan->paketFoto->nama;
|
||||
$invoice = $pesanan->no_invoice;
|
||||
$total = number_format($pesanan->total_bayar, 0, ',', '.');
|
||||
|
||||
// Format Tanggal & Jam
|
||||
$tgl_obj = Carbon::parse($pesanan->tgl_booking)->locale('id');
|
||||
$tanggal = $tgl_obj->translatedFormat('l, d F Y');
|
||||
$jam = Carbon::parse($pesanan->jam_mulai)->format('H:i') . ' - ' . Carbon::parse($pesanan->jam_selesai)->format('H:i');
|
||||
|
||||
// Logika List Additional
|
||||
$list_additional = $pesanan->detailAdditional->count() > 0
|
||||
? $pesanan->detailAdditional->map(fn($item) => "- " . $item->additional->nama . " (x" . $item->qty . ")")->implode("\n")
|
||||
: "- Tidak ada additional";
|
||||
|
||||
// Pilih Template
|
||||
$msg = self::getFotoTemplate($status, $nama, $invoice, $paket, $list_additional, $total, $tanggal, $jam);
|
||||
|
||||
// Format Nomor HP
|
||||
$no_wa = self::formatPhoneNumber($pesanan->pelanggan->no_wa);
|
||||
|
||||
return "https://wa.me/{$no_wa}?text=" . urlencode($msg);
|
||||
}
|
||||
|
||||
private static function getFotoTemplate($status, $nama, $invoice, $paket, $additional, $total, $tanggal, $jam)
|
||||
{
|
||||
if ($status === 'diterima') {
|
||||
return "Halo Kak *{$nama}*,\n\n" .
|
||||
"Booking foto Anda dengan Invoice: *{$invoice}* telah kami *TERIMA*. \n\n" .
|
||||
"*Rincian Booking:*\n" .
|
||||
"- *Paket:* {$paket}\n" .
|
||||
"*Additional:*\n{$additional}\n" .
|
||||
"- *Total Bayar:* Rp {$total}\n\n" .
|
||||
"*Jadwal Sesi Foto:*\n" .
|
||||
" Tanggal: {$tanggal}\n" .
|
||||
" Jam: {$jam} WIB\n\n" .
|
||||
"Mohon datang 15 menit sebelum jadwal dimulai ya Kak. Sampai jumpa di studio!";
|
||||
}
|
||||
|
||||
return "Halo Kak *{$nama}*,\n\n" .
|
||||
"Mohon maaf, booking foto Anda dengan Invoice *{$invoice}* terpaksa kami *TOLAK* dikarenakan:\n\n" .
|
||||
"*[TULIS ALASAN DI SINI]*\n\n" .
|
||||
"*Rincian Booking:*\n" .
|
||||
"- *Paket:* {$paket}\n" .
|
||||
"*Additional:*\n{$additional}\n" .
|
||||
"- *Total Bayar:* Rp {$total}\n" .
|
||||
"*Jadwal Sesi Foto:*\n" .
|
||||
" Tanggal: {$tanggal}\n" .
|
||||
" Jam: {$jam} WIB\n\n" .
|
||||
"Admin kami akan segera menghubungi Kakak untuk info pengembalian dana atau penjadwalan ulang. Terima kasih.";
|
||||
}
|
||||
|
||||
public static function getPesananBuketMessage($transaksi, $pelanggan)
|
||||
{
|
||||
$total = number_format($transaksi->buket->harga, 0, ',', '.');
|
||||
|
||||
$pesan = "Halo Admin! Saya sudah melakukan pembayaran untuk invoice *{$transaksi->no_invoice}*:\n\n" .
|
||||
"*Data Pemesan:*\n" .
|
||||
"- Nama: {$pelanggan->nama}\n" .
|
||||
"- WA: {$pelanggan->no_wa}\n\n" .
|
||||
"*Detail Produk:*\n" .
|
||||
"- Produk: {$transaksi->buket->nama}\n" .
|
||||
"- Total: Rp {$total}\n\n" .
|
||||
"Mohon segera diproses, ya! Terima kasih.";
|
||||
|
||||
return "https://wa.me/6282337687878?text=" . urlencode($pesan);
|
||||
}
|
||||
public static function getBookingFotoMessage($booking, $paket, $addons, $grandTotal, $jamSelesai)
|
||||
{
|
||||
$txtAddons = "";
|
||||
if (count($addons) > 0) {
|
||||
$txtAddons = "\n*Additional:*";
|
||||
foreach ($addons as $item) {
|
||||
$txtAddons .= "\n- " . $item['nama'] . " (" . $item['qty'] . "x)";
|
||||
}
|
||||
}
|
||||
|
||||
$pesan = "Halo Admin Flo.do! Saya sudah melakukan pembayaran untuk invoice *{$booking->no_invoice}*:\n\n" .
|
||||
"*Data Pemesan:*\n- Nama: {$booking->pelanggan->nama}\n- WA: {$booking->pelanggan->no_wa}\n\n" .
|
||||
"*Detail Booking:*\n- Paket: {$paket->nama}{$txtAddons}\n" .
|
||||
"- Tanggal: " . \Carbon\Carbon::parse($booking->tgl_booking)->translatedFormat('l, d F Y') . "\n" .
|
||||
"- Jam: {$booking->jam_mulai} - {$jamSelesai} WIB\n" .
|
||||
"- Total: Rp " . number_format($grandTotal, 0, ',', '.') . "\n\n" .
|
||||
"Mohon segera diproses, ya! Terima kasih.";
|
||||
|
||||
return "https://wa.me/6282337687878?text=" . urlencode($pesan);
|
||||
}
|
||||
}
|
||||
|
|
@ -20,115 +20,39 @@
|
|||
@endif
|
||||
<section class="row gy-3">
|
||||
<div class="col-12">
|
||||
<div class="row gx-3">
|
||||
@if (Auth::user()->role === 'pemilik')
|
||||
<div class="col-6 col-lg-3 col-md-6">
|
||||
<div class="card mb-0">
|
||||
<div class="card-body px-3 py-4">
|
||||
<div class="stat-header">
|
||||
<h6 class="stat-label">Pendapatan Bulan Ini</h6>
|
||||
<i class="bi bi-info-circle menu-dots" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="Total pendapatan dari pesanan buket dan foto dalam bulan ini"></i>
|
||||
</div>
|
||||
<div class="stat-body">
|
||||
<div class="stat-icon">
|
||||
<i class="bi bi-bank"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="stat-count">Rp {{ number_format($stat['pendapatan'], 0, ',', '.') }}</h6>
|
||||
<span
|
||||
class="stat-percent {{ $stat['pendapatan_grow'] >= 0 ? 'text-success' : 'text-danger' }} fw-bold">
|
||||
<i
|
||||
class="bi {{ $stat['pendapatan_grow'] >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>
|
||||
{{ abs($stat['pendapatan_grow']) }}%
|
||||
</span>
|
||||
<small class="stat-month">dari bulan lalu</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 col-lg-3 col-md-6">
|
||||
<div class="card mb-0">
|
||||
<div class="card-body px-3 py-4">
|
||||
<div class="stat-header">
|
||||
<h6 class="stat-label">Total Pesanan Masuk</h6>
|
||||
<i class="bi bi-info-circle menu-dots" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="Total pesanan buket dan foto yang masuk dalam bulan ini"></i>
|
||||
</div>
|
||||
<div class="stat-body">
|
||||
<div class="stat-icon">
|
||||
<i class="bi bi-cart-fill"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="stat-count">{{ $stat['masuk_count'] }} Pesanan</h6>
|
||||
<span
|
||||
class="stat-percent {{ $stat['masuk_grow'] >= 0 ? 'text-success' : 'text-danger' }} fw-bold">
|
||||
<i
|
||||
class="bi {{ $stat['masuk_grow'] >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>
|
||||
{{ abs($stat['masuk_grow']) }}%
|
||||
</span>
|
||||
<small class="stat-month">dari bulan lalu</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-lg-3 col-md-6">
|
||||
<div class="card mb-0">
|
||||
<div class="card-body px-3 py-4">
|
||||
<div class="stat-header">
|
||||
<h6 class="stat-label">Pesanan Selesai</h6>
|
||||
<i class="bi bi-info-circle menu-dots" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="Total pesanan buket dan foto yang selesai dalam bulan ini"></i>
|
||||
</div>
|
||||
<div class="stat-body">
|
||||
<div class="stat-icon">
|
||||
<i class="bi bi-cart-check-fill"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="stat-count">{{ $stat['selesai_count'] }} Pesanan</h6>
|
||||
<span
|
||||
class="stat-percent {{ $stat['selesai_grow'] >= 0 ? 'text-success' : 'text-danger' }} fw-bold">
|
||||
<i
|
||||
class="bi {{ $stat['selesai_grow'] >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>{{ abs($stat['selesai_grow']) }}%
|
||||
</span>
|
||||
<small class="stat-month">dari bulan lalu</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-lg-3 col-md-6">
|
||||
<div class="card mb-0">
|
||||
<div class="card-body px-3 py-4">
|
||||
<div class="stat-header">
|
||||
<h6 class="stat-label">Pesanan Ditolak</h6>
|
||||
<i class="bi bi-info-circle menu-dots" data-bs-toggle="tooltip" data-bs-placement="top"
|
||||
title="Total pesanan buket dan foto yang ditolak dalam bulan ini"></i>
|
||||
</div>
|
||||
<div class="stat-body">
|
||||
<div class="stat-icon">
|
||||
<i class="bi bi-cart-x-fill"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="stat-count">{{ $stat['batal_count'] }} Pesanan</h6>
|
||||
<span
|
||||
class="stat-percent {{ $stat['batal_grow'] <= 0 ? 'text-success' : 'text-danger' }} fw-bold">
|
||||
<i
|
||||
class="bi {{ $stat['batal_grow'] >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>
|
||||
{{ abs($stat['batal_grow']) }}%
|
||||
</span>
|
||||
<small class="stat-month">dari bulan lalu</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (Auth::user()->role === 'pemilik')
|
||||
<div class="row">
|
||||
@include('admin.components._stat_card', [
|
||||
'label' => 'Pendapatan',
|
||||
'icon' => 'bi-bank',
|
||||
'value' => 'Rp ' . number_format($stat['pendapatan'], 0, ',', '.'),
|
||||
'grow' => $stat['pendapatan_grow'],
|
||||
'tooltip' => 'Total pendapatan dari pesanan buket dan foto bulan ini',
|
||||
])
|
||||
@include('admin.components._stat_card', [
|
||||
'label' => 'Pesanan Masuk',
|
||||
'icon' => 'bi-cart-fill',
|
||||
'value' => $stat['masuk_count'] . ' Pesanan',
|
||||
'grow' => $stat['masuk_grow'],
|
||||
'tooltip' => 'Total pesanan buket dan foto yang masuk bulan ini',
|
||||
])
|
||||
@include('admin.components._stat_card', [
|
||||
'label' => 'Pesanan Selesai',
|
||||
'icon' => 'bi-cart-check-fill',
|
||||
'value' => $stat['selesai_count'] . ' Pesanan',
|
||||
'grow' => $stat['selesai_grow'],
|
||||
'tooltip' => 'Total pesanan buket dan foto yang selesai bulan ini',
|
||||
])
|
||||
@include('admin.components._stat_card', [
|
||||
'label' => 'Pesanan Ditolak',
|
||||
'icon' => 'bi-cart-x-fill',
|
||||
'value' => $stat['batal_count'] . ' Pesanan',
|
||||
'grow' => $stat['batal_grow'],
|
||||
'tooltip' => 'Total pesanan buket dan foto yang ditolak bulan ini',
|
||||
'is_negative_metric' => true,
|
||||
])
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row gx-3">
|
||||
|
|
@ -201,8 +125,7 @@ class="bi {{ $stat['batal_grow'] >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>
|
|||
<td>{{ $p->paketFoto->nama }}</td>
|
||||
<td class="col-auto">
|
||||
<a href="#" class="btn icon btn-primary btn-action"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#foto{{ $p->id_booking }}">
|
||||
data-bs-toggle="modal" data-bs-target="#foto{{ $p->id_booking }}">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
|
|
@ -229,8 +152,7 @@ class="bi {{ $stat['batal_grow'] >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>
|
|||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="nav nav-pills nav-fill mb-4" id="v-pills-tab" role="tablist"
|
||||
aria-orientation="horizontal">
|
||||
<div class="nav nav-pills nav-fill mb-4" id="v-pills-tab" role="tablist" aria-orientation="horizontal">
|
||||
<a class="nav-link active" id="v-pills-home-tab" data-bs-toggle="pill" href="#v-pills-home"
|
||||
role="tab" aria-controls="v-pills-home" aria-selected="true">
|
||||
Buket
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
<div class="col-6 col-lg-3 col-md-6">
|
||||
<div class="card mb-0">
|
||||
<div class="card-body px-3 py-4">
|
||||
<div class="stat-header">
|
||||
<h6 class="stat-label">{{ $label }}</h6>
|
||||
<i class="bi bi-info-circle menu-dots" data-bs-toggle="tooltip" title="{{ $tooltip }}"></i>
|
||||
</div>
|
||||
<div class="stat-body">
|
||||
<div class="stat-icon">
|
||||
<i class="bi {{ $icon }}"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="stat-count">{{ $value }}</h6>
|
||||
|
||||
@php
|
||||
// Logika untuk menentukan warna
|
||||
$colorClass = 'text-success'; // Default hijau (bagus)
|
||||
|
||||
// Cek apakah ini metrik negatif (seperti Pesanan Ditolak)
|
||||
if ($is_negative_metric ?? false) {
|
||||
// Jika naik (> 0), berarti BURUK -> Merah
|
||||
if ($grow > 0) {
|
||||
$colorClass = 'text-danger';
|
||||
}
|
||||
} else {
|
||||
// Untuk metrik biasa (Pendapatan, dll)
|
||||
// Jika turun (< 0), berarti BURUK -> Merah
|
||||
if ($grow < 0) {
|
||||
$colorClass = 'text-danger';
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<span class="stat-percent {{ $colorClass }} fw-bold">
|
||||
{{-- Ikon panah tetap menunjukkan arah matematis (naik/turun) --}}
|
||||
<i class="bi {{ $grow >= 0 ? 'bi-arrow-up' : 'bi-arrow-down' }}"></i>
|
||||
{{ abs($grow) }}%
|
||||
</span>
|
||||
<small class="stat-month">dari bulan lalu</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Loading…
Reference in New Issue