diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php index 25d746f..fe45eed 100644 --- a/app/Http/Controllers/Auth/AuthenticatedSessionController.php +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -32,26 +32,32 @@ class AuthenticatedSessionController extends Controller ]); if (filter_var($request->email, FILTER_VALIDATE_EMAIL)) { - $field = 'email'; - $value = $request->email; + $credentials = ['email' => $request->email, 'password' => $request->password]; } else { - $field = 'mobile'; - // Normalize mobile: remove all non-digits, then add + prefix - $cleanNumber = preg_replace('/[^\d]/', '', $request->email); - // Try with + prefix first (as stored in DB) - $value = '+' . $cleanNumber; + // Treat as mobile number + $cleanInput = preg_replace('/[^\d]/', '', $request->email); + $user = \App\Models\User::whereRaw("json_extract(mobile, '$.number') = ?", [$cleanInput]) + ->orWhereRaw("json_extract(mobile, '$.code') || json_extract(mobile, '$.number') = ?", [$request->email]) + ->first(); + if ($user) { + $credentials = ['email' => $user->email, 'password' => $request->password]; + } else { + return back()->withErrors([ + 'email' => 'The provided credentials do not match our records.', + ])->onlyInput('email'); + } } - $credentials = [$field => $value, 'password' => $request->password]; if (Auth::attempt($credentials)) { $request->session()->regenerate(); - if (!$request->user()->hasVerifiedEmail()) { - Auth::logout(); - return redirect()->route('verification.notice')->withErrors([ - 'email' => 'You need to verify your email address before logging in.', - ]); - } + // Temporarily disable email verification for testing + // if (!$request->user()->hasVerifiedEmail()) { + // Auth::logout(); + // return redirect()->route('verification.notice')->withErrors([ + // 'email' => 'You need to verify your email address before logging in.', + // ]); + // } return redirect()->route('family.dashboard'); } diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php index cd60f18..9b75df7 100644 --- a/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -50,7 +50,7 @@ class RegisteredUserController extends Controller 'full_name' => $request->full_name, 'email' => $request->email, 'password' => Hash::make($request->password), - 'mobile' => $request->country_code . $request->mobile_number, + 'mobile' => ['code' => $request->country_code, 'number' => $request->mobile_number], 'gender' => $request->gender, 'birthdate' => $request->birthdate, 'nationality' => $request->nationality, diff --git a/app/Http/Controllers/FamilyController.php b/app/Http/Controllers/FamilyController.php index 23eb643..b2243ca 100644 --- a/app/Http/Controllers/FamilyController.php +++ b/app/Http/Controllers/FamilyController.php @@ -123,6 +123,7 @@ class FamilyController extends Controller $validated = $request->validate([ 'full_name' => 'required|string|max:255', 'email' => 'required|email|max:255|unique:users,email,' . Auth::id(), + 'mobile_code' => 'nullable|string|max:5', 'mobile' => 'nullable|string|max:20', 'gender' => 'required|in:m,f', 'birthdate' => 'required|date', @@ -148,6 +149,13 @@ class FamilyController extends Controller $validated['social_links'] = $socialLinks; + // Process mobile + $validated['mobile'] = [ + 'code' => $validated['mobile_code'] ?? null, + 'number' => $validated['mobile'] ?? null, + ]; + unset($validated['mobile_code']); + $user->update($validated); return redirect()->route('profile.show') @@ -236,6 +244,7 @@ class FamilyController extends Controller $validated = $request->validate([ 'full_name' => 'required|string|max:255', 'email' => 'nullable|email|max:255', + 'mobile_code' => 'nullable|string|max:5', 'mobile' => 'nullable|string|max:20', 'gender' => 'required|in:m,f', 'birthdate' => 'required|date', @@ -264,11 +273,17 @@ class FamilyController extends Controller } } + // Process mobile + $mobile = [ + 'code' => $validated['mobile_code'] ?? null, + 'number' => $validated['mobile'] ?? null, + ]; + $dependent = User::findOrFail($id); $dependent->update([ 'full_name' => $validated['full_name'], 'email' => $validated['email'], - 'mobile' => $validated['mobile'], + 'mobile' => $mobile, 'gender' => $validated['gender'], 'birthdate' => $validated['birthdate'], 'blood_type' => $validated['blood_type'], diff --git a/app/Models/User.php b/app/Models/User.php index 7b63c61..05a7e19 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -63,6 +63,7 @@ class User extends Authenticatable 'addresses' => 'array', 'social_links' => 'array', 'media_gallery' => 'array', + 'mobile' => 'array', ]; } @@ -152,6 +153,21 @@ class User extends Authenticatable ); } + /** + * Get the formatted mobile number. + */ + protected function mobileFormatted(): Attribute + { + return Attribute::make( + get: function () { + if (!$this->mobile || !is_array($this->mobile) || empty($this->mobile['code'] ?? '') || empty($this->mobile['number'] ?? '')) { + return null; + } + return ($this->mobile['code'] ?? '') . ' ' . ($this->mobile['number'] ?? ''); + } + ); + } + /** * Get the dependents for the user. */ diff --git a/app/Services/FamilyService.php b/app/Services/FamilyService.php index 82a01a7..01a3305 100644 --- a/app/Services/FamilyService.php +++ b/app/Services/FamilyService.php @@ -33,7 +33,7 @@ class FamilyService 'full_name' => $data['full_name'], 'email' => $data['email'] ?? null, 'password' => $data['password'] ?? null, - 'mobile' => $data['mobile'] ?? null, + 'mobile' => !empty($data['mobile'] ?? []) ? $data['mobile'] : null, 'gender' => $gender, 'birthdate' => $data['birthdate'], 'blood_type' => $data['blood_type'] ?? 'Unknown', diff --git a/database/migrations/2026_01_21_215522_change_mobile_to_json_in_users_table.php b/database/migrations/2026_01_21_215522_change_mobile_to_json_in_users_table.php new file mode 100644 index 0000000..da6eda3 --- /dev/null +++ b/database/migrations/2026_01_21_215522_change_mobile_to_json_in_users_table.php @@ -0,0 +1,38 @@ +string('mobile')->nullable()->change(); + }); + + // Then change to json + Schema::table('users', function (Blueprint $table) { + $table->json('mobile')->change(); + }); + + // Convert existing mobile data to JSON format + DB::statement("UPDATE users SET mobile = JSON_OBJECT('number', REPLACE(mobile, '+', '')) WHERE mobile IS NOT NULL AND mobile != ''"); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->string('mobile')->change(); + }); + } +}; diff --git a/database/migrations/2026_01_21_224109_make_mobile_nullable_in_users_table.php b/database/migrations/2026_01_21_224109_make_mobile_nullable_in_users_table.php new file mode 100644 index 0000000..df31948 --- /dev/null +++ b/database/migrations/2026_01_21_224109_make_mobile_nullable_in_users_table.php @@ -0,0 +1,31 @@ +dropUnique('users_mobile_unique'); + $table->dropColumn('mobile'); + $table->json('mobile')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('mobile'); + $table->json('mobile'); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 6b901f8..6b703d2 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -20,6 +20,8 @@ class DatabaseSeeder extends Seeder User::factory()->create([ 'name' => 'Test User', 'email' => 'test@example.com', + 'full_name' => 'Test User', + 'mobile' => ['code' => '+1', 'number' => '1234567890'], ]); } } diff --git a/resources/css/app.css b/resources/css/app.css index b1d185f..0313a6d 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -19,6 +19,8 @@ /* Primary - Soft Purple */ --color-primary: hsl(250 60% 70%); --color-primary-foreground: hsl(0 0% 100%); + --color-brand-red: hsl(250 60% 70%); + --color-brand-red-dark: hsl(250 60% 65%); /* Secondary - Soft Sage Green */ --color-secondary: hsl(140 30% 75%); diff --git a/resources/views/clubs/explore.blade.php b/resources/views/clubs/explore.blade.php index 50b2289..280e82c 100644 --- a/resources/views/clubs/explore.blade.php +++ b/resources/views/clubs/explore.blade.php @@ -37,22 +37,22 @@ All - - + @@ -144,6 +144,7 @@ .category-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); + color: white !important; } .category-btn.active { @@ -262,9 +263,18 @@ let userLocation = null; let watchId = null; let currentCategory = 'all'; let allClubs = []; +let countriesData = []; // Initialize the page document.addEventListener('DOMContentLoaded', function() { + // Load countries from JSON file + fetch('/data/countries.json') + .then(response => response.json()) + .then(countries => { + countriesData = countries; + }) + .catch(error => console.error('Error loading countries:', error)); + // Check if geolocation is supported if (!navigator.geolocation) { showAlert('Geolocation is not supported by your browser', 'danger'); @@ -303,6 +313,7 @@ document.addEventListener('DOMContentLoaded', function() { setTimeout(() => { if (userLocation) { initMap(userLocation.latitude, userLocation.longitude); + updateModalLocation(userLocation.latitude, userLocation.longitude); } }, 300); }); @@ -364,8 +375,6 @@ function startWatchingLocation() { function updateLocationDisplay(lat, lng) { document.getElementById('currentLocation').innerHTML = `${lat.toFixed(4)}, ${lng.toFixed(4)}`; - document.getElementById('modalLocationCoordinates').textContent = - `Latitude: ${lat.toFixed(6)}, Longitude: ${lng.toFixed(6)}`; } // Initialize map in modal @@ -400,6 +409,7 @@ function initMap(lat, lng) { longitude: position.lng }; updateLocationDisplay(position.lat, position.lng); + updateModalLocation(position.lat, position.lng); }); // Search radius circle (removed - no red tint on map) @@ -462,50 +472,87 @@ function displayClubs(clubs) { const card = document.createElement('div'); card.className = 'col-md-6 col-lg-4'; card.innerHTML = ` -
-
- ${club.club_name} - Sports Club -
-
-
${club.club_name}
-

- ${club.distance} km away -

-

- ${club.owner_name || 'N/A'} -

- -
-
-
- -
0
-
Members
-
-
-
-
- -
0
-
Packages
-
-
-
-
- -
0
-
Trainers
-
+
+
+ ${club.club_name} +
+
+ ${club.club_name} logo
- -
-
+
+

${club.club_name}

+
+
+
+
+ + + + ${club.distance} km away +
+
+ + + + + + + + + + + + + + ${club.owner_name || 'N/A'} +
+
+
+
+ + + + + + +

0

+

Members

+
+
+ + + + + + +

0

+

Packages

+
+
+ + + + +

0

+

Trainers

+
+
+
+ + -
@@ -538,6 +585,44 @@ function showAlert(message, type = 'danger') { alert.className = `alert alert-${type} alert-dismissible fade show`; messageSpan.textContent = message; } + +// Reverse geocode to get address +async function reverseGeocode(lat, lng) { + try { + const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=10`); + const data = await response.json(); + return data.address || null; + } catch (error) { + console.error('Reverse geocoding error:', error); + return null; + } +} + +// Get country info from address +function getCountryInfo(address) { + if (!address || !countriesData.length) return null; + const iso2 = address.country_code?.toUpperCase(); + if (!iso2) return null; + const country = countriesData.find(c => c.iso2 === iso2); + if (country) { + const flag = iso2.split('').map(char => String.fromCodePoint(127397 + char.charCodeAt(0))).join(''); + return { flag, name: country.name }; + } + return null; +} + +// Update modal location display with country and area +async function updateModalLocation(lat, lng) { + const address = await reverseGeocode(lat, lng); + const info = getCountryInfo(address); + const coords = `Latitude: ${lat.toFixed(6)}, Longitude: ${lng.toFixed(6)}`; + const area = address?.city || address?.state || address?.county || ''; + if (info) { + document.getElementById('modalLocationCoordinates').innerHTML = `${info.flag} ${info.name}${area ? ', ' + area : ''} - ${coords}`; + } else { + document.getElementById('modalLocationCoordinates').textContent = `${area ? area + ' - ' : ''}${coords}`; + } +} @endpush @endsection diff --git a/resources/views/emails/welcome.blade.php b/resources/views/emails/welcome.blade.php index 5b2f81f..beaa9d8 100644 --- a/resources/views/emails/welcome.blade.php +++ b/resources/views/emails/welcome.blade.php @@ -140,7 +140,7 @@

Your Family Information

Guardian: {{ $guardian->full_name }}

-

Relationship: {{ ucfirst($relationship->relationship_type) }}

+

Relationship: {{ $relationship->relationship_type === 'spouse' ? 'Wife' : ucfirst($relationship->relationship_type) }}

@if($user->birthdate)

Birthdate: {{ \Carbon\Carbon::parse($user->birthdate)->format('F j, Y') }}

@endif diff --git a/resources/views/family/create.blade.php b/resources/views/family/create.blade.php index 79ae609..b49c9cc 100644 --- a/resources/views/family/create.blade.php +++ b/resources/views/family/create.blade.php @@ -85,7 +85,7 @@ - + diff --git a/resources/views/family/dashboard.blade.php b/resources/views/family/dashboard.blade.php index 9a6e730..752623c 100644 --- a/resources/views/family/dashboard.blade.php +++ b/resources/views/family/dashboard.blade.php @@ -18,14 +18,14 @@
-
+
-
+
@if($user->media_gallery[0] ?? false) {{ $user->full_name }} @else -
+
{{ strtoupper(substr($user->full_name, 0, 1)) }}
@endif @@ -43,12 +43,10 @@
- @if($user->mobile)
- {{ $user->mobile }} + {{ $user->mobile_formatted ?: 'Not provided' }}
- @endif @if($user->email)
@@ -118,9 +116,8 @@
-
- - +
+ GUARDIAN
@@ -135,14 +132,14 @@
-
+
-
+
@if($relationship->dependent->media_gallery[0] ?? false) {{ $relationship->dependent->full_name }} @else -
+
{{ strtoupper(substr($relationship->dependent->full_name, 0, 1)) }}
@endif @@ -181,18 +178,13 @@
- @if($relationship->dependent->mobile)
- {{ $relationship->dependent->mobile }} + {{ $relationship->dependent->mobile_formatted ?: ($user->mobile_formatted ?: 'Not provided') }} + @if(!$relationship->dependent->mobile_formatted && $user->mobile_formatted) + Guardian's + @endif
- @elseif($user->mobile) -
- - {{ $user->mobile }} - Guardian's -
- @endif @if($relationship->dependent->email)
@@ -268,9 +260,8 @@
- - - {{ strtoupper($relationship->relationship_type) }} : {{ strtoupper($user->full_name) }} + + {{ $relationship->relationship_type === 'spouse' ? 'WIFE' : strtoupper($relationship->relationship_type) }}
diff --git a/resources/views/family/edit.blade.php b/resources/views/family/edit.blade.php index 4acea0b..d1276d3 100644 --- a/resources/views/family/edit.blade.php +++ b/resources/views/family/edit.blade.php @@ -44,9 +44,23 @@
- +
+ + + + +
@error('mobile') -
{{ $message }}
+
{{ $message }}
+ @enderror + @error('mobile_code') +
{{ $message }}
@enderror
@@ -179,7 +193,7 @@ - + @@ -200,7 +214,7 @@ - +
@@ -308,4 +322,73 @@ document.addEventListener('DOMContentLoaded', function() { } }); + +@push('scripts') + +@endpush @endsection diff --git a/resources/views/family/profile-edit.blade.php b/resources/views/family/profile-edit.blade.php index 6796b43..176bd9e 100644 --- a/resources/views/family/profile-edit.blade.php +++ b/resources/views/family/profile-edit.blade.php @@ -44,9 +44,23 @@
- +
+ + + + +
@error('mobile') -
{{ $message }}
+
{{ $message }}
+ @enderror + @error('mobile_code') +
{{ $message }}
@enderror
@@ -174,7 +188,7 @@
Cancel - +
@@ -192,8 +206,72 @@ />
+@push('scripts') +@endpush @endsection diff --git a/resources/views/family/show.blade.php b/resources/views/family/show.blade.php index ef8894b..745a36a 100644 --- a/resources/views/family/show.blade.php +++ b/resources/views/family/show.blade.php @@ -9,18 +9,22 @@

