diff --git a/app/Http/Controllers/ClubController.php b/app/Http/Controllers/ClubController.php
index 3a2c7a1..62add1b 100644
--- a/app/Http/Controllers/ClubController.php
+++ b/app/Http/Controllers/ClubController.php
@@ -54,9 +54,9 @@ class ClubController extends Controller
'gps_lat' => (float) $club->gps_lat,
'gps_long' => (float) $club->gps_long,
'distance' => round($distance, 2), // distance in kilometers
- 'owner_name' => $club->owner->full_name,
- 'owner_email' => $club->owner->email,
- 'owner_mobile' => $club->owner->mobile,
+ 'owner_name' => $club->owner ? $club->owner->full_name : 'N/A',
+ 'owner_email' => $club->owner ? $club->owner->email : null,
+ 'owner_mobile' => $club->owner ? $club->owner->mobile : null,
];
});
@@ -107,9 +107,7 @@ class ClubController extends Controller
*/
public function all()
{
- $clubs = Tenant::whereNotNull('gps_lat')
- ->whereNotNull('gps_long')
- ->with('owner')
+ $clubs = Tenant::with('owner')
->get()
->map(function ($club) {
return [
@@ -117,9 +115,9 @@ class ClubController extends Controller
'club_name' => $club->club_name,
'slug' => $club->slug,
'logo' => $club->logo,
- 'gps_lat' => (float) $club->gps_lat,
- 'gps_long' => (float) $club->gps_long,
- 'owner_name' => $club->owner->full_name,
+ 'gps_lat' => $club->gps_lat ? (float) $club->gps_lat : null,
+ 'gps_long' => $club->gps_long ? (float) $club->gps_long : null,
+ 'owner_name' => $club->owner ? $club->owner->full_name : 'N/A',
];
});
diff --git a/app/Http/Controllers/FamilyController.php b/app/Http/Controllers/FamilyController.php
index f8ef2b1..83761fb 100644
--- a/app/Http/Controllers/FamilyController.php
+++ b/app/Http/Controllers/FamilyController.php
@@ -31,9 +31,8 @@ class FamilyController extends Controller
->sortBy(function($relationship) {
return $relationship->dependent->full_name;
});
- $familyInvoices = $this->familyService->getFamilyInvoices($user->id);
- return view('family.dashboard', compact('user', 'dependents', 'familyInvoices'));
+ return view('family.dashboard', compact('user', 'dependents'));
}
/**
diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php
index fb3ddbf..4bf9dab 100644
--- a/app/Http/Controllers/InvoiceController.php
+++ b/app/Http/Controllers/InvoiceController.php
@@ -34,7 +34,7 @@ class InvoiceController extends Controller
* @param int $id
* @return \Illuminate\View\View
*/
- public function show($id)
+ public function show(Request $request, $id)
{
$user = Auth::user();
$invoice = Invoice::where('id', $id)
@@ -42,9 +42,30 @@ class InvoiceController extends Controller
->with(['student', 'tenant'])
->firstOrFail();
+ if ($request->ajax()) {
+ return response()->json(['html' => view('invoices._show_modal', compact('invoice'))->render()]);
+ }
+
return view('invoices.show', compact('invoice'));
}
+ /**
+ * Display the receipt for the specified invoice.
+ *
+ * @param int $id
+ * @return \Illuminate\View\View
+ */
+ public function receipt($id)
+ {
+ $user = Auth::user();
+ $invoice = Invoice::where('id', $id)
+ ->where('payer_user_id', $user->id)
+ ->with(['student', 'tenant'])
+ ->firstOrFail();
+
+ return view('invoices.receipt', compact('invoice'));
+ }
+
/**
* Process payment for the specified invoice.
*
@@ -63,7 +84,7 @@ class InvoiceController extends Controller
'status' => 'paid'
]);
- return redirect()->route('invoices.show', $invoice->id)
+ return redirect()->route('bills.show', $invoice->id)
->with('success', 'Payment processed successfully.');
}
@@ -86,7 +107,7 @@ class InvoiceController extends Controller
]);
}
- return redirect()->route('invoices.index')
+ return redirect()->route('bills.index')
->with('success', 'All payments processed successfully.');
}
}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 6b703d2..b28fd5a 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -3,6 +3,9 @@
namespace Database\Seeders;
use App\Models\User;
+use App\Models\Tenant;
+use App\Models\Membership;
+use App\Models\Invoice;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
@@ -17,11 +20,47 @@ class DatabaseSeeder extends Seeder
{
// User::factory(10)->create();
- User::factory()->create([
- 'name' => 'Test User',
+ $user = User::firstOrCreate([
'email' => 'test@example.com',
+ ], [
+ 'name' => 'Test User',
'full_name' => 'Test User',
'mobile' => ['code' => '+1', 'number' => '1234567890'],
+ 'password' => bcrypt('password'),
+ ]);
+
+ // Create a tenant (club)
+ $tenant = Tenant::create([
+ 'owner_user_id' => $user->id,
+ 'club_name' => 'Test Club',
+ 'slug' => 'test-club',
+ ]);
+
+ // Create membership
+ Membership::create([
+ 'user_id' => $user->id,
+ 'tenant_id' => $tenant->id,
+ 'status' => 'active',
+ ]);
+
+ // Create a paid invoice
+ Invoice::create([
+ 'tenant_id' => $tenant->id,
+ 'student_user_id' => $user->id,
+ 'payer_user_id' => $user->id,
+ 'amount' => 50.00,
+ 'status' => 'paid',
+ 'due_date' => now()->addDays(30),
+ ]);
+
+ // Create a pending invoice
+ Invoice::create([
+ 'tenant_id' => $tenant->id,
+ 'student_user_id' => $user->id,
+ 'payer_user_id' => $user->id,
+ 'amount' => 25.00,
+ 'status' => 'pending',
+ 'due_date' => now()->addDays(15),
]);
}
}
diff --git a/resources/views/clubs/explore.blade.php b/resources/views/clubs/explore.blade.php
index 16e5451..114f7ba 100644
--- a/resources/views/clubs/explore.blade.php
+++ b/resources/views/clubs/explore.blade.php
@@ -89,8 +89,8 @@
-
-
+
+
No Results Found
Try adjusting your search or location
@@ -284,6 +284,9 @@ document.addEventListener('DOMContentLoaded', function() {
startWatchingLocation();
}
+ // Initial load: fetch all clubs since 'all' is default
+ fetchAllClubs();
+
// Category buttons
document.querySelectorAll('.category-btn').forEach(btn => {
btn.addEventListener('click', function() {
@@ -295,7 +298,14 @@ document.addEventListener('DOMContentLoaded', function() {
this.classList.add('active', 'btn-primary');
currentCategory = this.dataset.category;
- filterClubs();
+ if (currentCategory === 'all') {
+ fetchAllClubs();
+ } else if (userLocation) {
+ fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
+ } else {
+ // If no location, show message or fetch all as fallback
+ fetchAllClubs();
+ }
});
});
@@ -324,7 +334,11 @@ document.addEventListener('DOMContentLoaded', function() {
mapModal.hide();
if (userLocation) {
- fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
+ if (currentCategory === 'all') {
+ fetchAllClubs();
+ } else {
+ fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
+ }
}
});
});
@@ -339,7 +353,11 @@ function startWatchingLocation() {
};
updateLocationDisplay(userLocation.latitude, userLocation.longitude);
- fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
+
+ // If current category is not 'all', fetch nearby clubs
+ if (currentCategory !== 'all') {
+ fetchNearbyClubs(userLocation.latitude, userLocation.longitude);
+ }
// Stop watching after first successful location
if (watchId) {
@@ -361,7 +379,10 @@ function startWatchingLocation() {
break;
}
showAlert(errorMessage, 'danger');
- document.getElementById('loadingSpinner').style.display = 'none';
+ // If location fails and category is not 'all', perhaps fetch all as fallback
+ if (currentCategory !== 'all') {
+ fetchAllClubs();
+ }
},
{
enableHighAccuracy: true,
@@ -454,19 +475,50 @@ function fetchNearbyClubs(lat, lng) {
});
}
+// Fetch all clubs
+function fetchAllClubs() {
+ document.getElementById('loadingSpinner').style.display = 'block';
+ document.getElementById('clubsGrid').style.display = 'none';
+
+ fetch(`{{ route('clubs.all') }}`, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-CSRF-TOKEN': '{{ csrf_token() }}'
+ }
+ })
+ .then(response => response.json())
+ .then(data => {
+ document.getElementById('loadingSpinner').style.display = 'none';
+ document.getElementById('clubsGrid').style.display = 'block';
+
+ if (data.success) {
+ allClubs = data.clubs;
+ displayClubs(allClubs);
+ } else {
+ showAlert('Failed to fetch clubs', 'danger');
+ }
+ })
+ .catch(error => {
+ console.error('Error:', error);
+ document.getElementById('loadingSpinner').style.display = 'none';
+ showAlert('Error fetching clubs', 'danger');
+ });
+}
+
// Display clubs as cards
function displayClubs(clubs) {
const container = document.getElementById('clubsContainer');
- const noResults = document.getElementById('noResults');
+ const noResultsContainer = document.getElementById('noResultsContainer');
container.innerHTML = '';
if (clubs.length === 0) {
- noResults.style.display = 'flex';
+ noResultsContainer.style.display = 'flex';
return;
}
- noResults.style.display = 'none';
+ noResultsContainer.style.display = 'none';
clubs.forEach(club => {
const card = document.createElement('div');
@@ -491,7 +543,7 @@ function displayClubs(clubs) {
-
${club.distance} km away
+
${club.distance ? club.distance + ' km away' : 'Location available'}
-
- Next Birthday
-
- {{ $user->birthdate->copy()->year(now()->year)->isFuture()
- ? $user->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
- : $user->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
-
-
+
+ Next Birthday
+
+ @if($user->birthdate)
+ {{ $user->birthdate->copy()->year(now()->year)->isFuture()
+ ? $user->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
+ : $user->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
+ @else
+ N/A
+ @endif
+
+
Member Since
{{ $user->created_at->format('d/m/Y') }}
@@ -245,9 +244,13 @@
Next Birthday
- {{ $relationship->dependent->birthdate->copy()->year(now()->year)->isFuture()
- ? $relationship->dependent->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
- : $relationship->dependent->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
+ @if($relationship->dependent->birthdate)
+ {{ $relationship->dependent->birthdate->copy()->year(now()->year)->isFuture()
+ ? $relationship->dependent->birthdate->copy()->year(now()->year)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE])
+ : $relationship->dependent->birthdate->copy()->year(now()->year + 1)->diffForHumans(['parts' => 2, 'syntax' => \Carbon\CarbonInterface::DIFF_ABSOLUTE]) }}
+ @else
+ N/A
+ @endif
@@ -283,68 +286,7 @@
-
-
-
-
-
-
-
-
- | Student Name |
- Class/Package |
- Amount |
- Status |
- Actions |
-
-
-
- @forelse($familyInvoices as $invoice)
-
- | {{ $invoice->student->full_name }} |
- {{ $invoice->tenant->club_name }} |
- ${{ number_format($invoice->amount, 2) }} |
-
- @if($invoice->status === 'paid')
- Paid
- @elseif($invoice->status === 'pending')
- Pending
- @else
- Overdue
- @endif
- |
-
-
- View
-
- @if($invoice->status !== 'paid')
-
- Pay
-
- @endif
- |
-
- @empty
-
- |
- No payments due at this time.
- |
-
- @endforelse
-
-
-
-
-
-
+
diff --git a/resources/views/invoices/_show_modal.blade.php b/resources/views/invoices/_show_modal.blade.php
new file mode 100644
index 0000000..ba4e840
--- /dev/null
+++ b/resources/views/invoices/_show_modal.blade.php
@@ -0,0 +1,83 @@
+@if(session('success'))
+
+ {{ session('success') }}
+
+
+@endif
+
+
+
+
Billed To
+
{{ Auth::user()->full_name }}
+
{{ Auth::user()->email }}
+ @if(Auth::user()->mobile)
+
{{ Auth::user()->mobile }}
+ @endif
+
+
+
Invoice Details
+
Invoice #: {{ $invoice->id }}
+
Due Date: {{ $invoice->due_date->format('F j, Y') }}
+
+ Status:
+ @if($invoice->status === 'paid')
+ Paid
+ @elseif($invoice->status === 'pending')
+ Pending
+ @else
+ Overdue
+ @endif
+
+
+
+
+
+
+
Club Information
+
{{ $invoice->tenant->club_name }}
+
{{ $invoice->tenant->owner->full_name }} (Owner)
+
+
+
Student Information
+
{{ $invoice->student->full_name }}
+
Age: {{ $invoice->student->age }} ({{ $invoice->student->life_stage }})
+
+
+
+
+
+
+
+ | Description |
+ Amount |
+
+
+
+
+ | Club Membership Fee - {{ $invoice->tenant->club_name }} |
+ ${{ number_format($invoice->amount, 2) }} |
+
+
+
+
+ | Total |
+ ${{ number_format($invoice->amount, 2) }} |
+
+
+
+
+
+
+ @if($invoice->status !== 'paid')
+
+ Pay Now
+
+ @else
+
+
+ View Receipt
+
+ @endif
+
diff --git a/resources/views/invoices/index.blade.php b/resources/views/invoices/index.blade.php
index e50cce1..46117a0 100644
--- a/resources/views/invoices/index.blade.php
+++ b/resources/views/invoices/index.blade.php
@@ -3,21 +3,21 @@
@section('content')
-
My Invoices
+ My Bills
@@ -62,13 +62,17 @@
{{ $invoice->due_date->format('M j, Y') }} |
|
@@ -80,18 +84,57 @@
@else
-
No Invoices Found
-
There are no invoices matching your criteria.
+
No Bills Found
+
There are no bills matching your criteria.
@endif
@if($invoices->where('status', '!=', 'paid')->count() > 0)
@endif
+
+
+
+
+
@endsection
diff --git a/resources/views/invoices/receipt.blade.php b/resources/views/invoices/receipt.blade.php
new file mode 100644
index 0000000..d59f578
--- /dev/null
+++ b/resources/views/invoices/receipt.blade.php
@@ -0,0 +1,429 @@
+
+
+
+
+
+
Receipt
+
+
+
+
+
+
+
+
+
+
+
RECEIPT TO :
+
+ {{ $invoice->student->full_name }}
+ {{ $invoice->student->mobile_formatted ?: 'N/A' }}
+ {{ $invoice->student->email ?: 'N/A' }}
+ {{ $invoice->student->addresses ? implode(', ', array_filter($invoice->student->addresses)) : 'N/A' }}
+
+
+
+
+ RECEIPT NO
+ {{ $invoice->id }}
+
+
+ ISSUE DATE
+ {{ $invoice->created_at->format('d-m-Y') }}
+
+
+
+
+
+
+
+ | Item No |
+ Description |
+ Qty |
+ Price |
+ Total |
+
+
+
+
+ | 1 |
+ Club Membership Fee - {{ $invoice->tenant->club_name }} |
+ 1 |
+ {{ number_format($invoice->amount, 2) }} |
+ |
+
+
+
+
+
+
Note: This payment covers the membership fee for {{ $invoice->student->full_name }} at {{ $invoice->tenant->club_name }}.
+
+
+
+
+
+ | Sub Total: |
+ |
+
+
+ | VAT (0%): |
+ |
+
+
+ | Grand Total: |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/views/invoices/show.blade.php b/resources/views/invoices/show.blade.php
index 3e533e3..a947968 100644
--- a/resources/views/invoices/show.blade.php
+++ b/resources/views/invoices/show.blade.php
@@ -7,8 +7,8 @@
@@ -83,13 +83,16 @@
@if($invoice->status !== 'paid')
-
+
Pay Now
@else
-
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index 00df68f..ed81154 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -391,6 +391,9 @@
My Family
+
+ My Bills
+
Settings
diff --git a/routes/web.php b/routes/web.php
index a989733..ebb0224 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -95,9 +95,10 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::post('/family/{id}/upload-picture', [FamilyController::class, 'uploadFamilyMemberPicture'])->name('family.upload-picture');
Route::delete('/family/{id}', [FamilyController::class, 'destroy'])->name('family.destroy');
- // Invoice routes
- Route::get('/invoices', [InvoiceController::class, 'index'])->name('invoices.index');
- Route::get('/invoices/{id}', [InvoiceController::class, 'show'])->name('invoices.show');
- Route::get('/invoices/{id}/pay', [InvoiceController::class, 'pay'])->name('invoices.pay');
- Route::get('/invoices/pay-all', [InvoiceController::class, 'payAll'])->name('invoices.pay-all');
+ // Bills routes
+ Route::get('/bills', [InvoiceController::class, 'index'])->name('bills.index');
+ Route::get('/bills/{id}', [InvoiceController::class, 'show'])->name('bills.show');
+ Route::get('/bills/{id}/receipt', [InvoiceController::class, 'receipt'])->name('bills.receipt');
+ Route::get('/bills/{id}/pay', [InvoiceController::class, 'pay'])->name('bills.pay');
+ Route::get('/bills/pay-all', [InvoiceController::class, 'payAll'])->name('bills.pay-all');
});