created admin panel with some of its content
This commit is contained in:
1 parent
2ee4b76599
commit
b16351c416
52 files changed
+5690
-203
No files matched your search
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\Role;
|
||||
|
||||
class MakeSuperAdmin extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'admin:make-super {email : The email of the user to make super admin}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Make a user a super admin';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$email = $this->argument('email');
|
||||
|
||||
$user = User::where('email', $email)->first();
|
||||
|
||||
if (!$user) {
|
||||
$this->error("User with email '{$email}' not found!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if user already has super-admin role
|
||||
if ($user->hasRole('super-admin')) {
|
||||
$this->info("User '{$user->full_name}' ({$email}) is already a super admin!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Assign super-admin role
|
||||
$user->assignRole('super-admin');
|
||||
|
||||
$this->info("✅ Successfully made '{$user->full_name}' ({$email}) a super admin!");
|
||||
$this->info("They can now access the admin panel at: " . url('/admin'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PlatformController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the platform admin dashboard.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// Get all clubs with counts
|
||||
$clubs = Tenant::with(['owner'])
|
||||
->withCount(['members', 'packages', 'instructors'])
|
||||
->latest()
|
||||
->get();
|
||||
|
||||
$clubsCount = $clubs->count();
|
||||
|
||||
return view('admin.platform.index', compact('clubs', 'clubsCount'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display all clubs management page.
|
||||
*/
|
||||
public function clubs(Request $request)
|
||||
{
|
||||
$search = $request->input('search');
|
||||
|
||||
$clubs = Tenant::with(['owner', 'members'])
|
||||
->withCount(['members', 'packages', 'instructors'])
|
||||
->when($search, function ($query, $search) {
|
||||
$query->where('club_name', 'like', "%{$search}%")
|
||||
->orWhere('address', 'like', "%{$search}%")
|
||||
->orWhere('description', 'like', "%{$search}%");
|
||||
})
|
||||
->latest()
|
||||
->paginate(12);
|
||||
|
||||
return view('admin.platform.clubs', compact('clubs', 'search'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display all members management page.
|
||||
*/
|
||||
public function members(Request $request)
|
||||
{
|
||||
$search = $request->input('search');
|
||||
|
||||
$members = User::with(['memberClubs', 'dependents'])
|
||||
->withCount('memberClubs')
|
||||
->when($search, function ($query, $search) {
|
||||
$query->where('full_name', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%")
|
||||
->orWhere('nationality', 'like', "%{$search}%")
|
||||
->orWhereRaw("JSON_EXTRACT(mobile, '$.number') LIKE ?", ["%{$search}%"]);
|
||||
})
|
||||
->latest()
|
||||
->paginate(20);
|
||||
|
||||
return view('admin.platform.members', compact('members', 'search'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show create club form.
|
||||
*/
|
||||
public function createClub()
|
||||
{
|
||||
$users = User::orderBy('full_name')->get();
|
||||
return view('admin.platform.create-club', compact('users'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new club.
|
||||
*/
|
||||
public function storeClub(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'owner_user_id' => 'required|exists:users,id',
|
||||
'club_name' => 'required|string|max:255',
|
||||
'slug' => 'required|string|max:255|unique:tenants,slug',
|
||||
'email' => 'nullable|email',
|
||||
'phone_code' => 'nullable|string',
|
||||
'phone_number' => 'nullable|string',
|
||||
'currency' => 'nullable|string|max:3',
|
||||
'timezone' => 'nullable|string',
|
||||
'country' => 'nullable|string',
|
||||
'address' => 'nullable|string',
|
||||
'gps_lat' => 'nullable|numeric|between:-90,90',
|
||||
'gps_long' => 'nullable|numeric|between:-180,180',
|
||||
'logo' => 'nullable|image|max:2048',
|
||||
'cover_image' => 'nullable|image|max:2048',
|
||||
]);
|
||||
|
||||
// Handle phone as JSON
|
||||
if ($request->filled('phone_code') && $request->filled('phone_number')) {
|
||||
$validated['phone'] = [
|
||||
'code' => $request->phone_code,
|
||||
'number' => $request->phone_number,
|
||||
];
|
||||
}
|
||||
|
||||
// Handle logo upload
|
||||
if ($request->hasFile('logo')) {
|
||||
$validated['logo'] = $request->file('logo')->store('clubs/logos', 'public');
|
||||
}
|
||||
|
||||
// Handle cover image upload
|
||||
if ($request->hasFile('cover_image')) {
|
||||
$validated['cover_image'] = $request->file('cover_image')->store('clubs/covers', 'public');
|
||||
}
|
||||
|
||||
$club = Tenant::create($validated);
|
||||
|
||||
// Assign club-admin role to owner
|
||||
$owner = User::find($validated['owner_user_id']);
|
||||
$owner->assignRole('club-admin', $club->id);
|
||||
|
||||
return redirect()->route('admin.platform.clubs')
|
||||
->with('success', 'Club created successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show edit club form.
|
||||
*/
|
||||
public function editClub(Tenant $club)
|
||||
{
|
||||
$users = User::orderBy('full_name')->get();
|
||||
return view('admin.platform.edit-club', compact('club', 'users'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a club.
|
||||
*/
|
||||
public function updateClub(Request $request, Tenant $club)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'owner_user_id' => 'required|exists:users,id',
|
||||
'club_name' => 'required|string|max:255',
|
||||
'slug' => 'required|string|max:255|unique:tenants,slug,' . $club->id,
|
||||
'email' => 'nullable|email',
|
||||
'phone_code' => 'nullable|string',
|
||||
'phone_number' => 'nullable|string',
|
||||
'currency' => 'nullable|string|max:3',
|
||||
'timezone' => 'nullable|string',
|
||||
'country' => 'nullable|string',
|
||||
'address' => 'nullable|string',
|
||||
'gps_lat' => 'nullable|numeric|between:-90,90',
|
||||
'gps_long' => 'nullable|numeric|between:-180,180',
|
||||
'logo' => 'nullable|image|max:2048',
|
||||
'cover_image' => 'nullable|image|max:2048',
|
||||
]);
|
||||
|
||||
// Handle phone as JSON
|
||||
if ($request->filled('phone_code') && $request->filled('phone_number')) {
|
||||
$validated['phone'] = [
|
||||
'code' => $request->phone_code,
|
||||
'number' => $request->phone_number,
|
||||
];
|
||||
}
|
||||
|
||||
// Handle logo upload
|
||||
if ($request->hasFile('logo')) {
|
||||
// Delete old logo
|
||||
if ($club->logo) {
|
||||
Storage::disk('public')->delete($club->logo);
|
||||
}
|
||||
$validated['logo'] = $request->file('logo')->store('clubs/logos', 'public');
|
||||
}
|
||||
|
||||
// Handle cover image upload
|
||||
if ($request->hasFile('cover_image')) {
|
||||
// Delete old cover
|
||||
if ($club->cover_image) {
|
||||
Storage::disk('public')->delete($club->cover_image);
|
||||
}
|
||||
$validated['cover_image'] = $request->file('cover_image')->store('clubs/covers', 'public');
|
||||
}
|
||||
|
||||
$club->update($validated);
|
||||
|
||||
return redirect()->route('admin.platform.clubs')
|
||||
->with('success', 'Club updated successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a club.
|
||||
*/
|
||||
public function destroyClub(Tenant $club)
|
||||
{
|
||||
// Delete associated files
|
||||
if ($club->logo) {
|
||||
Storage::disk('public')->delete($club->logo);
|
||||
}
|
||||
if ($club->cover_image) {
|
||||
Storage::disk('public')->delete($club->cover_image);
|
||||
}
|
||||
if ($club->favicon) {
|
||||
Storage::disk('public')->delete($club->favicon);
|
||||
}
|
||||
|
||||
// Delete club (cascade will handle related records)
|
||||
$club->delete();
|
||||
|
||||
return redirect()->route('admin.platform.clubs')
|
||||
->with('success', 'Club deleted successfully!');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display database backup page.
|
||||
*/
|
||||
public function backup()
|
||||
{
|
||||
return view('admin.platform.backup');
|
||||
}
|
||||
|
||||
/**
|
||||
* Download database backup as JSON.
|
||||
*/
|
||||
public function downloadBackup()
|
||||
{
|
||||
$tables = [
|
||||
'users',
|
||||
'user_relationships',
|
||||
'tenants',
|
||||
'memberships',
|
||||
'invoices',
|
||||
'roles',
|
||||
'permissions',
|
||||
'role_permission',
|
||||
'user_roles',
|
||||
'club_facilities',
|
||||
'club_instructors',
|
||||
'club_activities',
|
||||
'club_packages',
|
||||
'club_package_activities',
|
||||
'club_member_subscriptions',
|
||||
'club_transactions',
|
||||
'club_gallery_images',
|
||||
'club_social_links',
|
||||
'club_bank_accounts',
|
||||
'club_messages',
|
||||
'club_reviews',
|
||||
'health_records',
|
||||
'tournament_events',
|
||||
'performance_results',
|
||||
'goals',
|
||||
'attendance_records',
|
||||
'club_affiliations',
|
||||
'skill_acquisitions',
|
||||
];
|
||||
|
||||
$backup = [];
|
||||
foreach ($tables as $table) {
|
||||
$backup[$table] = DB::table($table)->get()->toArray();
|
||||
}
|
||||
|
||||
$filename = 'takeone_backup_' . date('Y-m-d_H-i-s') . '.json';
|
||||
|
||||
return response()->json($backup, 200, [
|
||||
'Content-Type' => 'application/json',
|
||||
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore database from JSON backup.
|
||||
*/
|
||||
public function restoreBackup(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'backup_file' => 'required|file|mimes:json',
|
||||
]);
|
||||
|
||||
$file = $request->file('backup_file');
|
||||
$content = file_get_contents($file->getRealPath());
|
||||
$backup = json_decode($content, true);
|
||||
|
||||
if (!$backup) {
|
||||
return back()->with('error', 'Invalid backup file format.');
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// Disable foreign key checks
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=0');
|
||||
|
||||
foreach ($backup as $table => $records) {
|
||||
// Truncate table
|
||||
DB::table($table)->truncate();
|
||||
|
||||
// Insert records in chunks
|
||||
if (!empty($records)) {
|
||||
$chunks = array_chunk($records, 100);
|
||||
foreach ($chunks as $chunk) {
|
||||
DB::table($table)->insert($chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable foreign key checks
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1');
|
||||
|
||||
DB::commit();
|
||||
|
||||
return back()->with('success', 'Database restored successfully!');
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS=1');
|
||||
|
||||
return back()->with('error', 'Restore failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all authentication users.
|
||||
*/
|
||||
public function exportAuthUsers()
|
||||
{
|
||||
$users = User::select('id', 'full_name', 'email', 'password', 'created_at')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$filename = 'auth_users_' . date('Y-m-d_H-i-s') . '.json';
|
||||
|
||||
return response()->json($users, 200, [
|
||||
'Content-Type' => 'application/json',
|
||||
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -104,12 +104,30 @@ class ClubController extends Controller
|
||||
|
||||
/**
|
||||
* Get all clubs for the map view.
|
||||
* If latitude and longitude are provided, calculate distance and sort by nearest.
|
||||
*/
|
||||
public function all()
|
||||
public function all(Request $request)
|
||||
{
|
||||
$clubs = Tenant::with('owner')
|
||||
->get()
|
||||
->map(function ($club) {
|
||||
$userLat = $request->input('latitude');
|
||||
$userLng = $request->input('longitude');
|
||||
|
||||
$clubs = Tenant::with('owner')->get();
|
||||
|
||||
// If user location is provided, calculate distance for each club
|
||||
if ($userLat !== null && $userLng !== null) {
|
||||
$clubsWithDistance = $clubs->map(function ($club) use ($userLat, $userLng) {
|
||||
$distance = null;
|
||||
|
||||
// Calculate distance only if club has GPS coordinates
|
||||
if ($club->gps_lat && $club->gps_long) {
|
||||
$distance = $this->calculateDistance(
|
||||
$userLat,
|
||||
$userLng,
|
||||
$club->gps_lat,
|
||||
$club->gps_long
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $club->id,
|
||||
'club_name' => $club->club_name,
|
||||
@@ -117,14 +135,57 @@ class ClubController extends Controller
|
||||
'logo' => $club->logo,
|
||||
'gps_lat' => $club->gps_lat ? (float) $club->gps_lat : null,
|
||||
'gps_long' => $club->gps_long ? (float) $club->gps_long : null,
|
||||
'distance' => $distance !== null ? round($distance, 2) : null,
|
||||
'owner_name' => $club->owner ? $club->owner->full_name : 'N/A',
|
||||
];
|
||||
});
|
||||
|
||||
// Sort by distance (nearest first), clubs without GPS coordinates go to the end
|
||||
$clubsWithDistance = $clubsWithDistance->sort(function ($a, $b) {
|
||||
// If both have distance, sort by distance
|
||||
if ($a['distance'] !== null && $b['distance'] !== null) {
|
||||
return $a['distance'] <=> $b['distance'];
|
||||
}
|
||||
// If only one has distance, prioritize it
|
||||
if ($a['distance'] !== null) {
|
||||
return -1;
|
||||
}
|
||||
if ($b['distance'] !== null) {
|
||||
return 1;
|
||||
}
|
||||
// If neither has distance, maintain original order
|
||||
return 0;
|
||||
})->values();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'clubs' => $clubsWithDistance,
|
||||
'total' => $clubsWithDistance->count(),
|
||||
'user_location' => [
|
||||
'latitude' => $userLat,
|
||||
'longitude' => $userLng,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// If no location provided, return clubs without distance calculation
|
||||
$clubsData = $clubs->map(function ($club) {
|
||||
return [
|
||||
'id' => $club->id,
|
||||
'club_name' => $club->club_name,
|
||||
'slug' => $club->slug,
|
||||
'logo' => $club->logo,
|
||||
'gps_lat' => $club->gps_lat ? (float) $club->gps_lat : null,
|
||||
'gps_long' => $club->gps_long ? (float) $club->gps_long : null,
|
||||
'distance' => null,
|
||||
'owner_name' => $club->owner ? $club->owner->full_name : 'N/A',
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'clubs' => $clubs,
|
||||
'total' => $clubs->count(),
|
||||
'clubs' => $clubsData,
|
||||
'total' => $clubsData->count(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CheckPermission
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, string $permission): Response
|
||||
{
|
||||
if (!auth()->check()) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
$user = auth()->user();
|
||||
$tenantId = $request->route('tenant') ?? $request->route('slug')
|
||||
? \App\Models\Tenant::where('slug', $request->route('slug'))->value('id')
|
||||
: null;
|
||||
|
||||
// Check if user has the required permission
|
||||
if ($user->hasPermission($permission, $tenantId)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// If user doesn't have required permission, abort with 403
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CheckRole
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, string ...$roles): Response
|
||||
{
|
||||
if (!auth()->check()) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
$user = auth()->user();
|
||||
$tenantId = $request->route('tenant') ?? $request->route('slug')
|
||||
? \App\Models\Tenant::where('slug', $request->route('slug'))->value('id')
|
||||
: null;
|
||||
|
||||
// Check if user has any of the required roles
|
||||
if ($user->hasAnyRole($roles, $tenantId)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// If user doesn't have required role, abort with 403
|
||||
abort(403, 'Unauthorized action.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class ClubActivity extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_activities';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'name',
|
||||
'duration_minutes',
|
||||
'frequency_per_week',
|
||||
'facility_id',
|
||||
'schedule',
|
||||
'description',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'duration_minutes' => 'integer',
|
||||
'frequency_per_week' => 'integer',
|
||||
'schedule' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the activity.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facility where the activity takes place.
|
||||
*/
|
||||
public function facility(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ClubFacility::class, 'facility_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the packages that include this activity.
|
||||
*/
|
||||
public function packages(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(ClubPackage::class, 'club_package_activities', 'activity_id', 'package_id')
|
||||
->withPivot('instructor_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class ClubBankAccount extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_bank_accounts';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'bank_name',
|
||||
'account_name',
|
||||
'account_number',
|
||||
'iban',
|
||||
'swift_code',
|
||||
'is_primary',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_primary' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the bank account.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt and decrypt account_number.
|
||||
*/
|
||||
protected function accountNumber(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value ? Crypt::decryptString($value) : null,
|
||||
set: fn ($value) => $value ? Crypt::encryptString($value) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt and decrypt iban.
|
||||
*/
|
||||
protected function iban(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value ? Crypt::decryptString($value) : null,
|
||||
set: fn ($value) => $value ? Crypt::encryptString($value) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt and decrypt swift_code.
|
||||
*/
|
||||
protected function swiftCode(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn ($value) => $value ? Crypt::decryptString($value) : null,
|
||||
set: fn ($value) => $value ? Crypt::encryptString($value) : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get masked account number for display.
|
||||
*/
|
||||
public function getMaskedAccountNumber(): string
|
||||
{
|
||||
$accountNumber = $this->account_number;
|
||||
if (!$accountNumber) {
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
$length = strlen($accountNumber);
|
||||
if ($length <= 4) {
|
||||
return str_repeat('*', $length);
|
||||
}
|
||||
|
||||
return str_repeat('*', $length - 4) . substr($accountNumber, -4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class ClubFacility extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_facilities';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'name',
|
||||
'photo',
|
||||
'address',
|
||||
'gps_lat',
|
||||
'gps_long',
|
||||
'is_available',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'gps_lat' => 'decimal:7',
|
||||
'gps_long' => 'decimal:7',
|
||||
'is_available' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the facility.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activities for the facility.
|
||||
*/
|
||||
public function activities(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubActivity::class, 'facility_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ClubGalleryImage extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_gallery_images';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'image_path',
|
||||
'caption',
|
||||
'uploaded_by',
|
||||
'display_order',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'display_order' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the image.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who uploaded the image.
|
||||
*/
|
||||
public function uploader(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'uploaded_by');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class ClubInstructor extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_instructors';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'user_id',
|
||||
'role',
|
||||
'experience_years',
|
||||
'rating',
|
||||
'skills',
|
||||
'bio',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'experience_years' => 'integer',
|
||||
'rating' => 'decimal:2',
|
||||
'skills' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the instructor.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user associated with the instructor.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the packages that include this instructor.
|
||||
*/
|
||||
public function packages(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(ClubPackage::class, 'club_package_activities', 'instructor_id', 'package_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ClubMemberSubscription extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_member_subscriptions';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'user_id',
|
||||
'package_id',
|
||||
'start_date',
|
||||
'end_date',
|
||||
'status',
|
||||
'payment_status',
|
||||
'amount_paid',
|
||||
'amount_due',
|
||||
'notes',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'start_date' => 'date',
|
||||
'end_date' => 'date',
|
||||
'amount_paid' => 'decimal:2',
|
||||
'amount_due' => 'decimal:2',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the subscription.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the member associated with the subscription.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package associated with the subscription.
|
||||
*/
|
||||
public function package(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ClubPackage::class, 'package_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transactions for the subscription.
|
||||
*/
|
||||
public function transactions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubTransaction::class, 'subscription_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if subscription is expiring soon (within 3 days).
|
||||
*/
|
||||
public function isExpiringSoon(): bool
|
||||
{
|
||||
if ($this->status !== 'active') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$daysUntilExpiry = Carbon::now()->diffInDays($this->end_date, false);
|
||||
return $daysUntilExpiry >= 0 && $daysUntilExpiry <= 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if subscription is expired.
|
||||
*/
|
||||
public function isExpired(): bool
|
||||
{
|
||||
return $this->end_date < Carbon::now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get days until expiry.
|
||||
*/
|
||||
public function daysUntilExpiry(): int
|
||||
{
|
||||
return Carbon::now()->diffInDays($this->end_date, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ClubMessage extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_messages';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'sender_id',
|
||||
'recipient_id',
|
||||
'subject',
|
||||
'message',
|
||||
'is_read',
|
||||
'read_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_read' => 'boolean',
|
||||
'read_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the message.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sender of the message.
|
||||
*/
|
||||
public function sender(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'sender_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recipient of the message.
|
||||
*/
|
||||
public function recipient(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'recipient_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark message as read.
|
||||
*/
|
||||
public function markAsRead(): void
|
||||
{
|
||||
$this->update([
|
||||
'is_read' => true,
|
||||
'read_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include unread messages.
|
||||
*/
|
||||
public function scopeUnread($query)
|
||||
{
|
||||
return $query->where('is_read', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include read messages.
|
||||
*/
|
||||
public function scopeRead($query)
|
||||
{
|
||||
return $query->where('is_read', true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class ClubPackage extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_packages';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'name',
|
||||
'cover_image',
|
||||
'type',
|
||||
'age_min',
|
||||
'age_max',
|
||||
'gender',
|
||||
'price',
|
||||
'duration_months',
|
||||
'session_count',
|
||||
'description',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'age_min' => 'integer',
|
||||
'age_max' => 'integer',
|
||||
'price' => 'decimal:2',
|
||||
'duration_months' => 'integer',
|
||||
'session_count' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the package.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activities included in the package.
|
||||
*/
|
||||
public function activities(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(ClubActivity::class, 'club_package_activities', 'package_id', 'activity_id')
|
||||
->withPivot('instructor_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subscriptions for the package.
|
||||
*/
|
||||
public function subscriptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubMemberSubscription::class, 'package_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active subscriptions for the package.
|
||||
*/
|
||||
public function activeSubscriptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubMemberSubscription::class, 'package_id')
|
||||
->where('status', 'active');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ClubReview extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_reviews';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'user_id',
|
||||
'rating',
|
||||
'comment',
|
||||
'is_approved',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'rating' => 'integer',
|
||||
'is_approved' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the review.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who wrote the review.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include approved reviews.
|
||||
*/
|
||||
public function scopeApproved($query)
|
||||
{
|
||||
return $query->where('is_approved', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include pending reviews.
|
||||
*/
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->where('is_approved', false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ClubSocialLink extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_social_links';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'platform',
|
||||
'url',
|
||||
'icon',
|
||||
'display_order',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'display_order' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the social link.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ClubTransaction extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'club_transactions';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'tenant_id',
|
||||
'user_id',
|
||||
'type',
|
||||
'category',
|
||||
'amount',
|
||||
'payment_method',
|
||||
'description',
|
||||
'transaction_date',
|
||||
'reference_number',
|
||||
'subscription_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'amount' => 'decimal:2',
|
||||
'transaction_date' => 'date',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the club that owns the transaction.
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Tenant::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user associated with the transaction.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subscription associated with the transaction.
|
||||
*/
|
||||
public function subscription(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ClubMemberSubscription::class, 'subscription_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include income transactions.
|
||||
*/
|
||||
public function scopeIncome($query)
|
||||
{
|
||||
return $query->where('type', 'income');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include expense transactions.
|
||||
*/
|
||||
public function scopeExpense($query)
|
||||
{
|
||||
return $query->where('type', 'expense');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include refund transactions.
|
||||
*/
|
||||
public function scopeRefund($query)
|
||||
{
|
||||
return $query->where('type', 'refund');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Permission extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'slug',
|
||||
'description',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the roles for the permission.
|
||||
*/
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Role::class, 'role_permission')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class Role extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'slug',
|
||||
'description',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the permissions for the role.
|
||||
*/
|
||||
public function permissions(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Permission::class, 'role_permission')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the users for the role.
|
||||
*/
|
||||
public function users(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'user_roles')
|
||||
->withPivot('tenant_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if role has a specific permission.
|
||||
*/
|
||||
public function hasPermission(string $permissionSlug): bool
|
||||
{
|
||||
return $this->permissions()->where('slug', $permissionSlug)->exists();
|
||||
}
|
||||
}
|
||||
+143
-1
@@ -7,10 +7,11 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Tenant extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -22,8 +23,25 @@ class Tenant extends Model
|
||||
'club_name',
|
||||
'slug',
|
||||
'logo',
|
||||
'slogan',
|
||||
'description',
|
||||
'enrollment_fee',
|
||||
'commercial_reg_number',
|
||||
'vat_reg_number',
|
||||
'vat_percentage',
|
||||
'email',
|
||||
'phone',
|
||||
'currency',
|
||||
'timezone',
|
||||
'country',
|
||||
'address',
|
||||
'favicon',
|
||||
'cover_image',
|
||||
'owner_name',
|
||||
'owner_email',
|
||||
'gps_lat',
|
||||
'gps_long',
|
||||
'settings',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -34,6 +52,10 @@ class Tenant extends Model
|
||||
protected $casts = [
|
||||
'gps_lat' => 'decimal:7',
|
||||
'gps_long' => 'decimal:7',
|
||||
'enrollment_fee' => 'decimal:2',
|
||||
'vat_percentage' => 'decimal:2',
|
||||
'phone' => 'array',
|
||||
'settings' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -61,4 +83,124 @@ class Tenant extends Model
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the facilities for the club.
|
||||
*/
|
||||
public function facilities(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubFacility::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instructors for the club.
|
||||
*/
|
||||
public function instructors(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubInstructor::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activities for the club.
|
||||
*/
|
||||
public function activities(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubActivity::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the packages for the club.
|
||||
*/
|
||||
public function packages(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubPackage::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subscriptions for the club.
|
||||
*/
|
||||
public function subscriptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubMemberSubscription::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transactions for the club.
|
||||
*/
|
||||
public function transactions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubTransaction::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gallery images for the club.
|
||||
*/
|
||||
public function galleryImages(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubGalleryImage::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the social links for the club.
|
||||
*/
|
||||
public function socialLinks(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubSocialLink::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bank accounts for the club.
|
||||
*/
|
||||
public function bankAccounts(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubBankAccount::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the messages for the club.
|
||||
*/
|
||||
public function messages(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubMessage::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reviews for the club.
|
||||
*/
|
||||
public function reviews(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubReview::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get approved reviews for the club.
|
||||
*/
|
||||
public function approvedReviews(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubReview::class)->where('is_approved', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the average rating for the club.
|
||||
*/
|
||||
public function getAverageRatingAttribute(): float
|
||||
{
|
||||
return $this->approvedReviews()->avg('rating') ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active members count.
|
||||
*/
|
||||
public function getActiveMembersCountAttribute(): int
|
||||
{
|
||||
return $this->subscriptions()->where('status', 'active')->distinct('user_id')->count('user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the club URL.
|
||||
*/
|
||||
public function getUrlAttribute(): string
|
||||
{
|
||||
return url("/club/{$this->slug}");
|
||||
}
|
||||
}
|
||||
@@ -258,6 +258,154 @@ class User extends Authenticatable
|
||||
return $this->hasMany(ClubAffiliation::class, 'member_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the roles for the user.
|
||||
*/
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Role::class, 'user_roles')
|
||||
->withPivot('tenant_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subscriptions for the user.
|
||||
*/
|
||||
public function subscriptions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubMemberSubscription::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the transactions for the user.
|
||||
*/
|
||||
public function transactions(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubTransaction::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sent messages for the user.
|
||||
*/
|
||||
public function sentMessages(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubMessage::class, 'sender_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the received messages for the user.
|
||||
*/
|
||||
public function receivedMessages(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubMessage::class, 'recipient_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reviews written by the user.
|
||||
*/
|
||||
public function reviews(): HasMany
|
||||
{
|
||||
return $this->hasMany(ClubReview::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has a specific role.
|
||||
*/
|
||||
public function hasRole(string $roleSlug, ?int $tenantId = null): bool
|
||||
{
|
||||
$query = $this->roles()->where('slug', $roleSlug);
|
||||
|
||||
if ($tenantId !== null) {
|
||||
$query->wherePivot('tenant_id', $tenantId);
|
||||
}
|
||||
|
||||
return $query->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has any of the given roles.
|
||||
*/
|
||||
public function hasAnyRole(array $roleSlugs, ?int $tenantId = null): bool
|
||||
{
|
||||
$query = $this->roles()->whereIn('slug', $roleSlugs);
|
||||
|
||||
if ($tenantId !== null) {
|
||||
$query->wherePivot('tenant_id', $tenantId);
|
||||
}
|
||||
|
||||
return $query->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has a specific permission.
|
||||
*/
|
||||
public function hasPermission(string $permissionSlug, ?int $tenantId = null): bool
|
||||
{
|
||||
$roles = $tenantId !== null
|
||||
? $this->roles()->wherePivot('tenant_id', $tenantId)->get()
|
||||
: $this->roles;
|
||||
|
||||
foreach ($roles as $role) {
|
||||
if ($role->hasPermission($permissionSlug)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is super admin.
|
||||
*/
|
||||
public function isSuperAdmin(): bool
|
||||
{
|
||||
return $this->hasRole('super-admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is club admin for a specific club.
|
||||
*/
|
||||
public function isClubAdmin(?int $tenantId = null): bool
|
||||
{
|
||||
return $this->hasRole('club-admin', $tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is instructor for a specific club.
|
||||
*/
|
||||
public function isInstructor(?int $tenantId = null): bool
|
||||
{
|
||||
return $this->hasRole('instructor', $tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a role to the user.
|
||||
*/
|
||||
public function assignRole(string $roleSlug, ?int $tenantId = null): void
|
||||
{
|
||||
$role = Role::where('slug', $roleSlug)->firstOrFail();
|
||||
|
||||
$this->roles()->attach($role->id, ['tenant_id' => $tenantId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a role from the user.
|
||||
*/
|
||||
public function removeRole(string $roleSlug, ?int $tenantId = null): void
|
||||
{
|
||||
$role = Role::where('slug', $roleSlug)->first();
|
||||
|
||||
if (!$role) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($tenantId !== null) {
|
||||
$this->roles()->wherePivot('tenant_id', $tenantId)->detach($role->id);
|
||||
} else {
|
||||
$this->roles()->detach($role->id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the email verification notification.
|
||||
* Override to prevent sending the default Laravel notification.
|
||||
|
||||
Reference in new issue
Block a user