orderBy('full_name') ->get() ->map(function ($user) { return [ 'id' => $user->id, 'full_name' => $user->full_name, 'email' => $user->email, 'mobile' => $user->mobile_formatted, 'profile_picture' => $user->profile_picture ? asset('storage/' . $user->profile_picture) : null, ]; }); return response()->json($users); } /** * Get club data for editing. */ public function getClub($id) { $club = Tenant::with(['owner', 'socialLinks', 'bankAccounts']) ->findOrFail($id); return response()->json([ 'id' => $club->id, 'owner_user_id' => $club->owner_user_id, 'club_name' => $club->club_name, 'slug' => $club->slug, 'logo' => $club->logo, 'cover_image' => $club->cover_image, 'slogan' => $club->slogan, 'description' => $club->description, 'established_date' => $club->established_date, 'commercial_reg_number' => $club->commercial_reg_number, 'vat_reg_number' => $club->vat_reg_number, 'vat_percentage' => $club->vat_percentage, 'email' => $club->email, 'phone' => $club->phone, 'currency' => $club->currency, 'timezone' => $club->timezone, 'country' => $club->country, 'address' => $club->address, 'gps_lat' => $club->gps_lat, 'gps_long' => $club->gps_long, 'enrollment_fee' => $club->enrollment_fee, 'status' => $club->status ?? 'active', 'public_profile_enabled' => $club->public_profile_enabled ?? true, 'owner' => $club->owner ? [ 'id' => $club->owner->id, 'full_name' => $club->owner->full_name, 'email' => $club->owner->email, 'mobile' => $club->owner->mobile_formatted, 'profile_picture' => $club->owner->profile_picture ? asset('storage/' . $club->owner->profile_picture) : null, ] : null, 'social_links' => $club->socialLinks->map(function ($link) { return [ 'platform' => $link->platform, 'url' => $link->url, ]; }), 'bank_accounts' => $club->bankAccounts->map(function ($account) { return [ 'bank_name' => $account->bank_name, 'account_name' => $account->account_name, 'account_number' => $account->account_number, 'iban' => $account->iban, 'swift_code' => $account->swift_code, 'benefitpay_account' => $account->benefitpay_account ?? '', ]; }), ]); } /** * Check if slug is available. */ public function checkSlug(Request $request) { $slug = $request->input('slug'); $clubId = $request->input('club_id'); $query = Tenant::where('slug', $slug); if ($clubId) { $query->where('id', '!=', $clubId); } $exists = $query->exists(); return response()->json([ 'available' => !$exists, 'message' => $exists ? 'This slug is already taken' : 'Slug is available' ]); } /** * Store a new club. */ public function store(Request $request) { $validator = Validator::make($request->all(), [ 'owner_user_id' => 'required|exists:users,id', 'club_name' => 'required|string|max:255', 'slug' => 'required|string|max:255|unique:tenants,slug', 'slogan' => 'nullable|string|max:100', 'description' => 'nullable|string|max:1000', 'established_date' => 'nullable|date', 'commercial_reg_number' => 'nullable|string|max:100', 'vat_reg_number' => 'nullable|string|max:100', 'vat_percentage' => 'nullable|numeric|min:0|max:100', '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', 'enrollment_fee' => 'nullable|numeric|min:0', 'club_status' => 'nullable|in:active,inactive,pending', 'public_profile_enabled' => 'nullable|boolean', 'logo' => 'nullable', 'cover_image' => 'nullable', 'social_links' => 'nullable|array', 'social_links.*.platform' => 'required_with:social_links.*.url|string', 'social_links.*.url' => 'required_with:social_links.*.platform|url', 'bank_accounts' => 'nullable|array', 'bank_accounts.*.bank_name' => 'required_with:bank_accounts|string', 'bank_accounts.*.account_name' => 'required_with:bank_accounts|string', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation failed', 'errors' => $validator->errors() ], 422); } DB::beginTransaction(); try { $data = $validator->validated(); // Handle phone as JSON if ($request->filled('phone_code') && $request->filled('phone_number')) { $data['phone'] = [ 'code' => $request->phone_code, 'number' => $request->phone_number, ]; } // Handle logo upload if ($request->filled('logo') && str_starts_with($request->logo, 'data:image')) { $data['logo'] = $this->handleBase64Image($request->logo, 'clubs/logos', 'logo_' . time()); } // Handle cover image upload if ($request->filled('cover_image') && str_starts_with($request->cover_image, 'data:image')) { $data['cover_image'] = $this->handleBase64Image($request->cover_image, 'clubs/covers', 'cover_' . time()); } // Set status $data['status'] = $request->input('club_status', 'active'); $data['public_profile_enabled'] = $request->boolean('public_profile_enabled', true); // Create club $club = Tenant::create($data); // Handle social links if ($request->has('social_links')) { foreach ($request->social_links as $index => $link) { if (!empty($link['platform']) && !empty($link['url'])) { ClubSocialLink::create([ 'tenant_id' => $club->id, 'platform' => $link['platform'], 'url' => $link['url'], 'display_order' => $index, ]); } } } // Handle bank accounts if ($request->has('bank_accounts')) { foreach ($request->bank_accounts as $account) { if (!empty($account['bank_name']) && !empty($account['account_name'])) { ClubBankAccount::create([ 'tenant_id' => $club->id, 'bank_name' => $account['bank_name'], 'account_name' => $account['account_name'], 'account_number' => $account['account_number'] ?? null, 'iban' => $account['iban'] ?? null, 'swift_code' => $account['swift_code'] ?? null, 'benefitpay_account' => $account['benefitpay_account'] ?? null, ]); } } } // Assign club-admin role to owner $owner = User::find($data['owner_user_id']); $owner->assignRole('club-admin', $club->id); DB::commit(); return response()->json([ 'success' => true, 'message' => 'Club created successfully!', 'club' => $club ]); } catch (\Exception $e) { DB::rollBack(); return response()->json([ 'success' => false, 'message' => 'Failed to create club: ' . $e->getMessage() ], 500); } } /** * Update an existing club. */ public function update(Request $request, $id) { $club = Tenant::findOrFail($id); $validator = Validator::make($request->all(), [ 'owner_user_id' => 'required|exists:users,id', 'club_name' => 'required|string|max:255', 'slug' => 'required|string|max:255|unique:tenants,slug,' . $id, 'slogan' => 'nullable|string|max:100', 'description' => 'nullable|string|max:1000', 'established_date' => 'nullable|date', 'commercial_reg_number' => 'nullable|string|max:100', 'vat_reg_number' => 'nullable|string|max:100', 'vat_percentage' => 'nullable|numeric|min:0|max:100', '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', 'enrollment_fee' => 'nullable|numeric|min:0', 'club_status' => 'nullable|in:active,inactive,pending', 'public_profile_enabled' => 'nullable|boolean', 'logo' => 'nullable', 'cover_image' => 'nullable', 'social_links' => 'nullable|array', 'bank_accounts' => 'nullable|array', ]); if ($validator->fails()) { return response()->json([ 'success' => false, 'message' => 'Validation failed', 'errors' => $validator->errors() ], 422); } DB::beginTransaction(); try { $data = $validator->validated(); // Handle phone as JSON if ($request->filled('phone_code') && $request->filled('phone_number')) { $data['phone'] = [ 'code' => $request->phone_code, 'number' => $request->phone_number, ]; } // Handle logo upload if ($request->filled('logo') && str_starts_with($request->logo, 'data:image')) { if ($club->logo) { Storage::disk('public')->delete($club->logo); } $data['logo'] = $this->handleBase64Image($request->logo, 'clubs/logos', 'logo_' . time()); } // Handle cover image upload if ($request->filled('cover_image') && str_starts_with($request->cover_image, 'data:image')) { if ($club->cover_image) { Storage::disk('public')->delete($club->cover_image); } $data['cover_image'] = $this->handleBase64Image($request->cover_image, 'clubs/covers', 'cover_' . time()); } // Set status $data['status'] = $request->input('club_status', 'active'); $data['public_profile_enabled'] = $request->boolean('public_profile_enabled', true); // Update club $club->update($data); // Update social links $club->socialLinks()->delete(); if ($request->has('social_links')) { foreach ($request->social_links as $index => $link) { if (!empty($link['platform']) && !empty($link['url'])) { ClubSocialLink::create([ 'tenant_id' => $club->id, 'platform' => $link['platform'], 'url' => $link['url'], 'display_order' => $index, ]); } } } // Update bank accounts $club->bankAccounts()->delete(); if ($request->has('bank_accounts')) { foreach ($request->bank_accounts as $account) { if (!empty($account['bank_name']) && !empty($account['account_name'])) { ClubBankAccount::create([ 'tenant_id' => $club->id, 'bank_name' => $account['bank_name'], 'account_name' => $account['account_name'], 'account_number' => $account['account_number'] ?? null, 'iban' => $account['iban'] ?? null, 'swift_code' => $account['swift_code'] ?? null, 'benefitpay_account' => $account['benefitpay_account'] ?? null, ]); } } } DB::commit(); return response()->json([ 'success' => true, 'message' => 'Club updated successfully!', 'club' => $club ]); } catch (\Exception $e) { DB::rollBack(); return response()->json([ 'success' => false, 'message' => 'Failed to update club: ' . $e->getMessage() ], 500); } } /** * Handle base64 image upload. */ private function handleBase64Image($base64String, $folder, $filename) { $imageParts = explode(";base64,", $base64String); $imageTypeAux = explode("image/", $imageParts[0]); $extension = $imageTypeAux[1]; $imageBinary = base64_decode($imageParts[1]); $fullPath = $folder . '/' . $filename . '.' . $extension; Storage::disk('public')->put($fullPath, $imageBinary); return $fullPath; } }