feat: introduce LogHelper for centralized exception handling and implement consistent error logging across admin controllers

This commit is contained in:
Yoga Pangestu 2026-04-22 21:19:33 +07:00
parent 635dcf712b
commit 53a7b23cbe
8 changed files with 446 additions and 256 deletions

View File

@ -4,10 +4,10 @@
use App\Models\Payroll; use App\Models\Payroll;
use App\Models\User; use App\Models\User;
use App\Support\LogHelper;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Throwable; use Throwable;
class GeneratePayrollCommand extends Command class GeneratePayrollCommand extends Command
@ -60,8 +60,8 @@ public function handle()
} }
}); });
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error('Payroll generate failed', [ LogHelper::logException($e, 'Payroll generate failed', [
'error' => $e->getMessage(), 'period' => $periodMonth,
]); ]);
$this->info('Gagal generate data penggajian, silakan hubungi pengembang.'); $this->info('Gagal generate data penggajian, silakan hubungi pengembang.');

View File

@ -5,12 +5,12 @@
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Finance\SalaryAdjustmentRequest; use App\Http\Requests\Admin\Finance\SalaryAdjustmentRequest;
use App\Models\Payroll; use App\Models\Payroll;
use App\Support\LogHelper;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia; use Inertia\Inertia;
use Inertia\Response; use Inertia\Response;
use Throwable; use Throwable;
@ -75,9 +75,8 @@ public function update(SalaryAdjustmentRequest $request, Payroll $payroll): Redi
]); ]);
}); });
} catch (Throwable $e) { } catch (Throwable $e) {
Log::error('Payroll update failed', [ LogHelper::logException($e, 'Payroll update failed', [
'payroll_id' => $payroll->id, 'payroll_id' => $payroll->id,
'error' => $e->getMessage(),
]); ]);
return back()->withInput()->with('error', 'Gagal memperbarui data. Silakan coba lagi.'); return back()->withInput()->with('error', 'Gagal memperbarui data. Silakan coba lagi.');

View File

@ -11,6 +11,7 @@
use App\Models\Order; use App\Models\Order;
use App\Models\OrderItem; use App\Models\OrderItem;
use App\Models\Product; use App\Models\Product;
use App\Support\LogHelper;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -46,48 +47,60 @@ public function store(OrderRequest $request): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($validated) { try {
$totalItemsPrice = collect($validated['items'])->sum('total'); DB::transaction(function () use ($validated) {
$hpp = collect($validated['items'])->sum(function ($item) { $totalItemsPrice = collect($validated['items'])->sum('total');
$product = Product::find($item['product_id']); $hpp = collect($validated['items'])->sum(function ($item) {
$purchasePrice = $product->prices()->where('price_type', PriceType::PURCHASE)->first()?->price ?? 0; $product = Product::find($item['product_id']);
$purchasePrice = $product->prices()->where('price_type', PriceType::PURCHASE)->first()?->price ?? 0;
return $purchasePrice * $item['qty']; return $purchasePrice * $item['qty'];
}); });
$order = Order::create([ $order = Order::create([
'user_id' => auth()->id(),
'invoice_number' => $validated['invoice_number'] ?? 'INV-'.now()->format('YmdHis').'-'.strtoupper(fake()->bothify('??##')),
'customer_name' => $validated['customer_name'],
'hpp' => $hpp,
'discount' => $validated['discount'],
'payment' => $validated['payment'],
'total' => $totalItemsPrice - $validated['discount'],
'payment_method' => $validated['payment_method'],
'order_status' => $validated['order_status'],
'order_channel' => $validated['order_channel'],
]);
foreach ($validated['items'] as $item) {
OrderItem::create([
'user_id' => auth()->id(), 'user_id' => auth()->id(),
'order_id' => $order->id, 'invoice_number' => $validated['invoice_number'] ?? 'INV-'.now()->format('YmdHis').'-'.strtoupper(fake()->bothify('??##')),
'product_id' => $item['product_id'], 'customer_name' => $validated['customer_name'],
'price' => $item['price'], 'hpp' => $hpp,
'qty' => $item['qty'], 'discount' => $validated['discount'],
'total' => $item['total'], 'payment' => $validated['payment'],
'price_type' => $item['price_type'], 'total' => $totalItemsPrice - $validated['discount'],
'payment_method' => $validated['payment_method'],
'order_status' => $validated['order_status'],
'order_channel' => $validated['order_channel'],
]); ]);
Product::find($item['product_id'])->decrement('stock', $item['qty']); foreach ($validated['items'] as $item) {
} OrderItem::create([
'user_id' => auth()->id(),
'order_id' => $order->id,
'product_id' => $item['product_id'],
'price' => $item['price'],
'qty' => $item['qty'],
'total' => $item['total'],
'price_type' => $item['price_type'],
]);
OrderItem::where('user_id', auth()->id()) Product::find($item['product_id'])->decrement('stock', $item['qty']);
->whereNull('order_id') }
->delete();
});
return redirect()->route('order.index')->with('success', 'Pesanan berhasil disimpan'); OrderItem::where('user_id', auth()->id())
->whereNull('order_id')
->delete();
});
return redirect()->route('order.index')->with('success', 'Pesanan berhasil disimpan');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to store order', [
'customer_name' => $validated['customer_name'],
'items_count' => count($validated['items'] ?? []),
]);
return redirect()
->back()
->withInput()
->with('error', 'Terjadi kesalahan saat menyimpan pesanan');
}
} }
public function edit(Order $order): Response public function edit(Order $order): Response
@ -108,76 +121,104 @@ public function update(OrderRequest $request, Order $order): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($validated, $order) { try {
$totalItemsPrice = collect($validated['items'])->sum(fn ($item) => $item['qty'] * $item['price']); DB::transaction(function () use ($validated, $order) {
$hpp = collect($validated['items'])->sum(function ($item) { $totalItemsPrice = collect($validated['items'])->sum(fn ($item) => $item['qty'] * $item['price']);
$product = Product::find($item['product_id']); $hpp = collect($validated['items'])->sum(function ($item) {
$purchasePrice = $product->prices()->where('price_type', PriceType::PURCHASE)->first()?->price ?? 0; $product = Product::find($item['product_id']);
$purchasePrice = $product->prices()->where('price_type', PriceType::PURCHASE)->first()?->price ?? 0;
return $purchasePrice * $item['qty']; return $purchasePrice * $item['qty'];
}); });
// Restore stock for old items // Restore stock for old items
foreach ($order->items as $item) { foreach ($order->items as $item) {
$item->product->increment('stock', $item->qty); $item->product->increment('stock', $item->qty);
} }
$order->items()->delete(); $order->items()->delete();
$order->update([ $order->update([
'customer_name' => $validated['customer_name'], 'customer_name' => $validated['customer_name'],
'hpp' => $hpp, 'hpp' => $hpp,
'discount' => $validated['discount'], 'discount' => $validated['discount'],
'payment' => $validated['payment'], 'payment' => $validated['payment'],
'total' => $totalItemsPrice - $validated['discount'], 'total' => $totalItemsPrice - $validated['discount'],
'payment_method' => $validated['payment_method'], 'payment_method' => $validated['payment_method'],
'order_status' => $validated['order_status'], 'order_status' => $validated['order_status'],
'order_channel' => $validated['order_channel'], 'order_channel' => $validated['order_channel'],
]);
foreach ($validated['items'] as $item) {
$order->items()->create([
'user_id' => auth()->id(),
'product_id' => $item['product_id'],
'price' => $item['price'],
'qty' => $item['qty'],
'total' => $item['total'],
'price_type' => $item['price_type'],
]); ]);
Product::find($item['product_id'])->decrement('stock', $item['qty']); foreach ($validated['items'] as $item) {
} $order->items()->create([
}); 'user_id' => auth()->id(),
'product_id' => $item['product_id'],
'price' => $item['price'],
'qty' => $item['qty'],
'total' => $item['total'],
'price_type' => $item['price_type'],
]);
return redirect()->route('order.index')->with('success', 'Pesanan berhasil diperbarui'); Product::find($item['product_id'])->decrement('stock', $item['qty']);
}
});
return redirect()->route('order.index')->with('success', 'Pesanan berhasil diperbarui');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to update order', [
'order_id' => $order->id,
'customer_name' => $validated['customer_name'],
]);
return redirect()
->back()
->withInput()
->with('error', 'Terjadi kesalahan saat memperbarui pesanan');
}
} }
public function destroy(Order $order): RedirectResponse public function destroy(Order $order): RedirectResponse
{ {
DB::transaction(function () use ($order) { try {
foreach ($order->items as $item) { DB::transaction(function () use ($order) {
$item->product->increment('stock', $item->qty); foreach ($order->items as $item) {
} $item->product->increment('stock', $item->qty);
$order->delete(); }
}); $order->delete();
});
return redirect()->back()->with('success', 'Pesanan berhasil dihapus'); return redirect()->back()->with('success', 'Pesanan berhasil dihapus');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to delete order', [
'order_id' => $order->id,
]);
return redirect()->back()->with('error', 'Terjadi kesalahan saat menghapus pesanan');
}
} }
public function bulkDestroy(Request $request): RedirectResponse public function bulkDestroy(Request $request): RedirectResponse
{ {
$ids = $request->input('ids'); $ids = $request->input('ids');
DB::transaction(function () use ($ids) { try {
$orders = Order::with('items')->whereIn('id', $ids)->get(); DB::transaction(function () use ($ids) {
foreach ($orders as $order) { $orders = Order::with('items')->whereIn('id', $ids)->get();
foreach ($order->items as $item) { foreach ($orders as $order) {
$item->product->increment('stock', $item->qty); foreach ($order->items as $item) {
$item->product->increment('stock', $item->qty);
}
$order->delete();
} }
$order->delete(); });
}
});
return redirect()->back()->with('success', 'Pesanan terpilih berhasil dihapus'); return redirect()->back()->with('success', 'Pesanan terpilih berhasil dihapus');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to bulk delete orders', [
'ids' => $ids,
]);
return redirect()->back()->with('error', 'Terjadi kesalahan saat menghapus beberapa pesanan');
}
} }
} }

