refactor: consolidate alert components and improve functionality
- replaced `alert-error` and `alert-success` components with a single reusable `alert` component - added JS functionality for dismissible alerts - updated related views to use the new `alert` component - adjusted broker profile logic to display initials and verification status dynamically - refactored morph relations from `type` to `role` - enhanced image preview behavior for file inputs - made broker migration fields nullable and added safeguards against registration errors - Added confirmation when a user wants delete deal - Add dynamic initials for user profile picture - make image file name non-overidding with timestamp
This commit is contained in:
parent
1275a74506
commit
ccb5a2ed5e
@ -3,8 +3,10 @@
|
||||
namespace App\Http\Controllers\Broker;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Broker;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BrokerProfileController extends Controller
|
||||
{
|
||||
@ -13,10 +15,31 @@ class BrokerProfileController extends Controller
|
||||
*/
|
||||
public function show(User $profile)
|
||||
{
|
||||
// Get the broker profile
|
||||
$broker = $profile->type;
|
||||
|
||||
// TODO: move this to middleware
|
||||
if (! $broker instanceof Broker) {
|
||||
abort(403, 'This user is not a broker.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the initials from a full name (e.g. John Doe, Alex Mark, jane clerk)
|
||||
* to display on profile page (e.g. JD, AM, JC).
|
||||
*/
|
||||
$initials = Str::of($profile->name)
|
||||
->explode(' ')
|
||||
->map(fn ($word) => Str::substr(ucfirst($word), 0, 1))
|
||||
->join('');
|
||||
|
||||
return view('dashboards.broker.profile.show')
|
||||
->with('name', $profile->name)
|
||||
->with('joinDate', $profile->created_at->format('F Y'))
|
||||
->with('email', $profile->email);
|
||||
->with('email', $profile->email)
|
||||
->with('initials', $initials)
|
||||
->with('verified', $broker->verified)
|
||||
->with('location', $broker->location)
|
||||
->with('phone', $broker->phone);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -5,7 +5,10 @@
|
||||
use App\Enums\UserStatus;
|
||||
use App\Enums\UserTypes;
|
||||
use App\Http\Requests\StoreRegisterdUser;
|
||||
use App\Models\Broker;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class RegisteredUserController extends Controller
|
||||
{
|
||||
@ -18,17 +21,30 @@ public function store(StoreRegisterdUser $request)
|
||||
{
|
||||
$data = $request->validated();
|
||||
|
||||
$status = UserStatus::Active->value;
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($data) {
|
||||
if ($data['role'] === UserTypes::Broker->value) {
|
||||
$status = UserStatus::Pending->value;
|
||||
}
|
||||
$data['status'] = UserStatus::Pending->value;
|
||||
|
||||
$data['status'] = $status;
|
||||
// Create Broker first, then the User linked to it
|
||||
$broker = Broker::create();
|
||||
$broker->user()->create($data);
|
||||
} else {
|
||||
$data['status'] = UserStatus::Active->value;
|
||||
|
||||
User::create($data);
|
||||
}
|
||||
});
|
||||
|
||||
return to_route('login.create')
|
||||
->with('userRegistered', 'User registered successfully.');
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Registration Failed: '.$e->getMessage());
|
||||
|
||||
return back()
|
||||
->withInput()
|
||||
->with('error', 'Something went wrong during registration.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,12 +11,11 @@ protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'verified' => 'boolean',
|
||||
'active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): MorphOne
|
||||
{
|
||||
return $this->morphOne(User::class, 'type');
|
||||
return $this->morphOne(User::class, 'role');
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,10 +63,10 @@ public function deals(): HasMany
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns model of User's type
|
||||
* Returns model of User's role type
|
||||
*/
|
||||
public function type(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
return $this->morphTo('role');
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,10 @@ class FileService
|
||||
{
|
||||
public function upload(UploadedFile $file, string $folder, string $filename): string
|
||||
{
|
||||
return $file->storeAs($folder, $filename.'.'.$file->extension(), 'public');
|
||||
// This prevents overriding of same image name
|
||||
$filename = time().$filename.'.'.$file->extension();
|
||||
|
||||
return $file->storeAs($folder, $filename, 'public');
|
||||
}
|
||||
|
||||
public function delete(?string $path): void
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
$middleware->redirectGuestsTo(fn () => route('login.create'));
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
||||
@ -13,11 +13,10 @@ public function up(): void
|
||||
{
|
||||
Schema::create('brokers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->text('bio');
|
||||
$table->string('location');
|
||||
$table->text('bio')->nullable();
|
||||
$table->string('location')->nullable();
|
||||
$table->string('phone')->nullable();
|
||||
$table->boolean('verified')->default(false);
|
||||
$table->boolean('active')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->nullableMorphs('type');
|
||||
$table->nullableMorphs('role');
|
||||
});
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ public function up(): void
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropMorphs('type');
|
||||
$table->dropMorphs('role');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
5
resources/js/alert.js
Normal file
5
resources/js/alert.js
Normal file
@ -0,0 +1,5 @@
|
||||
function removeAlert(child){
|
||||
const parentAlert = child.closest('.alert');
|
||||
parentAlert.style.display = "none";
|
||||
}
|
||||
document.removeAlert = removeAlert;
|
||||
@ -31,6 +31,7 @@ function upload(size) {
|
||||
closeModalBtn.addEventListener('click', () => {
|
||||
// this closes then modal and sets the preview image
|
||||
previewImage.src = imageUrl;
|
||||
previewImage.style.display = "block";
|
||||
modal.close();
|
||||
})
|
||||
|
||||
|
||||
@ -15,15 +15,15 @@ class="bg-linear-135 h-screen from-[#EFF6FF] to-[#FCF3F8] flex flex-col justify-
|
||||
</div>
|
||||
|
||||
@session('error')
|
||||
<x-ui.alert-error>
|
||||
<x-ui.alert variant="error">
|
||||
{{$value}}
|
||||
</x-ui.alert-error>
|
||||
</x-ui.alert>
|
||||
@endsession
|
||||
|
||||
@session('userRegistered')
|
||||
<x-ui.alert-success>
|
||||
<x-ui.alert variant="success">
|
||||
{{$value}}
|
||||
</x-ui.alert-success>
|
||||
</x-ui.alert>
|
||||
@endsession
|
||||
|
||||
<form action="{{route('login.store')}}" method="post" class="flex flex-col space-y-5">
|
||||
|
||||
@ -33,7 +33,7 @@ class="text-xs underline text-accent-601 mt-1">
|
||||
<x-ui.button :link="route('broker.deals.edit', $id)" class="w-full border border-accent-600/30"
|
||||
icon="pencil-square">Edit
|
||||
</x-ui.button>
|
||||
<form class="w-full" method="post" action="{{route('broker.deals.destroy', $id)}}">
|
||||
<form class="w-full" onsubmit="return confirm('Are you sure to delete this ?')" method="post" action="{{route('broker.deals.destroy', $id)}}">
|
||||
@csrf
|
||||
@method("DELETE")
|
||||
<x-ui.button variant="red" class="w-full" icon="trash" >Delete</x-ui.button>
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
<div class="py-2 px-4 bg-red-100 rounded-lg text-xs font-bold text-red-950">
|
||||
{{$slot}}
|
||||
</div>
|
||||
@ -1,3 +0,0 @@
|
||||
<div class="py-2 px-4 bg-green-100 rounded-lg text-xs font-bold text-green-950">
|
||||
{{$slot}}
|
||||
</div>
|
||||
21
resources/views/components/ui/alert.blade.php
Normal file
21
resources/views/components/ui/alert.blade.php
Normal file
@ -0,0 +1,21 @@
|
||||
@props(['variant' => 'neutral'])
|
||||
@php
|
||||
$variants = [
|
||||
'neutral' => 'bg-black text-white',
|
||||
'success' => 'bg-green-100 text-green-950',
|
||||
'error' => 'bg-red-100 text-red-950'
|
||||
];
|
||||
|
||||
$variantClasses = $variants[$variant];
|
||||
@endphp
|
||||
<div class="alert flex items-center space-x-4 py-2 px-4 rounded-lg text-xs font-bold {{$variantClasses}} ">
|
||||
<div class="flex-1">
|
||||
{{$slot}}
|
||||
</div>
|
||||
<div>
|
||||
<x-ui.button-sm onclick="removeAlert(this)">
|
||||
<x-heroicon-o-x-mark class="w-4"/>
|
||||
</x-ui.button-sm>
|
||||
</div>
|
||||
</div>
|
||||
@vite('resources/js/alert.js')
|
||||
@ -13,7 +13,7 @@
|
||||
<div
|
||||
class="p-8 border-2 border-dashed border-accent-600/70 rounded-lg flex flex-col space-y-2 justify-center items-center overflow-hidden min-h-40 bg-gray-50 relative">
|
||||
|
||||
<img src="{{ $value }}" id="preview-image"
|
||||
<img src="{{ $value }}" id="preview-image" alt="" onerror="this.style.display='none'"
|
||||
class="absolute inset-0 w-full h-full object-cover opacity-60 group-hover:opacity-10 transition-opacity">
|
||||
|
||||
<x-heroicon-o-arrow-up-tray class="w-8 text-accent-600/70 z-10"/>
|
||||
@ -26,7 +26,7 @@ class="absolute inset-0 w-full h-full object-cover opacity-60 group-hover:opacit
|
||||
<input
|
||||
name="{{$name}}"
|
||||
id="image-input"
|
||||
class="opacity-0 absolute w-full h-full top-0 left-0 cursor-pointer"
|
||||
class="opacity-0 absolute w-full h-full top-0 left-0 cursor-pointer z-20"
|
||||
type="file"
|
||||
accept="{{ $allowed ? '.' . str_replace(',', ',.', $allowed) : 'image/*' }}"
|
||||
onchange="upload(10)"
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
|
||||
<div class="wrapper">
|
||||
@session('success')
|
||||
<x-ui.alert-success>{{$value}}</x-ui.alert-success>
|
||||
<x-ui.alert variant="success">{{$value}}</x-ui.alert>
|
||||
@endsession
|
||||
|
||||
@session('error')
|
||||
<x-ui.alert-error>{{$value}}</x-ui.alert-error>
|
||||
<x-ui.alert variant="error">{{$value}}</x-ui.alert>
|
||||
@endsession
|
||||
</div>
|
||||
|
||||
|
||||
@ -16,7 +16,8 @@
|
||||
<div class="grid grid-cols-8 gap-6">
|
||||
|
||||
<div class="col-span-8 place-self-start md:col-span-1 flex items-center justify-center w-full">
|
||||
<div class="w-25 h-25 rounded-xl bg-linear-150 from-[#305afc] to-[#941dfb] text-5xl text-white flex justify-center items-center">UR
|
||||
<div class="w-25 h-25 rounded-xl bg-linear-150 from-[#305afc] to-[#941dfb] text-5xl text-white flex justify-center items-center">
|
||||
{{$initials}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -25,7 +26,7 @@
|
||||
<div class="">
|
||||
<p class="text-3xl font-bold">{{$name ?? 'Name'}}</p>
|
||||
<div class="flex space-x-1 mt-2">
|
||||
<x-ui.button-sm variant="neutral">Verified Broker</x-ui.button-sm>
|
||||
<x-ui.button-sm variant="neutral">{{$verified ? 'Verified Broker' : 'Not Verified'}}</x-ui.button-sm>
|
||||
<x-heroicon-s-star class="ml-2 w-4 fill-amber-300"/>
|
||||
<p>4.8</p>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user