Fix 'My Membership' 404 by adding missing profile flow
- Added a 'Create Member Profile' page for existing users who don't have a member record. - Updated MemberDashboardController to redirect to profile creation instead of aborting 404. - Added 'member.profile.create' and 'member.profile.store' routes.
This commit is contained in:
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Member;
|
||||
use App\Support\AuditLogger;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class MemberDashboardController extends Controller
|
||||
{
|
||||
@@ -12,7 +15,7 @@ class MemberDashboardController extends Controller
|
||||
$member = $user->member;
|
||||
|
||||
if (! $member) {
|
||||
abort(404);
|
||||
return redirect()->route('member.profile.create');
|
||||
}
|
||||
|
||||
$member->load([
|
||||
@@ -31,5 +34,58 @@ class MemberDashboardController extends Controller
|
||||
'pendingPayment' => $pendingPayment,
|
||||
]);
|
||||
}
|
||||
|
||||
public function createProfile()
|
||||
{
|
||||
$user = Auth::user();
|
||||
if ($user->member) {
|
||||
return redirect()->route('member.dashboard');
|
||||
}
|
||||
return view('member.create-profile');
|
||||
}
|
||||
|
||||
public function storeProfile(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
if ($user->member) {
|
||||
return redirect()->route('member.dashboard');
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'full_name' => ['required', 'string', 'max:255'],
|
||||
'phone' => ['nullable', 'string', 'max:20'],
|
||||
'national_id' => ['nullable', 'string', 'max:20'],
|
||||
'address_line_1' => ['nullable', 'string', 'max:255'],
|
||||
'address_line_2' => ['nullable', 'string', 'max:255'],
|
||||
'city' => ['nullable', 'string', 'max:100'],
|
||||
'postal_code' => ['nullable', 'string', 'max:10'],
|
||||
'emergency_contact_name' => ['nullable', 'string', 'max:255'],
|
||||
'emergency_contact_phone' => ['nullable', 'string', 'max:20'],
|
||||
'terms_accepted' => ['required', 'accepted'],
|
||||
]);
|
||||
|
||||
$member = Member::create([
|
||||
'user_id' => $user->id,
|
||||
'full_name' => $validated['full_name'],
|
||||
'email' => $user->email,
|
||||
'phone' => $validated['phone'] ?? null,
|
||||
'national_id' => $validated['national_id'] ?? null,
|
||||
'address_line_1' => $validated['address_line_1'] ?? null,
|
||||
'address_line_2' => $validated['address_line_2'] ?? null,
|
||||
'city' => $validated['city'] ?? null,
|
||||
'postal_code' => $validated['postal_code'] ?? null,
|
||||
'emergency_contact_name' => $validated['emergency_contact_name'] ?? null,
|
||||
'emergency_contact_phone' => $validated['emergency_contact_phone'] ?? null,
|
||||
'membership_status' => Member::STATUS_PENDING,
|
||||
'membership_type' => Member::TYPE_REGULAR,
|
||||
]);
|
||||
|
||||
AuditLogger::log('member.created_profile', $member, [
|
||||
'user_id' => $user->id,
|
||||
'name' => $member->full_name,
|
||||
]);
|
||||
|
||||
return redirect()->route('member.dashboard')
|
||||
->with('status', __('Profile completed! Please submit your membership payment.'));
|
||||
}
|
||||
}
|
||||
121
resources/views/member/create-profile.blade.php
Normal file
121
resources/views/member/create-profile.blade.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="text-xl font-semibold leading-tight text-gray-800 dark:text-gray-200">
|
||||
{{ __('Complete Your Membership Profile') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg dark:bg-gray-800">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
<div class="mb-6">
|
||||
{{ __('To access the member area, please provide your membership details. This information is required for our records.') }}
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('member.profile.store') }}" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
{{-- Basic Information --}}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">{{ __('Basic Information') }}</h3>
|
||||
|
||||
<!-- Full Name -->
|
||||
<div>
|
||||
<x-input-label for="full_name" :value="__('Full Name')" />
|
||||
<x-text-input id="full_name" class="block mt-1 w-full" type="text" name="full_name" :value="old('full_name', Auth::user()->name)" required autofocus />
|
||||
<x-input-error :messages="$errors->get('full_name')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Phone -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="phone" :value="__('Phone')" />
|
||||
<x-text-input id="phone" class="block mt-1 w-full" type="text" name="phone" :value="old('phone')" />
|
||||
<x-input-error :messages="$errors->get('phone')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- National ID (Optional) -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="national_id" :value="__('National ID (Optional)')" />
|
||||
<x-text-input id="national_id" class="block mt-1 w-full" type="text" name="national_id" :value="old('national_id')" maxlength="20" />
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ __('Your national ID will be encrypted for security.') }}</p>
|
||||
<x-input-error :messages="$errors->get('national_id')" class="mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Address Information --}}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">{{ __('Address') }}</h3>
|
||||
|
||||
<!-- Address Line 1 -->
|
||||
<div>
|
||||
<x-input-label for="address_line_1" :value="__('Address Line 1')" />
|
||||
<x-text-input id="address_line_1" class="block mt-1 w-full" type="text" name="address_line_1" :value="old('address_line_1')" />
|
||||
<x-input-error :messages="$errors->get('address_line_1')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Address Line 2 -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="address_line_2" :value="__('Address Line 2')" />
|
||||
<x-text-input id="address_line_2" class="block mt-1 w-full" type="text" name="address_line_2" :value="old('address_line_2')" />
|
||||
<x-input-error :messages="$errors->get('address_line_2')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mt-4">
|
||||
<!-- City -->
|
||||
<div>
|
||||
<x-input-label for="city" :value="__('City')" />
|
||||
<x-text-input id="city" class="block mt-1 w-full" type="text" name="city" :value="old('city')" />
|
||||
<x-input-error :messages="$errors->get('city')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Postal Code -->
|
||||
<div>
|
||||
<x-input-label for="postal_code" :value="__('Postal Code')" />
|
||||
<x-text-input id="postal_code" class="block mt-1 w-full" type="text" name="postal_code" :value="old('postal_code')" />
|
||||
<x-input-error :messages="$errors->get('postal_code')" class="mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Emergency Contact --}}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-4">
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-3">{{ __('Emergency Contact') }}</h3>
|
||||
|
||||
<!-- Emergency Contact Name -->
|
||||
<div>
|
||||
<x-input-label for="emergency_contact_name" :value="__('Emergency Contact Name')" />
|
||||
<x-text-input id="emergency_contact_name" class="block mt-1 w-full" type="text" name="emergency_contact_name" :value="old('emergency_contact_name')" />
|
||||
<x-input-error :messages="$errors->get('emergency_contact_name')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<!-- Emergency Contact Phone -->
|
||||
<div class="mt-4">
|
||||
<x-input-label for="emergency_contact_phone" :value="__('Emergency Contact Phone')" />
|
||||
<x-text-input id="emergency_contact_phone" class="block mt-1 w-full" type="text" name="emergency_contact_phone" :value="old('emergency_contact_phone')" />
|
||||
<x-input-error :messages="$errors->get('emergency_contact_phone')" class="mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Terms and Conditions --}}
|
||||
<div class="mt-4">
|
||||
<label class="inline-flex items-center">
|
||||
<input type="checkbox" name="terms_accepted" value="1" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" {{ old('terms_accepted') ? 'checked' : '' }} required>
|
||||
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __('I accept the terms and conditions and agree to submit payment for membership activation.') }}
|
||||
</span>
|
||||
</label>
|
||||
<x-input-error :messages="$errors->get('terms_accepted')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end mt-6">
|
||||
<x-primary-button>
|
||||
{{ __('Complete Profile') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -82,6 +82,10 @@ Route::middleware('auth')->group(function () {
|
||||
// Member Payment Submission Routes
|
||||
Route::get('/member/submit-payment', [MemberPaymentController::class, 'create'])->name('member.payments.create');
|
||||
Route::post('/member/payments', [MemberPaymentController::class, 'store'])->name('member.payments.store');
|
||||
|
||||
Route::get('/create-member-profile', [MemberDashboardController::class, 'createProfile'])->name('member.profile.create');
|
||||
Route::post('/create-member-profile', [MemberDashboardController::class, 'storeProfile'])->name('member.profile.store');
|
||||
|
||||
Route::get('/my-membership', [MemberDashboardController::class, 'show'])
|
||||
->name('member.dashboard');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user