View File

@ -7,6 +7,7 @@
use App\Models\Product; use App\Models\Product;
use App\Models\Purchase; use App\Models\Purchase;
use App\Models\PurchaseItem; use App\Models\PurchaseItem;
use App\Support\LogHelper;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -38,34 +39,46 @@ public function store(PurchaseRequest $request): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($validated) { try {
$total = collect($validated['items'])->sum(fn ($item) => $item['quantity'] * $item['unit_price']); DB::transaction(function () use ($validated) {
$total = collect($validated['items'])->sum(fn ($item) => $item['quantity'] * $item['unit_price']);
$purchase = Purchase::create([ $purchase = Purchase::create([
'purchase_date' => $validated['purchase_date'], 'purchase_date' => $validated['purchase_date'],
'note' => $validated['note'], 'note' => $validated['note'],
'total' => $total, 'total' => $total,
]);
foreach ($validated['items'] as $item) {
PurchaseItem::create([
'user_id' => auth()->id(),
'purchase_id' => $purchase->id,
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'unit_price' => $item['unit_price'],
'total_price' => $item['quantity'] * $item['unit_price'],
]); ]);
Product::find($item['product_id'])->increment('stock', $item['quantity']); foreach ($validated['items'] as $item) {
} PurchaseItem::create([
'user_id' => auth()->id(),
'purchase_id' => $purchase->id,
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'unit_price' => $item['unit_price'],
'total_price' => $item['quantity'] * $item['unit_price'],
]);
PurchaseItem::where('user_id', auth()->id()) Product::find($item['product_id'])->increment('stock', $item['quantity']);
->whereNull('purchase_id') }
->delete();
});
return redirect()->route('purchase.index')->with('success', 'Data berhasil disimpan'); PurchaseItem::where('user_id', auth()->id())
->whereNull('purchase_id')
->delete();
});
return redirect()->route('purchase.index')->with('success', 'Data berhasil disimpan');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to store purchase', [
'purchase_date' => $validated['purchase_date'] ?? null,
'items_count' => count($validated['items'] ?? []),
]);
return redirect()
->back()
->withInput()
->with('error', 'Terjadi kesalahan saat menyimpan pembelian');
}
} }
public function edit(Purchase $purchase): Response public function edit(Purchase $purchase): Response
@ -82,63 +95,91 @@ public function update(PurchaseRequest $request, Purchase $purchase): RedirectRe
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($validated, $purchase) { try {
$total = collect($validated['items'])->sum(fn ($item) => $item['quantity'] * $item['unit_price']); DB::transaction(function () use ($validated, $purchase) {
$total = collect($validated['items'])->sum(fn ($item) => $item['quantity'] * $item['unit_price']);
$purchase->update([ $purchase->update([
'purchase_date' => $validated['purchase_date'], 'purchase_date' => $validated['purchase_date'],
'note' => $validated['note'], 'note' => $validated['note'],
'total' => $total, 'total' => $total,
]);
foreach ($purchase->items as $item) {
$item->product->decrement('stock', $item->quantity);
}
$purchase->items()->delete();
foreach ($validated['items'] as $item) {
$purchase->items()->create([
'user_id' => auth()->id(),
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'unit_price' => $item['unit_price'],
'total_price' => $item['quantity'] * $item['unit_price'],
]); ]);
Product::find($item['product_id'])->increment('stock', $item['quantity']); foreach ($purchase->items as $item) {
} $item->product->decrement('stock', $item->quantity);
}); }
return redirect()->route('purchase.index')->with('success', 'Data berhasil diperbarui'); $purchase->items()->delete();
foreach ($validated['items'] as $item) {
$purchase->items()->create([
'user_id' => auth()->id(),
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'unit_price' => $item['unit_price'],
'total_price' => $item['quantity'] * $item['unit_price'],
]);
Product::find($item['product_id'])->increment('stock', $item['quantity']);
}
});
return redirect()->route('purchase.index')->with('success', 'Data berhasil diperbarui');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to update purchase', [
'purchase_id' => $purchase->id,
'items_count' => count($validated['items'] ?? []),
]);
return redirect()
->back()
->withInput()
->with('error', 'Terjadi kesalahan saat memperbarui pembelian');
}
} }
public function destroy(Purchase $purchase): RedirectResponse public function destroy(Purchase $purchase): RedirectResponse
{ {
DB::transaction(function () use ($purchase) { try {
foreach ($purchase->items as $item) { DB::transaction(function () use ($purchase) {
$item->product->decrement('stock', $item->quantity); foreach ($purchase->items as $item) {
} $item->product->decrement('stock', $item->quantity);
$purchase->delete(); }
}); $purchase->delete();
});
return redirect()->back()->with('success', 'Data berhasil dihapus'); return redirect()->back()->with('success', 'Data berhasil dihapus');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to delete purchase', [
'purchase_id' => $purchase->id,
]);
return redirect()->back()->with('error', 'Terjadi kesalahan saat menghapus pembelian');
}
} }
public function bulkDestroy(Request $request): RedirectResponse public function bulkDestroy(Request $request): RedirectResponse
{ {
$ids = $request->input('ids'); $ids = $request->input('ids');
DB::transaction(function () use ($ids) { try {
$purchases = Purchase::with('items')->whereIn('id', $ids)->get(); DB::transaction(function () use ($ids) {
foreach ($purchases as $purchase) { $purchases = Purchase::with('items')->whereIn('id', $ids)->get();
foreach ($purchase->items as $item) { foreach ($purchases as $purchase) {
$item->product->decrement('stock', $item->quantity); foreach ($purchase->items as $item) {
$item->product->decrement('stock', $item->quantity);
}
$purchase->delete();
} }
$purchase->delete(); });
}
});
return redirect()->back()->with('success', 'Data terpilih berhasil dihapus'); return redirect()->back()->with('success', 'Data terpilih berhasil dihapus');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to bulk delete purchases', [
'ids' => $ids,
]);
return redirect()->back()->with('error', 'Terjadi kesalahan saat menghapus beberapa pembelian');
}
} }
} }

