Initial commit

This commit is contained in:
Ronald Huynen
2026-03-23 21:37:59 +01:00
commit 2547717edb
2193 changed files with 972171 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
<?php
namespace App\Http\Livewire\Locations;
use App\Models\Locations\City;
use App\Models\Locations\Country;
use App\Models\Locations\District;
use App\Models\Locations\Division;
use Livewire\Component;
class LocationsDropdown extends Component
{
public $country;
public $divisions = [];
public $division;
public $cities = [];
public $city;
public $districts = [];
public $district;
public $hideLabel = false;
protected $listeners = ['countryToChildren', 'divisionToChildren', 'cityToChildren', 'districtToChildren'];
/**
* Receive country value from parent component.
* Does NOT reset downstream values - that would clear values being set by subsequent ToChildren calls.
*/
public function countryToChildren($value)
{
$this->country = $value;
// Don't call updatedCountry() as it resets division/city/district
// Parent is responsible for setting all values correctly
}
/**
* Receive division value from parent component.
* Does NOT reset downstream values - that would clear values being set by subsequent ToChildren calls.
*/
public function divisionToChildren($value)
{
$this->division = $value;
if ($this->division === '') {
$this->division = null;
}
// Don't call updatedDivision() as it resets city/district
}
/**
* Receive city value from parent component.
* Does NOT reset downstream values - that would clear values being set by subsequent ToChildren calls.
*/
public function cityToChildren($value)
{
$this->city = $value;
if ($this->city === '') {
$this->city = null;
}
// Don't call updatedCity() as it resets district
}
/**
* Receive district value from parent component.
*/
public function districtToChildren($value)
{
$this->district = $value;
if ($this->district === '') {
$this->district = null;
}
// No downstream values to reset
}
public function updatedCountry()
{
$this->reset(['division', 'divisions']);
$this->reset(['city', 'cities']);
$this->reset(['district', 'districts']);
$this->dispatch('countryToParent', $this->country);
$this->dispatch('divisionToParent', $this->division);
$this->dispatch('cityToParent', $this->city);
$this->dispatch('districtToParent', $this->district);
}
public function updatedDivision()
{
$this->reset(['city', 'cities']);
$this->reset(['district', 'districts']);
if ($this->division === '') {
$this->division = null;
}
$this->dispatch('divisionToParent', $this->division);
$this->dispatch('cityToParent', $this->city);
$this->dispatch('districtToParent', $this->district);
}
public function updatedCity()
{
$this->reset(['district', 'districts']);
if ($this->city === '') {
$this->city = null;
}
$this->dispatch('cityToParent', $this->city);
$this->dispatch('districtToParent', $this->district);
}
public function updatedDistrict()
{
if ($this->district === '') {
$this->district = null;
}
$this->dispatch('districtToParent', $this->district);
}
public function countrySelected()
{
$this->dispatch('countryToParent', $this->country);
}
public function divisionSelected()
{
$this->dispatch('divisionToParent', $this->division);
}
public function citySelected()
{
$this->dispatch('cityToParent', $this->city);
}
public function districtSelected()
{
$this->dispatch('districtToParent', $this->district);
}
public function render()
{
if (!empty($this->country)) {
$country = Country::find($this->country);
$this->divisions = Division::with(['translations'])->where('country_id', $this->country)->get();
$this->cities = City::with(['translations'])->where('country_id', $this->country)->get();
}
if (!empty($this->city)) {
$this->districts = District::with(['translations'])->where('city_id', $this->city)->get();
}
$countries = Country::with(['translations'])->get();
return view('livewire.locations.locations-dropdown', compact(['countries']));
}
}

View File

