feature(admin-panel): add manage customer page
- list customer - edit customer details - delete customer
This commit is contained in:
parent
91a11c8f56
commit
690a50408d
30
app/Actions/UpdateCustomerAction.php
Normal file
30
app/Actions/UpdateCustomerAction.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
final readonly class UpdateCustomerAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function execute(array $data, User $profile): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Separate the user fields from the broker fields
|
||||||
|
*/
|
||||||
|
$userFields = ['name', 'email'];
|
||||||
|
$data = collect($data);
|
||||||
|
$profileData = $data->only($userFields)->toArray();
|
||||||
|
$userData = $data->except($userFields)->toArray();
|
||||||
|
|
||||||
|
DB::transaction(function () use ($profileData, $profile, $userData) {
|
||||||
|
$profile->update($profileData);
|
||||||
|
$user = $profile->type;
|
||||||
|
$user->update($userData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
59
app/Http/Controllers/Admin/CustomerController.php
Normal file
59
app/Http/Controllers/Admin/CustomerController.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Actions\UpdateCustomerAction;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\StoreCustomerProfileRequest;
|
||||||
|
use App\Models\Customer;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class CustomerController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.customers.index')
|
||||||
|
->with('customers', Customer::select(['id', 'location'])
|
||||||
|
->with('user:id,name,email,role_id,role_type')
|
||||||
|
->get()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Customer $customer)
|
||||||
|
{
|
||||||
|
return view('dashboards.admin.customers.edit')
|
||||||
|
->with('profile', $customer->user)
|
||||||
|
->with('backLink', route('admin.customers.index'))
|
||||||
|
->with('actionLink', route('admin.customers.update', $customer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(StoreCustomerProfileRequest $request, Customer $customer, UpdateCustomerAction $action)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$action->execute($request->validated(), $customer->user);
|
||||||
|
|
||||||
|
return to_route('admin.customers.index')
|
||||||
|
->with('success', 'Profile updated successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Customer Profile Update Failed: '.$e->getMessage(), $e->getTrace());
|
||||||
|
|
||||||
|
return back()->withInput()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Customer $customer)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
\DB::transaction(function () use ($customer) {
|
||||||
|
$customer->user->delete();
|
||||||
|
$customer->delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return back()->with('success', 'Customer deleted successfully.');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Customer Delete Failed: '.$e->getMessage(), $e->getTrace());
|
||||||
|
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,7 +33,7 @@ protected function deals(FormRequest $request, Builder $query, AddRecentSearchAc
|
|||||||
{
|
{
|
||||||
// Add a search query
|
// Add a search query
|
||||||
if ($request->has('search') && $request->get('search') !== null) {
|
if ($request->has('search') && $request->get('search') !== null) {
|
||||||
$query->tap(fn($q) => (new Deal)->search($q, $request->search));
|
$query->tap(fn ($q) => (new Deal)->search($q, $request->search));
|
||||||
|
|
||||||
\Illuminate\Support\defer(function () use ($action, $request) {
|
\Illuminate\Support\defer(function () use ($action, $request) {
|
||||||
$action->execute($request->user(), ['query' => $request->search]);
|
$action->execute($request->user(), ['query' => $request->search]);
|
||||||
@ -42,7 +42,7 @@ protected function deals(FormRequest $request, Builder $query, AddRecentSearchAc
|
|||||||
|
|
||||||
// Add category sorting filter
|
// Add category sorting filter
|
||||||
if ($request->has('category') && $request->get('category') !== null) {
|
if ($request->has('category') && $request->get('category') !== null) {
|
||||||
$query->tap(fn($q) => (new Deal)->filterByCategory($q, $request->category));
|
$query->tap(fn ($q) => (new Deal)->filterByCategory($q, $request->category));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add sorting filters
|
// Add sorting filters
|
||||||
@ -81,9 +81,10 @@ protected function categories(): Collection
|
|||||||
|
|
||||||
protected function recentSearches(): Collection
|
protected function recentSearches(): Collection
|
||||||
{
|
{
|
||||||
if (!Auth::check()) {
|
if (! Auth::check()) {
|
||||||
return collect();
|
return collect();
|
||||||
}
|
}
|
||||||
return Auth::user()->recentSearches()->latest()->select(['id','query'])->get();
|
|
||||||
|
return Auth::user()->recentSearches()->latest()->select(['id', 'query'])->get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,6 @@
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class ReportController extends Controller
|
class ReportController extends Controller
|
||||||
|
|||||||
@ -8,7 +8,8 @@ class RecentSearchController extends Controller
|
|||||||
{
|
{
|
||||||
public function __invoke(RecentSearch $recentSearch)
|
public function __invoke(RecentSearch $recentSearch)
|
||||||
{
|
{
|
||||||
$recentSearch->delete();
|
$recentSearch->delete();
|
||||||
return response()->json(['message' => 'Search deleted successfully.']);
|
|
||||||
|
return response()->json(['message' => 'Search deleted successfully.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
use App\Actions\GetUserFavoritesAction;
|
use App\Actions\GetUserFavoritesAction;
|
||||||
use App\Actions\GetUserReportedDealsAction;
|
use App\Actions\GetUserReportedDealsAction;
|
||||||
|
use App\Actions\UpdateCustomerAction;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Http\Requests\StoreCustomerProfileRequest;
|
use App\Http\Requests\StoreCustomerProfileRequest;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\ProfileInitialsService;
|
use App\Services\ProfileInitialsService;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class UserProfileController extends Controller
|
class UserProfileController extends Controller
|
||||||
@ -47,28 +47,20 @@ public function edit(User $profile)
|
|||||||
{
|
{
|
||||||
return view('dashboards.user.profile.edit')
|
return view('dashboards.user.profile.edit')
|
||||||
->with('profile', $profile)
|
->with('profile', $profile)
|
||||||
->with('broker', $profile->type);
|
->with('pageTitle', 'Edit Profile')
|
||||||
|
->with('title', 'Edit Your Profile')
|
||||||
|
->with('description', 'Update your profile information.')
|
||||||
|
->with('backLink', route('customer.profile.show', $profile))
|
||||||
|
->with('actionLink', route('customer.profile.update', $profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*/
|
*/
|
||||||
public function update(StoreCustomerProfileRequest $request, User $profile)
|
public function update(StoreCustomerProfileRequest $request, User $profile, UpdateCustomerAction $action)
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Separate the user fields from the broker fields
|
|
||||||
*/
|
|
||||||
$userFields = ['name', 'email'];
|
|
||||||
$data = collect($request->validated());
|
|
||||||
$profileData = $data->only($userFields)->toArray();
|
|
||||||
$userData = $data->except($userFields)->toArray();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($profileData, $profile, $userData) {
|
$action->execute($request->validated(), $profile);
|
||||||
$profile->update($profileData);
|
|
||||||
$user = $profile->type;
|
|
||||||
$user->update($userData);
|
|
||||||
});
|
|
||||||
|
|
||||||
return to_route('customer.profile.show', $profile)
|
return to_route('customer.profile.show', $profile)
|
||||||
->with('success', 'Profile updated successfully.');
|
->with('success', 'Profile updated successfully.');
|
||||||
|
|||||||
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use AllowDynamicProperties;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
#[AllowDynamicProperties]
|
||||||
class StoreCustomerProfileRequest extends FormRequest
|
class StoreCustomerProfileRequest extends FormRequest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -12,7 +14,17 @@ class StoreCustomerProfileRequest extends FormRequest
|
|||||||
*/
|
*/
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
return $this->user()->isCustomer();
|
// If this request is by a customer profile, then only allow the owner to update it.
|
||||||
|
if (isset($this->profile)) {
|
||||||
|
$this->user = $this->profile;
|
||||||
|
|
||||||
|
return $this->user()->id === $this->profile->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this request is by an admin, then allow them to update any profile.
|
||||||
|
$this->user = $this->customer->user;
|
||||||
|
|
||||||
|
return $this->user()->isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +37,7 @@ public function rules(): array
|
|||||||
return [
|
return [
|
||||||
'name' => 'required|string|min:3|max:255',
|
'name' => 'required|string|min:3|max:255',
|
||||||
'bio' => 'required|string|min:10|max:255',
|
'bio' => 'required|string|min:10|max:255',
|
||||||
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($this->user()->id)],
|
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($this->user->id)],
|
||||||
'phone' => 'required|string|min:10|max:255',
|
'phone' => 'required|string|min:10|max:255',
|
||||||
'location' => 'required|string|min:3|max:255',
|
'location' => 'required|string|min:3|max:255',
|
||||||
];
|
];
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
@props(['profile', 'actionLink', 'backLink'])
|
||||||
|
|
||||||
|
<x-dashboard.card class="w-full">
|
||||||
|
<h3 class="text-md font-bold">Profile Information</h3>
|
||||||
|
<form method="post" enctype="multipart/form-data" action="{{$actionLink}}"
|
||||||
|
class="flex flex-col space-y-8 mt-4">
|
||||||
|
@csrf
|
||||||
|
@method('PATCH')
|
||||||
|
|
||||||
|
<x-ui.input name="name" label="Name" :value="$profile->name" required placeholder="e.g. John Doe"/>
|
||||||
|
|
||||||
|
<x-ui.textarea :value="$profile->type->bio" name="bio" label="Bio" required
|
||||||
|
placeholder="Describe yourself in detail..."/>
|
||||||
|
|
||||||
|
<x-ui.input name="email" label="Email" :value="$profile->email" required
|
||||||
|
placeholder="example@email.com"/>
|
||||||
|
|
||||||
|
<x-ui.input name="phone" label="Phone" :value="$profile->type->phone" required placeholder="+00 00000 00000"/>
|
||||||
|
|
||||||
|
<x-ui.input name="location" label="Location" :value="$profile->type->location" required
|
||||||
|
placeholder="Kolkata, India"/>
|
||||||
|
|
||||||
|
<div class="grid md:grid-cols-12 w-full gap-4">
|
||||||
|
<x-ui.button variant="neutral" class="md:col-span-10">Update</x-ui.button>
|
||||||
|
<x-ui.button :link="$backLink"
|
||||||
|
class=" border border-accent-600/20 md:col-span-2">Cancel
|
||||||
|
</x-ui.button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</x-dashboard.card>
|
||||||
13
resources/views/dashboards/admin/customers/edit.blade.php
Normal file
13
resources/views/dashboards/admin/customers/edit.blade.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<x-dashboard.admin.layout title="Edit Customer">
|
||||||
|
<x-slot:heading>
|
||||||
|
<x-dashboard.page-heading
|
||||||
|
title="Edit Customer Profile"
|
||||||
|
description="Update customer profile information."
|
||||||
|
:back-link="$backLink"
|
||||||
|
/>
|
||||||
|
</x-slot:heading>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-center px-4 pb-4 pt-0 md:px-8 md:pb-8">
|
||||||
|
<x-dashboard.user.edit-profile-card :back-link="$backLink" :action-link="$actionLink" :profile="$profile" />
|
||||||
|
</div>
|
||||||
|
</x-dashboard.admin.layout>
|
||||||
@ -30,6 +30,7 @@
|
|||||||
onsubmit="return confirm('Are you sure to delete this ?')" method="post"
|
onsubmit="return confirm('Are you sure to delete this ?')" method="post"
|
||||||
class=" h-full items-center flex justify-center">
|
class=" h-full items-center flex justify-center">
|
||||||
@csrf
|
@csrf
|
||||||
|
@method('DELETE')
|
||||||
<x-ui.button-sm variant="red">
|
<x-ui.button-sm variant="red">
|
||||||
<x-heroicon-o-trash class="w-4"/>
|
<x-heroicon-o-trash class="w-4"/>
|
||||||
</x-ui.button-sm>
|
</x-ui.button-sm>
|
||||||
|
|||||||
@ -1,37 +1,10 @@
|
|||||||
<x-layout title="Edit Broker Profile">
|
<x-layout :title="$pageTitle">
|
||||||
<x-dashboard.page-heading
|
<x-dashboard.page-heading
|
||||||
title="Edit Profile"
|
:title="$title"
|
||||||
description="Modify your profile details"
|
:description="$description"
|
||||||
:back-link="route('customer.profile.show', $profile)"
|
:back-link="$backLink"
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center justify-center mt-4 md:mt-8 px-4 pb-4 pt-0 md:px-8 md:pb-8">
|
<div class="flex items-center justify-center mt-4 md:mt-8 px-4 pb-4 pt-0 md:px-8 md:pb-8">
|
||||||
<x-dashboard.card class="w-full">
|
<x-dashboard.user.edit-profile-card :action-link="$actionLink" :profile="$profile" :back-link="$backLink" />
|
||||||
<h3 class="text-md font-bold">Profile Information</h3>
|
|
||||||
<form method="post" enctype="multipart/form-data" action="{{route('customer.profile.update', $profile)}}"
|
|
||||||
class="flex flex-col space-y-8 mt-4">
|
|
||||||
@csrf
|
|
||||||
@method('PATCH')
|
|
||||||
|
|
||||||
<x-ui.input name="name" label="Name" :value="$profile->name" required placeholder="e.g. John Doe"/>
|
|
||||||
|
|
||||||
<x-ui.textarea :value="$broker->bio" name="bio" label="Bio" required
|
|
||||||
placeholder="Describe yourself in detail..."/>
|
|
||||||
|
|
||||||
<x-ui.input name="email" label="Email" :value="$profile->email" required
|
|
||||||
placeholder="example@email.com"/>
|
|
||||||
|
|
||||||
<x-ui.input name="phone" label="Phone" :value="$broker->phone" required placeholder="+00 00000 00000"/>
|
|
||||||
|
|
||||||
<x-ui.input name="location" label="Location" :value="$broker->location" required
|
|
||||||
placeholder="Kolkata, India"/>
|
|
||||||
|
|
||||||
<div class="grid md:grid-cols-12 w-full gap-4">
|
|
||||||
<x-ui.button variant="neutral" class="md:col-span-10">Update</x-ui.button>
|
|
||||||
<x-ui.button :link="route('broker.profile.show', $profile)"
|
|
||||||
class=" border border-accent-600/20 md:col-span-2">Cancel
|
|
||||||
</x-ui.button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</x-dashboard.card>
|
|
||||||
</div>
|
</div>
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user