View File

@ -7,6 +7,7 @@
use App\Http\Requests\Admin\Master\ProductRequest; use App\Http\Requests\Admin\Master\ProductRequest;
use App\Models\Category; use App\Models\Category;
use App\Models\Product; use App\Models\Product;
use App\Support\LogHelper;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -35,38 +36,57 @@ public function store(ProductRequest $request): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($request, $validated) { try {
$product = Product::create([ DB::transaction(function () use ($request, $validated) {
'name' => $validated['name'], $product = Product::create([
'description' => $validated['description'], 'name' => $validated['name'],
'description' => $validated['description'],
]);
$product->categories()->sync($validated['category_ids'] ?? []);
foreach ($validated['prices'] as $type => $price) {
$enumType = PriceType::tryFrom($type);
if ($enumType) {
$product->prices()->create([
'price_type' => $enumType,
'price' => $price,
]);
}
}
if ($request->hasFile('thumbnail')) {
$product->addMediaFromRequest('thumbnail')
->toMediaCollection('thumbnail');
}
if ($request->hasFile('images')) {
foreach ($request->file('images') as $image) {
$product->addMedia($image)
->toMediaCollection('images');
}
}
});
return redirect()->route('product.index')->with('success', 'Data berhasil disimpan');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to store product', [
'input_summary' => [
'name' => $request->input('name'),
'categories_count' => count($request->input('category_ids', [])),
'prices_keys' => array_keys($request->input('prices', [])),
'has_thumbnail' => $request->hasFile('thumbnail'),
'images_count' => $request->hasFile('images')
? count($request->file('images'))
: 0,
],
]); ]);
$product->categories()->sync($validated['category_ids'] ?? []); return redirect()
->back()
foreach ($validated['prices'] as $type => $price) { ->withInput()
$enumType = PriceType::tryFrom($type); ->with('error', 'Terjadi kesalahan saat menyimpan data');
if ($enumType) { }
$product->prices()->create([
'price_type' => $enumType,
'price' => $price,
]);
}
}
if ($request->hasFile('thumbnail')) {
$product->addMediaFromRequest('thumbnail')
->toMediaCollection('thumbnail');
}
if ($request->hasFile('images')) {
foreach ($request->file('images') as $image) {
$product->addMedia($image)
->toMediaCollection('images');
}
}
});
return redirect()->route('product.index')->with('success', 'Data berhasil disimpan');
} }
public function edit(Product $product): Response public function edit(Product $product): Response
@ -83,46 +103,63 @@ public function update(ProductRequest $request, Product $product): RedirectRespo
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($request, $product, $validated) { try {
$product->update([ DB::transaction(function () use ($request, $product, $validated) {
'name' => $validated['name'], $product->update([
'description' => $validated['description'], 'name' => $validated['name'],
'stock' => $validated['stock'] ?? 0, 'description' => $validated['description'],
'stock' => $validated['stock'] ?? 0,
]);
$product->categories()->sync($validated['category_ids'] ?? []);
foreach ($validated['prices'] as $type => $price) {
$enumType = PriceType::tryFrom($type);
if ($enumType) {
$product->prices()->updateOrCreate(
['price_type' => $enumType],
['price' => $price]
);
}
}
if (! empty($validated['delete_image_ids'])) {
Media::whereIn('id', $validated['delete_image_ids'])
->where('model_type', Product::class)
->where('model_id', $product->id)
->each(fn ($media) => $media->delete());
}
if ($request->hasFile('thumbnail')) {
$product->addMediaFromRequest('thumbnail')
->toMediaCollection('thumbnail');
}
if ($request->hasFile('images')) {
foreach ($request->file('images') as $image) {
$product->addMedia($image)
->toMediaCollection('images');
}
}
});
return redirect()->route('product.index')->with('success', 'Data berhasil diperbarui');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to update product', [
'product_id' => $product->id,
'input_summary' => [
'name' => $request->input('name'),
'categories_count' => count($request->input('category_ids', [])),
'prices_keys' => array_keys($request->input('prices', [])),
'has_thumbnail' => $request->hasFile('thumbnail'),
],
]); ]);
$product->categories()->sync($validated['category_ids'] ?? []); return redirect()
->back()
foreach ($validated['prices'] as $type => $price) { ->withInput()
$enumType = PriceType::tryFrom($type); ->with('error', 'Terjadi kesalahan saat memperbarui data');
if ($enumType) { }
$product->prices()->updateOrCreate(
['price_type' => $enumType],
['price' => $price]
);
}
}
if (! empty($validated['delete_image_ids'])) {
Media::whereIn('id', $validated['delete_image_ids'])
->where('model_type', Product::class)
->where('model_id', $product->id)
->each(fn ($media) => $media->delete());
}
if ($request->hasFile('thumbnail')) {
$product->addMediaFromRequest('thumbnail')
->toMediaCollection('thumbnail');
}
if ($request->hasFile('images')) {
foreach ($request->file('images') as $image) {
$product->addMedia($image)
->toMediaCollection('images');
}
}
});
return redirect()->route('product.index')->with('success', 'Data berhasil diperbarui');
} }
public function destroy(Product $product): RedirectResponse public function destroy(Product $product): RedirectResponse