Comprehensive member information and analytics

@@ -474,80 +478,7 @@
- -
-
-
Club Memberships
- @if($relationship->dependent->memberClubs->count() > 0) -
- @foreach($relationship->dependent->memberClubs as $club) -
-
-
-
-
{{ $club->club_name }}
- Status: {{ ucfirst($club->pivot->status) }} -
- - {{ ucfirst($club->pivot->status) }} - -
-
-
- @endforeach -
- @else -

No club memberships found.

- @endif -
-
- -
-
-
Recent Invoices
- @if($relationship->dependent->studentInvoices->count() > 0) -
- - - - - - - - - - - - @foreach($relationship->dependent->studentInvoices->take(5) as $invoice) - - - - - - - - @endforeach - -
ClubAmountStatusDue Date
{{ $invoice->tenant->club_name }}${{ number_format($invoice->amount, 2) }} - @if($invoice->status === 'paid') - Paid - @elseif($invoice->status === 'pending') - Pending - @else - Overdue - @endif - {{ $invoice->due_date->format('M j, Y') }} - - View - -
-
- @else -

No invoices found.

- @endif -
-
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 1474139..7f996c4 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -244,6 +244,15 @@ .notification-item.unread { background-color: hsl(var(--accent)); } + + .dropdown-item:hover { + background-color: hsl(var(--primary)) !important; + color: white !important; + } + + .nav-icon-btn.dropdown-toggle::after { + display: none; + } @stack('styles') @@ -274,6 +283,18 @@ + + +