From 91a11c8f56ac29ab11b4f4de69b3b2bc1eb42e63 Mon Sep 17 00:00:00 2001 From: kusowl Date: Tue, 27 Jan 2026 17:15:04 +0530 Subject: [PATCH] feature(admin-panel): add admin dashboard --- app/Actions/GetAdminStatsAction.php | 22 ++++++ .../Admin/AdminDashboardController.php | 15 ++++ app/Models/Admin.php | 14 ++++ app/Models/User.php | 15 ++-- .../2026_01_27_083425_create_admins_table.php | 21 ++++++ database/seeders/AdminUserSeeder.php | 25 +++++++ database/seeders/DatabaseSeeder.php | 11 +-- .../dashboard/admin/layout.blade.php | 26 +++++++ .../dashboard/admin/navbar.blade.php | 45 ++++++++++++ .../dashboard/admin/sidebar.blade.php | 70 +++++++++++++++++++ .../dashboard/admin/stats.blade.php | 20 ++++++ .../dashboard/broker/sidebar/item.blade.php | 9 ++- .../dashboard/broker/stats.blade.php | 2 +- .../components/dashboard/stats-card.blade.php | 10 +-- .../admin/customers/index.blade.php | 48 +++++++++++++ .../views/dashboards/admin/index.blade.php | 9 ++- routes/web.php | 10 +-- routes/web/admin.php | 14 ++++ 18 files changed, 352 insertions(+), 34 deletions(-) create mode 100644 app/Actions/GetAdminStatsAction.php create mode 100644 app/Http/Controllers/Admin/AdminDashboardController.php create mode 100644 app/Models/Admin.php create mode 100644 database/migrations/2026_01_27_083425_create_admins_table.php create mode 100644 database/seeders/AdminUserSeeder.php create mode 100644 resources/views/components/dashboard/admin/layout.blade.php create mode 100644 resources/views/components/dashboard/admin/navbar.blade.php create mode 100644 resources/views/components/dashboard/admin/sidebar.blade.php create mode 100644 resources/views/components/dashboard/admin/stats.blade.php create mode 100644 resources/views/dashboards/admin/customers/index.blade.php create mode 100644 routes/web/admin.php diff --git a/app/Actions/GetAdminStatsAction.php b/app/Actions/GetAdminStatsAction.php new file mode 100644 index 0000000..2ed2eac --- /dev/null +++ b/app/Actions/GetAdminStatsAction.php @@ -0,0 +1,22 @@ + + */ + public function execute(): array + { + return [ + 'listings' => Deal::count(), + 'customers' => User::where('role', UserTypes::User->value)->count(), + 'brokers' => User::where('role', UserTypes::Broker->value)->count(), + ]; + } +} diff --git a/app/Http/Controllers/Admin/AdminDashboardController.php b/app/Http/Controllers/Admin/AdminDashboardController.php new file mode 100644 index 0000000..c63b822 --- /dev/null +++ b/app/Http/Controllers/Admin/AdminDashboardController.php @@ -0,0 +1,15 @@ +with('stats', $action->execute()); + } +} diff --git a/app/Models/Admin.php b/app/Models/Admin.php new file mode 100644 index 0000000..eb7e9ce --- /dev/null +++ b/app/Models/Admin.php @@ -0,0 +1,14 @@ +morphOne(User::class, 'role'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index cffb2cd..ec470ea 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -57,6 +57,16 @@ public function isBroker(): bool return $this->type instanceof Broker; } + public function isAdmin(): bool + { + return $this->type instanceof Admin; + } + + public function isCustomer(): bool + { + return $this->type instanceof Customer; + } + public function deals(): HasMany { return $this->hasMany(Deal::class); @@ -80,11 +90,6 @@ public function recentSearches(): HasMany return $this->hasMany(RecentSearch::class); } - public function isCustomer(): bool - { - return $this->type instanceof Customer; - } - public function dealsInteractions(): HasManyThrough { return $this->hasManyThrough(Interaction::class, Deal::class); diff --git a/database/migrations/2026_01_27_083425_create_admins_table.php b/database/migrations/2026_01_27_083425_create_admins_table.php new file mode 100644 index 0000000..6e11a6b --- /dev/null +++ b/database/migrations/2026_01_27_083425_create_admins_table.php @@ -0,0 +1,21 @@ +id(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('admins'); + } +}; diff --git a/database/seeders/AdminUserSeeder.php b/database/seeders/AdminUserSeeder.php new file mode 100644 index 0000000..88299a4 --- /dev/null +++ b/database/seeders/AdminUserSeeder.php @@ -0,0 +1,25 @@ + 'Admin', + 'email' => 'admin@dealhub.com', + 'password' => 'password', + 'status' => UserStatus::Active->value, + 'role' => UserTypes::Admin->value, + ]; + + $admin = Admin::firstOrCreate(); + $admin->user()->updateOrCreate($data); + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 6b901f8..34ce8a3 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; @@ -13,13 +12,5 @@ class DatabaseSeeder extends Seeder /** * Seed the application's database. */ - public function run(): void - { - // User::factory(10)->create(); - - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); - } + public function run(): void {} } diff --git a/resources/views/components/dashboard/admin/layout.blade.php b/resources/views/components/dashboard/admin/layout.blade.php new file mode 100644 index 0000000..aec4ba1 --- /dev/null +++ b/resources/views/components/dashboard/admin/layout.blade.php @@ -0,0 +1,26 @@ +@props(['title' => '']) + +
+
+
diff --git a/resources/views/components/dashboard/admin/navbar.blade.php b/resources/views/components/dashboard/admin/navbar.blade.php new file mode 100644 index 0000000..af0de54 --- /dev/null +++ b/resources/views/components/dashboard/admin/navbar.blade.php @@ -0,0 +1,45 @@ + + + +
+
+ + + + +
+ +
diff --git a/resources/views/components/dashboard/admin/sidebar.blade.php b/resources/views/components/dashboard/admin/sidebar.blade.php new file mode 100644 index 0000000..04618af --- /dev/null +++ b/resources/views/components/dashboard/admin/sidebar.blade.php @@ -0,0 +1,70 @@ +@props(['activeClass' => 'bg-gray-100 text-gray-900']) + +
merge([ 'class' => 'border-r border-r-gray-300 transition-all duration-300 ease-in-out w-64 relative'])}}> + +
+@vite(['resources/js/sidebar.js']) + + diff --git a/resources/views/components/dashboard/admin/stats.blade.php b/resources/views/components/dashboard/admin/stats.blade.php new file mode 100644 index 0000000..d3a4978 --- /dev/null +++ b/resources/views/components/dashboard/admin/stats.blade.php @@ -0,0 +1,20 @@ +@props(['stats' => []]) +
+ + + + + + + + + + + + + + + + + +
diff --git a/resources/views/components/dashboard/broker/sidebar/item.blade.php b/resources/views/components/dashboard/broker/sidebar/item.blade.php index 54aff92..931a2c9 100644 --- a/resources/views/components/dashboard/broker/sidebar/item.blade.php +++ b/resources/views/components/dashboard/broker/sidebar/item.blade.php @@ -1,5 +1,10 @@ -@props([ 'link' => '']) +@props([ 'link' => '', 'active' => false]) +@php + if (!$active){ + $active = url()->current() === $link; + } +@endphp @aware(['activeClass' => 'bg-gray-100 text-gray-900']) -class(["flex space-x-3 items-center pl-3 py-3 rounded-xl hover:bg-gray-100 border border-transparent ease-in-out transition-all duration-300 active:scale-80 hover:border-gray-300", $activeClass => url()->current() == $link])}} > +class(["flex space-x-3 items-center pl-3 py-3 rounded-xl hover:bg-gray-100 border border-transparent ease-in-out transition-all duration-300 active:scale-80 hover:border-gray-300", $activeClass => $active])}} > {{$slot}} diff --git a/resources/views/components/dashboard/broker/stats.blade.php b/resources/views/components/dashboard/broker/stats.blade.php index 9272c6a..be752aa 100644 --- a/resources/views/components/dashboard/broker/stats.blade.php +++ b/resources/views/components/dashboard/broker/stats.blade.php @@ -1,6 +1,6 @@ @props(['stats' => []])
- + diff --git a/resources/views/components/dashboard/stats-card.blade.php b/resources/views/components/dashboard/stats-card.blade.php index 548fc04..b51c038 100644 --- a/resources/views/components/dashboard/stats-card.blade.php +++ b/resources/views/components/dashboard/stats-card.blade.php @@ -1,12 +1,14 @@ @props(['title' => '', 'score' => '']) -
+