View File

@ -5,6 +5,7 @@
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Master\UserRequest; use App\Http\Requests\Admin\Master\UserRequest;
use App\Models\User; use App\Models\User;
use App\Support\LogHelper;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -31,25 +32,37 @@ public function store(UserRequest $request): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($validated) { try {
$user = User::create([ DB::transaction(function () use ($validated) {
$user = User::create([
'username' => $validated['username'],
'email' => $validated['email'],
'password' => Hash::make(config('auth.password_default')),
]);
$user->profile()->create([
'nik' => $validated['nik'],
'full_name' => $validated['full_name'],
'phone_number' => $validated['phone_number'],
'address' => $validated['address'],
'birth_place' => $validated['birth_place'],
'birth_date' => $validated['birth_date'],
'base_salary' => $validated['base_salary'],
]);
});
return redirect()->route('user.index')->with('success', 'Data berhasil disimpan');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to store user', [
'username' => $validated['username'], 'username' => $validated['username'],
'email' => $validated['email'], 'email' => $validated['email'],
'password' => Hash::make(config('auth.password_default')),
]); ]);
$user->profile()->create([ return redirect()
'nik' => $validated['nik'], ->back()
'full_name' => $validated['full_name'], ->withInput()
'phone_number' => $validated['phone_number'], ->with('error', 'Terjadi kesalahan saat menyimpan data');
'address' => $validated['address'], }
'birth_place' => $validated['birth_place'],
'birth_date' => $validated['birth_date'],
'base_salary' => $validated['base_salary'],
]);
});
return redirect()->route('user.index')->with('success', 'Data berhasil disimpan');
} }
public function edit(User $user): Response public function edit(User $user): Response
@ -65,24 +78,36 @@ public function update(UserRequest $request, User $user): RedirectResponse
{ {
$validated = $request->validated(); $validated = $request->validated();
DB::transaction(function () use ($user, $validated) { try {
$user->update([ DB::transaction(function () use ($user, $validated) {
$user->update([
'username' => $validated['username'],
'email' => $validated['email'],
]);
$user->profile()->update([
'nik' => $validated['nik'],
'full_name' => $validated['full_name'],
'phone_number' => $validated['phone_number'],
'address' => $validated['address'],
'birth_place' => $validated['birth_place'],
'birth_date' => $validated['birth_date'],
'base_salary' => $validated['base_salary'],
]);
});
return redirect()->route('user.index')->with('success', 'Data berhasil diperbarui');
} catch (\Throwable $e) {
LogHelper::logException($e, 'Failed to update user', [
'user_id' => $user->id,
'username' => $validated['username'], 'username' => $validated['username'],
'email' => $validated['email'],
]); ]);
$user->profile()->update([ return redirect()
'nik' => $validated['nik'], ->back()
'full_name' => $validated['full_name'], ->withInput()
'phone_number' => $validated['phone_number'], ->with('error', 'Terjadi kesalahan saat memperbarui data');
'address' => $validated['address'], }
'birth_place' => $validated['birth_place'],
'birth_date' => $validated['birth_date'],
'base_salary' => $validated['base_salary'],
]);
});
return redirect()->route('user.index')->with('success', 'Data berhasil diperbarui');
} }
public function resetPassword(User $user): RedirectResponse public function resetPassword(User $user): RedirectResponse