@@ -0,0 +1,207 @@
<?php
namespace App\Http\Livewire\Locations;
use App\Models\Locations\City;
use App\Models\Locations\Country;
use App\Models\Locations\District;
use App\Models\Locations\Location;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
use WireUi\Traits\WireUiActions;
class UpdateProfileLocationForm extends Component
{
use WireUiActions;
public $state;
public $country;
public $division;
public $city;
public $district;
public $validateCountry = true;
public $validateDivision = true;
public $validateCity = true;
protected $listeners = ['countryToParent', 'divisionToParent', 'cityToParent', 'districtToParent'];
public function rules()
{
return [
'country' => 'required_if:validateCountry,true|integer',
'division' => 'required_if:validateDivision,true|nullable|integer',
'city' => 'required_if:validateCity,true|nullable|integer',
'district' => 'nullable|integer'
];
}
/**
* Prepare the component.
*
* @return void
*/
public function mount()
{
$this->state = session('activeProfileType')::find(session('activeProfileId'))
->load([
// 'locations',
'locations.country',
'locations.division',
'locations.city',
'locations.district']);
if ($this->state->locations->isNotEmpty()) {
if ($this->state->locations->first()->country) {
// For now we only use a single location. In the future this can become an array of locations.
$this->country = $this->state->locations->first()->country->id;
$this->dispatch('countryToChildren', $this->country);
}
if ($this->state->locations->first()->division) {
// For now we only use a single location. In the future this can become an array of locations.
$this->division = $this->state->locations->first()->division->id;
}
if ($this->state->locations->first()->city) {
// For now we only use a single location. In the future this can become an array of locations.
$this->city = $this->state->locations->first()->city->id;
// In case a location has a city without a country in the db:
if (!$this->state->locations->first()->country) {
$this->country = City::find($this->city)->country_id;
}
}
if ($this->state->locations->first()->district) {
// For now we only use a single location. In the future this can become an array of locations.
$this->district = $this->state->locations->first()->district->id;
// In case a location has a district without a city in the db:
if (!$this->state->locations->first()->city) {
$this->city = District::find($this->district)->city_id;
}
}
}
$this->setValidationOptions();
}
public function countryToParent($value)
{
$this->country = $value;
$this->setValidationOptions();
}
public function divisionToParent($value)
{
$this->division = $value;
$this->setValidationOptions();
}
public function cityToParent($value)
{
$this->city = $value;
$this->setValidationOptions();
}
public function districtToParent($value)
{
$this->district = $value;
$this->setValidationOptions();
}
public function emitLocationToChildren()
{
$this->dispatch('countryToChildren', $this->country);
$this->dispatch('divisionToChildren', $this->division);
$this->dispatch('cityToChildren', $this->city);
$this->dispatch('districtToChildren', $this->district);
}
public function setValidationOptions()
{
$this->validateCountry = $this->validateDivision = $this->validateCity = true;
// In case no cities or divisions for selected country are seeded in database
if ($this->country) {
$countDivisions = Country::find($this->country)->divisions()->count();
$countCities = Country::find($this->country)->cities()->count();
if ($countDivisions > 0 && $countCities < 1) {
$this->validateDivision = true;
$this->validateCity = false;
} elseif ($countDivisions < 1 && $countCities > 1) {
$this->validateDivision = false;
$this->validateCity = true;
} elseif ($countDivisions < 1 && $countCities < 1) {
$this->validateDivision = false;
$this->validateCity = false;
} elseif ($countDivisions > 0 && $countCities > 0) {
$this->validateDivision = false;
$this->validateCity = true;
}
}
// In case no country is selected, no need to show other validation errors
if (!$this->country) {
$this->validateCountry = true;
$this->validateDivision = $this->validateCity = false;
}
}
public function updateProfileInformation()
{
$this->validate();
// Use a transaction.
DB::transaction(function (): void {
$activeProfile = getActiveProfile();
// CRITICAL SECURITY: Validate user has ownership/access to this profile
\App\Helpers\ProfileAuthorizationHelper::authorize($activeProfile);
// For now we only use a single location. In the future this can become an array of locations.
if ($this->state->locations && $this->state->locations->isNotEmpty()) {
$location = Location::find($this->state->locations->first()->id);
} else {
$location = new Location();
$activeProfile->locations()->save($location); // create a new location
}
// Set country, division, and city IDs on the location
$location->country_id = $this->country;
$location->division_id = $this->division;
$location->city_id = $this->city;
$location->district_id = $this->district;
// Save the location with the updated relationship IDs
$location->save();
$activeProfile->touch(); // Observer catches this and reindexes search index
$this->dispatch('saved');
});
}
public function render()
{
return view('livewire.locations.update-profile-location-form');
}
}