826 lines
32 KiB
PHP
826 lines
32 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Livewire\Profiles;
|
|
|
|
use App\Events\Auth\RegisteredByAdmin;
|
|
use App\Http\Livewire\Traits\RequiresAdminAuthorization;
|
|
use App\Models\Account;
|
|
use App\Models\Admin;
|
|
use App\Models\Bank;
|
|
use App\Models\Language;
|
|
use App\Models\Locations\Country;
|
|
use App\Models\Locations\Location;
|
|
use App\Models\Organization;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\Rule;
|
|
use Livewire\Component;
|
|
use WireUi\Traits\WireUiActions;
|
|
|
|
class Create extends Component
|
|
{
|
|
use WireUiActions, RequiresAdminAuthorization;
|
|
|
|
public bool $showCreateModal = false;
|
|
// #[Validate] // Add this attribute
|
|
public $createProfile = [
|
|
'name' => null,
|
|
'full_name' => null,
|
|
'email' => null,
|
|
'password' => null,
|
|
'password_confirmation' => null,
|
|
'lang_preference' => null,
|
|
'type' => null,
|
|
];
|
|
public $profileTypeOptions = [];
|
|
public $profileTypeSelected;
|
|
public $linkBankOptions = [];
|
|
public $linkBankSelected;
|
|
public $linkUserOptions = [];
|
|
public $linkUserSelected;
|
|
|
|
public bool $generateRandomPassword = true;
|
|
private ?string $generatedPlainTextPassword = null; // Add property to store plain password
|
|
|
|
|
|
public $country;
|
|
public $division;
|
|
public $city;
|
|
public $district;
|
|
// Keep track of whether validation is needed
|
|
public $validateCountry = false;
|
|
public $validateDivision = false;
|
|
public $validateCity = false;
|
|
|
|
public $localeOptions;
|
|
|
|
protected $listeners = ['countryToParent', 'divisionToParent', 'cityToParent', 'districtToParent'];
|
|
|
|
public function mount()
|
|
{
|
|
// CRITICAL: Authorize admin access for profile creation component
|
|
$this->authorizeAdminAccess();
|
|
}
|
|
|
|
protected function rules()
|
|
{
|
|
$rules = [
|
|
'createProfile' => 'array',
|
|
'createProfile.type' => ['required', 'string'],
|
|
'country' => 'required_if:validateCountry,true',
|
|
'division' => 'required_if:validateDivision,true',
|
|
'city' => 'required_if:validateCity,true',
|
|
'district' => 'sometimes',
|
|
];
|
|
|
|
// Dynamically add rules based on type
|
|
if (!empty($this->createProfile['type'])) {
|
|
$typeKey = 'profile_' . strtolower(class_basename($this->createProfile['type']));
|
|
$isUserType = $this->createProfile['type'] === \App\Models\User::class;
|
|
$isOrgType = $this->createProfile['type'] === \App\Models\Organization::class;
|
|
$isAdminType = $this->createProfile['type'] === \App\Models\Admin::class;
|
|
$isBankType = $this->createProfile['type'] === \App\Models\Bank::class;
|
|
|
|
// --- Add rules for common fields like name, full_name, email ---
|
|
$rules['createProfile.name'] = Rule::when(
|
|
fn ($input) => isset($input['createProfile']['name']),
|
|
timebank_config("rules.{$typeKey}.name", []),
|
|
[]
|
|
);
|
|
$rules['createProfile.full_name'] = Rule::when(
|
|
fn ($input) => isset($input['createProfile']['full_name']),
|
|
timebank_config("rules.{$typeKey}.full_name", []),
|
|
[]
|
|
);
|
|
$rules['createProfile.email'] = Rule::when(
|
|
fn ($input) => isset($input['createProfile']['email']),
|
|
timebank_config("rules.{$typeKey}.email", []),
|
|
[]
|
|
);
|
|
|
|
// --- Conditional Password Rules (Only for User type) ---
|
|
|
|
//TODO NEXT: fix manual password confirmation
|
|
|
|
if ($isUserType) {
|
|
$rules['createProfile.password'] = Rule::when(
|
|
!$this->generateRandomPassword,
|
|
// Explicitly add 'confirmed' here for the final validation
|
|
// Merge with rules from config, ensuring 'confirmed' is present
|
|
timebank_config("rules.{$typeKey}.password" // Get base rules
|
|
),
|
|
['nullable', 'string'] // Rules when generating random password
|
|
);
|
|
$rules['createProfile.lang_preference'] = ['string', 'max:3'];
|
|
} else {
|
|
$rules['createProfile.password'] = ['nullable', 'string'];
|
|
$rules['createProfile.password_confirmation'] = ['nullable', 'string'];
|
|
}
|
|
|
|
|
|
// --- Conditional Link Rules ---
|
|
// Link Bank is required for User and Organization
|
|
if ($isUserType || $isOrgType) {
|
|
$rules['createProfile.linkBank'] = ['required', 'integer'];
|
|
} else {
|
|
// Ensure it's not required if not rendered
|
|
$rules['createProfile.linkBank'] = ['nullable', 'integer'];
|
|
}
|
|
|
|
// Link User is required for Organization, Admin, and Bank
|
|
if ($isOrgType || $isAdminType || $isBankType) {
|
|
$rules['createProfile.linkUser'] = ['required', 'integer'];
|
|
} else {
|
|
// Ensure it's not required if not rendered
|
|
$rules['createProfile.linkUser'] = ['nullable', 'integer'];
|
|
}
|
|
} else {
|
|
// Default rules if type is not yet selected (optional, but good practice)
|
|
$rules['createProfile.linkBank'] = ['nullable', 'integer'];
|
|
$rules['createProfile.linkUser'] = ['nullable', 'integer'];
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
|
|
|
|
public function openCreateModal()
|
|
{
|
|
$this->resetErrorBag();
|
|
$this->showCreateModal = true;
|
|
|
|
$appLocale = app()->getLocale();
|
|
|
|
// Get all optional profiles from config
|
|
$this->profileTypeOptions = collect(timebank_config('profiles'))
|
|
->map(function ($data, $key) {
|
|
return [
|
|
'name' => ucfirst($key),
|
|
'value' => 'App\Models\\'. ucfirst($key),
|
|
];
|
|
})
|
|
->values()
|
|
->toArray();
|
|
|
|
$this->generateRandomPassword = true; // Ensure it's checked on open
|
|
$this->generateAndSetPassword(); // Generate initial password
|
|
|
|
$this->localeOptions = Language::all()->filter(function ($lang) {
|
|
return ($lang->lang_code);
|
|
})->map(function ($lang) {
|
|
return [
|
|
'lang_code' => $lang->lang_code,
|
|
'label' => $lang->flag . ' ' . trans('messages.' . $lang->name),
|
|
];
|
|
})->toArray();
|
|
|
|
|
|
$this->resetErrorBag(['country', 'division', 'city', 'district']); // Clear location errors
|
|
}
|
|
|
|
|
|
public function updatedCreateProfileType()
|
|
{
|
|
$selectedType = $this->createProfile['type'] ?? null;
|
|
$optionsCollection = collect(); // Initialize empty collection
|
|
|
|
if ($selectedType === \App\Models\User::class || $selectedType === \App\Models\Organization::class) {
|
|
// Banks higher than level 1 are non-system banks
|
|
$optionsBankCollection = Bank::where('level', '>=', 2)->get(['id', 'name', 'full_name', 'email', 'profile_photo_path']);
|
|
// Make email visible if it's hidden and needed for description
|
|
$optionsBankCollection->each(fn ($item) => $item->makeVisible('email'));
|
|
// Same procedure for User model
|
|
$optionsUserCollection = User::get(['id', 'name', 'full_name', 'email', 'profile_photo_path']);
|
|
$optionsUserCollection->each(fn ($item) => $item->makeVisible('email'));
|
|
|
|
|
|
// Map to a plain array structure, needed for the wireUi user-option template
|
|
$this->linkBankOptions = $optionsBankCollection->map(function ($item) {
|
|
return [
|
|
'id' => $item->id,
|
|
'name' => $item->name,
|
|
'email' => $item->email,
|
|
'profile_photo_url' => $item->profile_photo_url,
|
|
];
|
|
})->toArray();
|
|
$this->linkUserOptions = $optionsUserCollection->map(function ($item) {
|
|
return [
|
|
'id' => $item->id,
|
|
'name' => $item->name,
|
|
'email' => $item->email,
|
|
'profile_photo_url' => $item->profile_photo_url,
|
|
];
|
|
})->toArray();
|
|
|
|
|
|
} elseif ($selectedType) {
|
|
$optionsUserCollection = User::get(['id', 'name', 'full_name', 'email', 'profile_photo_path']);
|
|
$optionsUserCollection->each(fn ($item) => $item->makeVisible('email'));
|
|
|
|
$this->linkUserOptions = $optionsUserCollection->map(function ($item) {
|
|
return [
|
|
'id' => $item->id,
|
|
'name' => $item->name,
|
|
'email' => $item->email,
|
|
'profile_photo_url' => $item->profile_photo_url,
|
|
];
|
|
})->toArray();
|
|
}
|
|
}
|
|
|
|
|
|
public function updated($propertyName)
|
|
{
|
|
// Only validate createProfile.* fields when they themselves change
|
|
if (str_starts_with($propertyName, 'createProfile.')
|
|
&& $propertyName !== 'createProfile.type') {
|
|
$this->validateOnly($propertyName);
|
|
}
|
|
|
|
// If the 'type' field specifically was updated, handle that separately
|
|
if ($propertyName === 'createProfile.type') {
|
|
$this->resetErrorBag(['createProfile.name', 'createProfile.full_name']);
|
|
$this->validateOnly('createProfile.name');
|
|
$this->validateOnly('createProfile.full_name');
|
|
$this->updatedCreateProfileType();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Method called when checkbox state changes
|
|
public function updatedGenerateRandomPassword(bool $value)
|
|
{
|
|
if ($value) {
|
|
// Checkbox is CHECKED - Generate password
|
|
$this->generateAndSetPassword();
|
|
} else {
|
|
// Checkbox is UNCHECKED - Clear password fields for manual input
|
|
$this->createProfile['password'] = null;
|
|
$this->createProfile['password_confirmation'] = null;
|
|
// Reset validation errors for password fields
|
|
$this->resetErrorBag(['createProfile.password', 'createProfile.password_confirmation']);
|
|
}
|
|
}
|
|
|
|
|
|
// Helper function to generate and set password
|
|
private function generateAndSetPassword()
|
|
{
|
|
$password = Str::password(12, true, true, true, false);
|
|
$this->generatedPlainTextPassword = $password; // Store plain text
|
|
$this->createProfile['password'] = $password; // Set for validation/hashing
|
|
$this->createProfile['passwordConfirmation'] = null;
|
|
$this->resetErrorBag(['createProfile.password', 'createProfile.password_confirmation']);
|
|
}
|
|
|
|
|
|
public function emitLocationToChildren()
|
|
{
|
|
$this->dispatch('countryToChildren', $this->country);
|
|
$this->dispatch('divisionToChildren', $this->division);
|
|
$this->dispatch('cityToChildren', $this->city);
|
|
$this->dispatch('districtToChildren', $this->district);
|
|
}
|
|
|
|
// --- Listener methods ---
|
|
// When a location value changes (from child), update the property,
|
|
// recalculate validation requirements, and trigger validation for that specific field.
|
|
public function countryToParent($value)
|
|
{
|
|
$this->country = $value;
|
|
|
|
if ($value) {
|
|
// Look up language preference by country, if available
|
|
$countryLanguage = DB::table('country_languages')->where('country_id', $this->country)->pluck('code');
|
|
count($countryLanguage) === 1 ? $this->createProfile['lang_preference'] = $countryLanguage->first() : $this->createProfile['lang_preference'] = null;
|
|
}
|
|
|
|
$this->setLocationValidationOptions();
|
|
$this->validateOnly('country'); // Validate country immediately
|
|
// Also re-validate division/city as their requirement might change
|
|
$this->validateOnly('division');
|
|
$this->validateOnly('city');
|
|
}
|
|
|
|
public function divisionToParent($value)
|
|
{
|
|
$this->division = $value;
|
|
$this->setLocationValidationOptions(); // Recalculate requirements
|
|
$this->validateOnly('division'); // Validate division immediately
|
|
}
|
|
|
|
public function cityToParent($value)
|
|
{
|
|
$this->city = $value;
|
|
$this->setLocationValidationOptions(); // Recalculate requirements
|
|
$this->validateOnly('city'); // Validate city immediately
|
|
}
|
|
|
|
// District doesn't usually affect others, just validate itself
|
|
public function districtToParent($value)
|
|
{
|
|
$this->district = $value;
|
|
$this->validateOnly('district');
|
|
}
|
|
// --- End Listener methods ---
|
|
|
|
|
|
public function setLocationValidationOptions()
|
|
{
|
|
// Store previous state to check if requirements changed
|
|
$oldValidateDivision = $this->validateDivision;
|
|
$oldValidateCity = $this->validateCity;
|
|
|
|
// Default to true, then adjust based on country data
|
|
$this->validateCountry = true; // Country is always potentially required initially
|
|
$this->validateDivision = true;
|
|
$this->validateCity = true;
|
|
|
|
if ($this->country) {
|
|
$countryModel = Country::find($this->country);
|
|
if ($countryModel) {
|
|
$countDivisions = $countryModel->divisions()->count();
|
|
$countCities = $countryModel->cities()->count();
|
|
|
|
// Logic based on available sub-locations for the selected country
|
|
if ($countDivisions > 0 && $countCities < 1) {
|
|
$this->validateDivision = true;
|
|
$this->validateCity = false; // City not needed if none exist for country
|
|
} elseif ($countDivisions < 1 && $countCities > 0) {
|
|
$this->validateDivision = false; // Division not needed if none exist
|
|
$this->validateCity = true;
|
|
} elseif ($countDivisions < 1 && $countCities < 1) {
|
|
$this->validateDivision = false; // Neither needed if none exist
|
|
$this->validateCity = false;
|
|
} elseif ($countDivisions > 0 && $countCities > 0) {
|
|
// Prefer City over Division if both exist
|
|
$this->validateDivision = false; // Assuming city is the primary choice here
|
|
$this->validateCity = true;
|
|
|
|
}
|
|
} else {
|
|
// Invalid country selected, potentially keep validation? Or reset?
|
|
// For now, keep defaults (true) as the country rule itself will fail.
|
|
}
|
|
} else {
|
|
// No country selected, only country is required.
|
|
$this->validateCountry = true;
|
|
$this->validateDivision = false;
|
|
$this->validateCity = false;
|
|
}
|
|
|
|
// --- Re-validate if requirements changed ---
|
|
// If the requirement for division/city changed, re-trigger their validation
|
|
// This helps clear errors if they become non-required.
|
|
if ($this->validateDivision !== $oldValidateDivision) {
|
|
$this->validateOnly('division');
|
|
}
|
|
if ($this->validateCity !== $oldValidateCity) {
|
|
$this->validateOnly('city');
|
|
}
|
|
// --- End Re-validation ---
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles the save button of the create profile modal.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function create()
|
|
{
|
|
// CRITICAL: Authorize admin access for creating profiles
|
|
$this->authorizeAdminAccess();
|
|
|
|
// --- If a user, bank, admin profile will be created, determine the plain password that will be emailed ---
|
|
if ($this->createProfile['type'] === \App\Models\User::class
|
|
|| $this->createProfile['type'] === \App\Models\Bank::class
|
|
|| $this->createProfile['type'] === \App\Models\Admin::class ) {
|
|
if ($this->generateRandomPassword) {
|
|
$this->generateAndSetPassword();
|
|
} elseif (!empty($this->createProfile['password'])) {
|
|
// Capture manually entered password (after trimming)
|
|
$this->generatedPlainTextPassword = trim($this->createProfile['password']);
|
|
} else {
|
|
// Manual mode, but password field is empty
|
|
$this->generatedPlainTextPassword = null;
|
|
}
|
|
} else {
|
|
// Not a profile type with password, ensure plain password is null
|
|
$this->generatedPlainTextPassword = null;
|
|
// Also nullify password fields before validation if not User
|
|
$this->createProfile['password'] = null;
|
|
$this->createProfile['passwordConfirmation'] = null;
|
|
}
|
|
|
|
|
|
// Trim password fields if they exist (important for validation)
|
|
if (isset($this->createProfile['password'])) {
|
|
$this->createProfile['password'] = trim($this->createProfile['password']);
|
|
}
|
|
if (isset($this->createProfile['passwordConfirmation'])) {
|
|
$this->createProfile['passwordConfirmation'] = trim($this->createProfile['passwordConfirmation']);
|
|
}
|
|
|
|
// Validate all fields based on current rules
|
|
$validatedData = $this->validate();
|
|
$profileData = $validatedData['createProfile']; // Get the nested profile data
|
|
|
|
// Remove password confirmation if it exists
|
|
unset($profileData['password_confirmation']);
|
|
|
|
// Add location data to the profile data array for helper methods
|
|
$profileData['country_id'] = $validatedData['country'] ?? null;
|
|
$profileData['division_id'] = $validatedData['division'] ?? null;
|
|
$profileData['city_id'] = $validatedData['city'] ?? null;
|
|
$profileData['district_id'] = $validatedData['district'] ?? null;
|
|
|
|
$newProfile = null;
|
|
|
|
try {
|
|
// Use a transaction for creating the new profile and related models
|
|
DB::transaction(function () use ($profileData, &$newProfile) {
|
|
|
|
switch ($profileData['type']) {
|
|
case \App\Models\User::class:
|
|
$newProfile = $this->createUserProfile($profileData);
|
|
break;
|
|
case \App\Models\Organization::class:
|
|
$newProfile = $this->createOrganizationProfile($profileData);
|
|
break;
|
|
case \App\Models\Bank::class:
|
|
$newProfile = $this->createBankProfile($profileData);
|
|
break;
|
|
case \App\Models\Admin::class:
|
|
$newProfile = $this->createAdminProfile($profileData);
|
|
break;
|
|
default:
|
|
throw new \Exception("Unknown profile type: " . $profileData['type']);
|
|
}
|
|
|
|
// Common logic after profile creation (if any) can go here
|
|
// e.g., creating a default location if not handled in helpers
|
|
if ($newProfile && !$newProfile->locations()->exists()) {
|
|
$this->createDefaultLocation($newProfile, $profileData);
|
|
}
|
|
|
|
}); // End of transaction
|
|
|
|
|
|
if ($newProfile) {
|
|
// Dispatch RegisteredByAdmin event to send email confirmation / password / welcome
|
|
event(new RegisteredByAdmin($newProfile, $this->generatedPlainTextPassword));
|
|
}
|
|
|
|
|
|
// Success
|
|
$this->notification()->success(
|
|
__('Profile Created'),
|
|
__('The profile has been successfully created.')
|
|
);
|
|
|
|
$this->showCreateModal = false;
|
|
$this->dispatch('profileCreated');
|
|
$this->resetForm();
|
|
|
|
} catch (Throwable $e) {
|
|
// --- Failure ---
|
|
Log::error('Profile creation failed: ' . $e->getMessage(), [
|
|
'profile_data' => $profileData, // Log data for debugging
|
|
'exception' => $e
|
|
]);
|
|
|
|
$this->notification()->error(
|
|
__('Error'),
|
|
// Provide a generic error message to the user
|
|
__('Failed to create profile. Please check the details and try again. If the problem persists, contact support.')
|
|
);
|
|
// Keep the modal open for correction
|
|
}
|
|
}
|
|
|
|
|
|
private function createUserProfile(array $data): User
|
|
{
|
|
// Hash password
|
|
$data['password'] = Hash::make($data['password']);
|
|
|
|
// Add default values from config
|
|
$data['profile_photo_path'] = timebank_config('profiles.user.profile_photo_path_new');
|
|
$data['limit_min'] = timebank_config('profiles.user.limit_min');
|
|
$data['limit_max'] = timebank_config('profiles.user.limit_max');
|
|
$data['lang_preference'] = $data['lang_preference'] ?? null;
|
|
|
|
|
|
// Create the User
|
|
$profile = User::create($data);
|
|
|
|
// Attach to Bank
|
|
if (!empty($data['linkBank'])) {
|
|
$profile->attachBankClient($data['linkBank']);
|
|
}
|
|
|
|
// Create Account
|
|
$this->createDefaultAccount($profile, 'user');
|
|
|
|
// Create Location
|
|
$this->createDefaultLocation($profile, $data);
|
|
|
|
|
|
// TODO: Replace commented rtippin messenger logic with wirechat logic
|
|
// Attach to Messenger
|
|
// Messenger::getProviderMessenger($profile);
|
|
|
|
return $profile;
|
|
}
|
|
|
|
|
|
|
|
private function createOrganizationProfile(array $data): Organization
|
|
{
|
|
// Add default values from config
|
|
$data['profile_photo_path'] = timebank_config('profiles.organization.profile_photo_path_new');
|
|
$data['limit_min'] = timebank_config('profiles.organization.limit_min');
|
|
$data['limit_max'] = timebank_config('profiles.organization.limit_max');
|
|
$data['lang_preference'] = $data['lang_preference'] ?? null;
|
|
|
|
// Create the profile
|
|
$profile = Organization::create($data);
|
|
|
|
// Attach to Bank
|
|
if (!empty($data['linkBank'])) {
|
|
$profile->attachBankClient($data['linkBank']);
|
|
}
|
|
|
|
// Attach to profile manager
|
|
if (!empty($data['linkUser'])) {
|
|
$profile->managers()->attach($data['linkUser']);
|
|
|
|
// Send email notifications to linked user about the new organization
|
|
$linkedUser = User::find($data['linkUser']);
|
|
if ($linkedUser) {
|
|
$this->sendProfileLinkNotifications($profile, $linkedUser);
|
|
}
|
|
}
|
|
|
|
// Create Account
|
|
$this->createDefaultAccount($profile, 'organization');
|
|
|
|
// Create Location
|
|
$this->createDefaultLocation($profile, $data);
|
|
|
|
|
|
// TODO: Replace commented rtippin messenger logic with wirechat logic
|
|
// Attach to Messenger
|
|
// Messenger::getProviderMessenger($profile);
|
|
|
|
return $profile;
|
|
}
|
|
|
|
|
|
private function createBankProfile(array $data): Bank
|
|
{
|
|
// Hash password
|
|
$data['password'] = Hash::make($data['password']);
|
|
|
|
// Add default values from config
|
|
$data['profile_photo_path'] = timebank_config('profiles.bank.profile_photo_path_new');
|
|
$data['level'] = $data['level'] ?? timebank_config('profiles.bank.level', 1);
|
|
$data['limit_min'] = timebank_config('profiles.bank.limit_min');
|
|
$data['limit_max'] = timebank_config('profiles.bank.limit_max');
|
|
$data['lang_preference'] = $data['lang_preference'] ?? null;
|
|
|
|
// Create the profile
|
|
$profile = Bank::create($data);
|
|
|
|
// Attach to profile manager
|
|
if (!empty($data['linkUser'])) {
|
|
$profile->managers()->attach($data['linkUser']);
|
|
|
|
// Send email notifications to linked user about the new bank
|
|
$linkedUser = User::find($data['linkUser']);
|
|
if ($linkedUser) {
|
|
$this->sendProfileLinkNotifications($profile, $linkedUser);
|
|
}
|
|
}
|
|
// Create Account
|
|
$this->createDefaultAccount($profile, 'bank');
|
|
|
|
// Create debit account for level 0 (source) banks
|
|
if ($profile->level === 0) {
|
|
$this->createDefaultAccount($profile, 'debit');
|
|
}
|
|
|
|
// Create Location
|
|
$this->createDefaultLocation($profile, $data);
|
|
|
|
|
|
// TODO: Replace commented rtippin messenger logic with wirechat logic
|
|
// Attach to Messenger
|
|
// Messenger::getProviderMessenger($profile);
|
|
|
|
return $profile;
|
|
}
|
|
|
|
|
|
private function createAdminProfile(array $data): Admin
|
|
{
|
|
// Hash password
|
|
$data['password'] = Hash::make($data['password']);
|
|
|
|
// Add default values from config
|
|
$data['profile_photo_path'] = timebank_config('profiles.admin.profile_photo_path_new');
|
|
$data['limit_min'] = timebank_config('profiles.admin.limit_min');
|
|
$data['limit_max'] = timebank_config('profiles.admin.limit_max');
|
|
$data['lang_preference'] = $data['lang_preference'] ?? null;
|
|
|
|
// Create the profile
|
|
$profile = Admin::create($data);
|
|
|
|
// Attach to User
|
|
if (!empty($data['linkUser'])) {
|
|
$profile->users()->attach($data['linkUser']);
|
|
$linkedUser = User::find($data['linkUser']);
|
|
$linkedUser->assignRole('admin');
|
|
|
|
// Create and assign scoped Admin role (required by getCanManageProfiles())
|
|
$scopedRoleName = "Admin\\{$profile->id}\\admin";
|
|
$scopedRole = \Spatie\Permission\Models\Role::findOrCreate($scopedRoleName, 'web');
|
|
$scopedRole->syncPermissions(\Spatie\Permission\Models\Permission::all());
|
|
$linkedUser->assignRole($scopedRoleName);
|
|
|
|
// Send email notifications to linked user about the new admin profile
|
|
$this->sendProfileLinkNotifications($profile, $linkedUser);
|
|
}
|
|
|
|
// Create Location
|
|
$this->createDefaultLocation($profile, $data);
|
|
|
|
|
|
// TODO: Replace commented rtippin messenger logic with wirechat logic
|
|
// Attach to Messenger
|
|
// Messenger::getProviderMessenger($profile);
|
|
|
|
return $profile;
|
|
}
|
|
|
|
|
|
|
|
// --- Helper function to create Default Location ---
|
|
private function createDefaultLocation($profileModel, array $data): void
|
|
{
|
|
if (empty($data['country_id'])) {
|
|
return;
|
|
} // Don't create if no country
|
|
|
|
$location = new Location();
|
|
$location->name = __('Default location');
|
|
$location->country_id = $data['country_id'];
|
|
$location->division_id = $data['division_id'] ?? null;
|
|
$location->city_id = $data['city_id'] ?? null;
|
|
$location->district_id = $data['district_id'] ?? null;
|
|
$profileModel->locations()->save($location);
|
|
}
|
|
|
|
// --- Helper function to create Default Account ---
|
|
private function createDefaultAccount($profileModel, string $type): void
|
|
{
|
|
// Check if accounts are enabled for this type and config exists
|
|
$accountConfig = timebank_config("accounts.{$type}");
|
|
if (!$accountConfig) {
|
|
Log::info("Account creation skipped for type '{$type}': No config found.");
|
|
return;
|
|
}
|
|
|
|
$account = new Account();
|
|
$account->name = __(timebank_config("accounts.{$type}.name", 'default Account'));
|
|
$account->limit_min = timebank_config("accounts.{$type}.limit_min", 0);
|
|
$account->limit_max = timebank_config("accounts.{$type}.limit_max", 0);
|
|
|
|
// Associate account with the profile model (assuming polymorphic relation 'accounts')
|
|
$profileModel->accounts()->save($account);
|
|
}
|
|
|
|
|
|
/**
|
|
* Resets the form fields to their initial state.
|
|
*/
|
|
public function resetForm()
|
|
{
|
|
$this->showCreateModal = false;
|
|
|
|
// Reset the main profile data array
|
|
$this->createProfile = [
|
|
'name' => null,
|
|
'full_name' => null,
|
|
'email' => null,
|
|
'password' => null,
|
|
'password_confirmation' => null,
|
|
'phone' => null,
|
|
'comment' => null,
|
|
'lang_preference' => null,
|
|
'type' => 'user', // Reset type back to default
|
|
'linkBank' => null, // Add linkBank if it's part of the array
|
|
'linkUser' => null, // Add linkUser if it's part of the array
|
|
];
|
|
|
|
// Reset select options and selections
|
|
$this->profileTypeOptions = [];
|
|
$this->profileTypeSelected = null;
|
|
$this->linkBankOptions = [];
|
|
$this->linkBankSelected = null;
|
|
$this->linkUserOptions = [];
|
|
$this->linkUserSelected = null;
|
|
|
|
// Reset password generation flag
|
|
$this->generateRandomPassword = true;
|
|
$this->generatedPlainTextPassword = null; // Clear stored password
|
|
|
|
// Reset location properties
|
|
$this->country = null;
|
|
$this->division = null;
|
|
$this->city = null;
|
|
$this->district = null;
|
|
|
|
// Reset location validation flags
|
|
$this->validateCountry = true;
|
|
$this->validateDivision = true;
|
|
$this->validateCity = true;
|
|
|
|
// Clear validation errors
|
|
$this->resetErrorBag();
|
|
|
|
// Re-fetch initial options if needed when resetting
|
|
$this->updatedCreateProfileType();
|
|
}
|
|
|
|
/**
|
|
* Send profile link notification emails to both the profile and the linked user
|
|
*
|
|
* @param mixed $profile The newly created profile (Organization/Bank/Admin)
|
|
* @param User $linkedUser The user being linked to the profile
|
|
* @return void
|
|
*/
|
|
private function sendProfileLinkNotifications($profile, User $linkedUser): void
|
|
{
|
|
Log::info('ProfileLinkCreated: Sending email notifications', [
|
|
'profile_id' => $profile->id,
|
|
'profile_type' => get_class($profile),
|
|
'profile_name' => $profile->name,
|
|
'linked_user_id' => $linkedUser->id,
|
|
'linked_user_email' => $linkedUser->email ?? 'NO EMAIL',
|
|
]);
|
|
|
|
// Send email to the linked user
|
|
$userMessageSetting = \App\Models\MessageSetting::where('message_settingable_id', $linkedUser->id)
|
|
->where('message_settingable_type', get_class($linkedUser))
|
|
->first();
|
|
|
|
$sendUserEmail = $userMessageSetting ? $userMessageSetting->system_message : true;
|
|
|
|
Log::info('ProfileLinkCreated: Linked user message setting check', [
|
|
'has_message_setting' => $userMessageSetting ? 'yes' : 'no',
|
|
'system_message' => $userMessageSetting ? $userMessageSetting->system_message : 'default:true',
|
|
'will_send_email' => $sendUserEmail ? 'yes' : 'no',
|
|
]);
|
|
|
|
if ($sendUserEmail) {
|
|
\App\Jobs\SendProfileLinkChangedMail::dispatch($linkedUser, $profile, 'attached');
|
|
Log::info('ProfileLinkCreated: Dispatched email to linked user', [
|
|
'recipient_email' => $linkedUser->email,
|
|
'profile_name' => $profile->name,
|
|
]);
|
|
} else {
|
|
Log::info('ProfileLinkCreated: Skipped email to linked user (system_message disabled)');
|
|
}
|
|
|
|
// Send email to the newly created profile
|
|
$profileMessageSetting = \App\Models\MessageSetting::where('message_settingable_id', $profile->id)
|
|
->where('message_settingable_type', get_class($profile))
|
|
->first();
|
|
|
|
$sendProfileEmail = $profileMessageSetting ? $profileMessageSetting->system_message : true;
|
|
|
|
Log::info('ProfileLinkCreated: Profile message setting check', [
|
|
'has_message_setting' => $profileMessageSetting ? 'yes' : 'no',
|
|
'system_message' => $profileMessageSetting ? $profileMessageSetting->system_message : 'default:true',
|
|
'will_send_email' => $sendProfileEmail ? 'yes' : 'no',
|
|
]);
|
|
|
|
if ($sendProfileEmail) {
|
|
\App\Jobs\SendProfileLinkChangedMail::dispatch($profile, $linkedUser, 'attached');
|
|
Log::info('ProfileLinkCreated: Dispatched email to profile', [
|
|
'recipient_email' => $profile->email,
|
|
'linked_user_name' => $linkedUser->name,
|
|
]);
|
|
} else {
|
|
Log::info('ProfileLinkCreated: Skipped email to profile (system_message disabled)');
|
|
}
|
|
}
|
|
|
|
}
|