{{$title}}

-

{{$score}}

-
- {{$slot}} +
+

{{$score}}

+
+ {{$slot}} +
diff --git a/resources/views/dashboards/admin/customers/index.blade.php b/resources/views/dashboards/admin/customers/index.blade.php new file mode 100644 index 0000000..bba7e8e --- /dev/null +++ b/resources/views/dashboards/admin/customers/index.blade.php @@ -0,0 +1,48 @@ + + + + +
+ + +

Customer details

+ + + Name + Email + Location + + + @forelse($customers as $customer) + + {{$customer->user->name}} + {{$customer->user->email}} + {{$customer->location}} + +
+ + + +
+ @csrf + + + +
+
+
+
+ @empty + + No Deals found + + @endforelse +
+
+
+
diff --git a/resources/views/dashboards/admin/index.blade.php b/resources/views/dashboards/admin/index.blade.php index 9302370..5c1418e 100644 --- a/resources/views/dashboards/admin/index.blade.php +++ b/resources/views/dashboards/admin/index.blade.php @@ -1,3 +1,6 @@ - - - + + + + + + diff --git a/routes/web.php b/routes/web.php index 9c22ff1..5322ea5 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,26 +1,18 @@ name('home'); Route::get('/explore', ExplorePageController::class)->name('explore'); -Route::middleware('auth')->group(function () { - - Route::view('/admin/dashboard', 'dashboards.admin.index') - ->middleware(HasRole::class.':'.UserTypes::Admin->value) - ->name('admin.dashboard'); -}); - /** * This routes are accessed by JS XHR requests, and is loaded here cause * we do not want to use sanctum for web requests diff --git a/routes/web/admin.php b/routes/web/admin.php new file mode 100644 index 0000000..3163f3a --- /dev/null +++ b/routes/web/admin.php @@ -0,0 +1,14 @@ +name('admin.') + ->middleware([HasRole::class.':'.UserTypes::Admin->value, 'auth']) + ->group(function () { + Route::get('dashboard', AdminDashboardController::class)->name('dashboard'); + Route::resource('customers', CustomerController::class)->except('show', 'create', 'store'); + });