View File

@ -39,8 +39,8 @@ private function getLogFiles(): array
$files = File::files($path); $files = File::files($path);
return collect($files) return collect($files)
->map(fn($file) => $file->getFilename()) ->map(fn ($file) => $file->getFilename())
->filter(fn($filename) => str_ends_with($filename, '.log')) ->filter(fn ($filename) => str_ends_with($filename, '.log'))
->sortDesc() ->sortDesc()
->take(10) ->take(10)
->values() ->values()

47
app/Support/LogHelper.php Normal file
View File

@ -0,0 +1,47 @@
<?php
namespace App\Support;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Throwable;
class LogHelper
{
/**
* Log an exception with full request and context details.
*
* @param Throwable $e The exception to log
* @param string $message Custom log message
* @param array $extraContext Additional context data
*/
public static function logException(Throwable $e, string $message, array $extraContext = []): void
{
$request = request();
Log::error($message, [
'message' => $e->getMessage(),
'exception' => get_class($e),
'file' => $e->getFile(),
'line' => $e->getLine(),
// stack trace (take top 10)
'trace' => collect($e->getTrace())->take(10)->toArray(),
// request context
'request' => [
'url' => $request->fullUrl(),
'method' => $request->method(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'payload' => Arr::except($request->all(), ['password', 'token', 'password_confirmation']),
],
// auth context
'user' => optional(auth()->user())->only(['id', 'email']),
// custom context
'extra' => $extraContext,
]);
}
}