From 9cc2ce530e3fd67aac23a6314cc232b725869cf3 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Tue, 7 Jul 2026 11:57:02 +0100 Subject: [PATCH] interim fix for drop downs --- .../Http/Controllers/CompanyController.php | 62 +++- .../Company/Resources/views/_form.blade.php | 223 ++++++++++-- .../Company/Resources/views/index.blade.php | 145 +++++--- Modules/Company/changelog.yaml | 8 +- Modules/Company/module.json | 2 +- .../Http/Controllers/CountriesController.php | 66 ++-- Modules/Countries/Routes/web.php | 2 +- resources/css/app.css | 321 +++++++----------- resources/js/app.js | 14 +- 9 files changed, 520 insertions(+), 323 deletions(-) diff --git a/Modules/Company/Http/Controllers/CompanyController.php b/Modules/Company/Http/Controllers/CompanyController.php index 0880b292..10a583c1 100644 --- a/Modules/Company/Http/Controllers/CompanyController.php +++ b/Modules/Company/Http/Controllers/CompanyController.php @@ -18,11 +18,11 @@ class CompanyController extends Controller { return view('company::index', [ 'companies' => Company::query() - ->with(['address.country', 'country']) + ->with(['address.country']) ->withCount(['users', 'warehouses', 'licenseKeys', 'divisions']) ->latest('id') ->paginate(20), - 'countries' => $this->countries(), + 'countries' => $this->countriesForSelect(), ]); } @@ -36,7 +36,7 @@ class CompanyController extends Controller 'status' => 'active', 'timezone' => 'Europe/London', ]), - 'countries' => $this->countries(), + 'countries' => $this->countriesForSelect(), ]); } @@ -45,7 +45,10 @@ class CompanyController extends Controller */ public function store(Request $request) { - $company = Company::create($this->validated($request)); + $validated = $this->validated($request); + + $company = Company::create($validated['company']); + $this->syncAddressCountry($company, $validated['country_id']); return redirect() ->route('platform.companies.index') @@ -73,7 +76,7 @@ class CompanyController extends Controller 'recentUsers' => $company->users()->latest('id')->limit(5)->get(), 'recentWarehouses' => $company->warehouses()->latest('id')->limit(5)->get(), 'recentLicenseKeys' => $company->licenseKeys()->latest('id')->limit(5)->get(), - 'countries' => Country::query()->active()->orderBy('sort_order')->orderBy('name')->get(['id', 'alpha2', 'name', 'display_name', 'common_name']), + 'countries' => $this->countriesForSelect(), 'companyUsers' => $company->users()->orderBy('name')->get(['id', 'name', 'email', 'company_id']), 'unassignedWarehouses' => $company->warehouses()->whereNull('division_id')->orderBy('name')->get(), ]); @@ -85,8 +88,8 @@ class CompanyController extends Controller public function edit(Company $company) { return view('company::edit', [ - 'company' => $company->loadMissing(['address.country', 'country']), - 'countries' => $this->countries(), + 'company' => $company->loadMissing('address.country'), + 'countries' => $this->countriesForSelect(), ]); } @@ -95,7 +98,10 @@ class CompanyController extends Controller */ public function update(Request $request, Company $company) { - $company->update($this->validated($request, $company)); + $validated = $this->validated($request, $company); + + $company->update($validated['company']); + $this->syncAddressCountry($company, $validated['country_id']); return redirect() ->route('platform.companies.show', $company) @@ -122,20 +128,44 @@ class CompanyController extends Controller 'name' => ['required', 'string', 'max:255'], 'billing_email' => ['nullable', 'email', 'max:255'], 'status' => ['required', Rule::in(['active', 'suspended', 'inactive'])], - 'country_id' => ['nullable', Rule::exists('countries', 'id')->where('is_active', true)], + 'country_id' => ['nullable', 'integer', Rule::exists('countries', 'id')], ]); - $data['slug'] = Str::slug($data['name']); - $data['timezone'] = $company?->timezone ?: 'Europe/London'; - - return $data; + return [ + 'company' => [ + 'name' => $data['name'], + 'billing_email' => $data['billing_email'] ?? null, + 'status' => $data['status'], + 'slug' => Str::slug($data['name']), + 'timezone' => $company?->timezone ?: 'Europe/London', + ], + 'country_id' => $data['country_id'] ?? null, + ]; } - private function countries() + private function countriesForSelect() { return Country::query() ->active() - ->ordered() - ->get(['id', 'alpha2', 'name', 'display_name', 'common_name', 'sort_order']); + ->orderByRaw("LOWER(COALESCE(NULLIF(display_name, ''), NULLIF(common_name, ''), name)) ASC") + ->get(['id', 'alpha2', 'name', 'display_name', 'common_name']); + } + + private function syncAddressCountry(Company $company, ?int $countryId): void + { + if ($countryId === null) { + $company->address()?->delete(); + + return; + } + + $company->address()->updateOrCreate( + [], + [ + 'country_id' => $countryId, + 'label' => 'Registered address', + 'is_primary' => true, + ] + ); } } diff --git a/Modules/Company/Resources/views/_form.blade.php b/Modules/Company/Resources/views/_form.blade.php index 6316cd1b..340639fe 100644 --- a/Modules/Company/Resources/views/_form.blade.php +++ b/Modules/Company/Resources/views/_form.blade.php @@ -1,13 +1,13 @@ @csrf -
+
@error('name') @@ -23,7 +23,7 @@ name="billing_email" value="{{ old('billing_email', $company->billing_email) }}" placeholder="billing@example.com" - class="mt-2 w-full rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white" + class="mt-2 h-12 w-full rounded-xl border-zinc-300 bg-white px-4 text-sm text-zinc-900 shadow-sm outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500/30 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white" > @error('billing_email')

{{ $message }}

@@ -35,7 +35,7 @@ - - @foreach ($countries as $country) - - @endforeach - -
-

Start typing to filter the country list.

+ @error('country_id')

{{ $message }}

@enderror @@ -73,14 +74,188 @@
- Cancel
+ + +@once + + + + + +@endonce diff --git a/Modules/Company/Resources/views/index.blade.php b/Modules/Company/Resources/views/index.blade.php index adb5d1f5..ab6fc316 100644 --- a/Modules/Company/Resources/views/index.blade.php +++ b/Modules/Company/Resources/views/index.blade.php @@ -30,57 +30,86 @@
-
+ @csrf -
- - -
+
+
+ + + @error('name') +

{{ $message }}

+ @enderror +
-
- - -
+
+ + + @error('billing_email') +

{{ $message }}

+ @enderror +
-
- - -
+
+ + + @error('status') +

{{ $message }}

+ @enderror +
-
- - -
+
+ + + @error('country_id') +

{{ $message }}

+ @enderror +
- + +
@@ -183,4 +212,30 @@ {{ $companies->links() }}
+ + @push('scripts') + + + + @endpush diff --git a/Modules/Company/changelog.yaml b/Modules/Company/changelog.yaml index 55cc3e18..5f29b1fe 100644 --- a/Modules/Company/changelog.yaml +++ b/Modules/Company/changelog.yaml @@ -1,6 +1,12 @@ -latest: 1.2.0 +latest: 1.2.1 versions: + - version: 1.2.1 + date: 2026-07-07 + changes: + - Replaced company country inputs with a shared Select2-enhanced dropdown populated by the Countries module. + - Aligned the company create forms and changed the primary create/save action to green. + - Stored the selected country on the company address relationship during create and update. - version: 1.2.0 date: 2026-07-02 changes: diff --git a/Modules/Company/module.json b/Modules/Company/module.json index 1689228d..6c43da16 100644 --- a/Modules/Company/module.json +++ b/Modules/Company/module.json @@ -1,7 +1,7 @@ { "name": "Company", "alias": "company", - "version": "1.2.0", + "version": "1.2.1", "description": "", "keywords": [], "priority": 0, diff --git a/Modules/Countries/Http/Controllers/CountriesController.php b/Modules/Countries/Http/Controllers/CountriesController.php index a6179046..e8ff25e7 100644 --- a/Modules/Countries/Http/Controllers/CountriesController.php +++ b/Modules/Countries/Http/Controllers/CountriesController.php @@ -13,10 +13,7 @@ use Modules\Countries\Support\CountryUsageReport; class CountriesController extends Controller { - /** - * Display ISO 3166 country records. - */ - public function index(Request $request): View + public function __invoke(Request $request): View { $search = trim((string) $request->query('search', '')); $status = (string) $request->query('status', 'all'); @@ -40,80 +37,78 @@ class CountriesController extends Controller ->when($status === 'active', fn ($query) => $query->where('is_active', true)) ->when($status === 'inactive', fn ($query) => $query->where('is_active', false)) ->when($region !== 'all' && $region !== '', fn ($query) => $query->where('region', $region)) - ->ordered() + ->orderByRaw('LOWER(COALESCE(display_name, common_name, name)) ASC') ->paginate(50) ->withQueryString(); $activeCount = Country::query()->where('is_active', true)->count(); $inactiveCount = Country::query()->where('is_active', false)->count(); $totalCount = Country::query()->count(); - $regions = Country::query()->whereNotNull('region')->distinct()->orderBy('region')->pluck('region'); - return view('countries::index', compact('countries', 'activeCount', 'inactiveCount', 'totalCount', 'search', 'status', 'region', 'regions')); + $regions = Country::query() + ->whereNotNull('region') + ->distinct() + ->orderBy('region') + ->pluck('region'); + + return view('countries::index', compact( + 'countries', + 'activeCount', + 'inactiveCount', + 'totalCount', + 'search', + 'status', + 'region', + 'regions' + )); } - /** - * Show the country creation form. - */ public function create(): View { return view('countries::create', ['country' => new Country]); } - /** - * Store a new ISO country record. - */ public function store(StoreCountryRequest $request): RedirectResponse { $country = Country::query()->create($this->payload($request)); - return redirect()->route('countries.show', $country)->with('success', 'Country created.'); + return redirect() + ->route('countries.show', $country) + ->with('success', 'Country created.'); } - /** - * Show a country record. - */ public function show(Country $country, CountryUsageReport $usageReport): View { $country->load(['subdivisions' => fn ($query) => $query->ordered()->limit(250)]); + $usageRows = $usageReport->forCountry($country); return view('countries::show', compact('country', 'usageRows')); } - /** - * Show the edit form. - */ public function edit(Country $country): View { return view('countries::edit', compact('country')); } - /** - * Update all editable ISO/support fields. - */ public function update(UpdateCountryRequest $request, Country $country): RedirectResponse { $country->update($this->payload($request)); - return redirect()->route('countries.show', $country)->with('success', 'Country updated.'); + return redirect() + ->route('countries.show', $country) + ->with('success', 'Country updated.'); } - /** - * Soft-delete a custom/retired country record. - */ public function destroy(Country $country): RedirectResponse { $country->delete(); - return redirect()->route('countries.index')->with('success', 'Country archived.'); + return redirect() + ->route('countries.index') + ->with('success', 'Country archived.'); } - /** - * Build the database payload from validated request data. - * - * @return array - */ private function payload(StoreCountryRequest|UpdateCountryRequest $request): array { $data = $request->validated(); @@ -130,7 +125,10 @@ class CountriesController extends Controller foreach (['languages', 'timezones', 'metadata'] as $jsonField) { $value = $data[$jsonField] ?? null; - $data[$jsonField] = blank($value) ? null : json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR); + + $data[$jsonField] = blank($value) + ? null + : json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR); } foreach (['is_active', 'independent', 'un_member'] as $booleanField) { diff --git a/Modules/Countries/Routes/web.php b/Modules/Countries/Routes/web.php index 9bf3c4ee..eaa78263 100644 --- a/Modules/Countries/Routes/web.php +++ b/Modules/Countries/Routes/web.php @@ -6,7 +6,7 @@ use Modules\Countries\Http\Controllers\CountrySyncController; use Modules\Countries\Http\Controllers\ReferenceDataController; Route::prefix('countries')->name('countries.')->middleware(['web', 'auth'])->group(function (): void { - Route::get('/', [CountriesController::class, 'index'])->name('index'); + Route::get('/', CountriesController::class)->name('index'); Route::get('/create', [CountriesController::class, 'create'])->name('create'); Route::post('/', [CountriesController::class, 'store'])->name('store'); Route::post('/sync', CountrySyncController::class)->name('sync'); diff --git a/resources/css/app.css b/resources/css/app.css index f677a995..280713c7 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1410,199 +1410,130 @@ html[data-wms-sidebar="collapsed"] .wms-sidebar { } - -@layer components { - .wms-select2-field .select2-container { - width: 100% !important; - display: block; - } - - .wms-select2-field .select2-container--default .select2-selection--single { - position: relative; - min-height: 2.75rem; - border: 1px solid rgb(212 212 216); - border-radius: 0.85rem; - background: linear-gradient(180deg, rgb(255 255 255), rgb(250 250 250)); - box-shadow: 0 1px 2px rgb(15 23 42 / 0.05); - transition: border-color 140ms ease, box-shadow 140ms ease, background 140ms ease; - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__rendered { - min-height: 2.75rem; - padding-left: 0.95rem; - padding-right: 3rem; - color: rgb(24 24 27); - font-size: 0.875rem; - font-weight: 650; - line-height: 2.75rem; - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__placeholder { - color: rgb(113 113 122); - font-weight: 550; - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__clear { - position: absolute; - right: 2.6rem; - top: 0.62rem; - z-index: 2; - width: 1.35rem; - height: 1.35rem; - border-radius: 999px; - color: rgb(113 113 122); - font-size: 1rem; - font-weight: 700; - line-height: 1.25rem; - text-align: center; - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__clear:hover { - background: rgb(244 244 245); - color: rgb(39 39 42); - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__arrow { - top: 0.45rem; - right: 0.55rem; - width: 1.85rem; - height: 1.85rem; - border-radius: 0.65rem; - background: rgb(244 244 245); - border: 1px solid rgb(228 228 231); - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__arrow b { - border-color: rgb(82 82 91) transparent transparent transparent; - border-width: 5px 4px 0 4px; - margin-left: -4px; - margin-top: -2px; - } - - .wms-select2-field .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { - border-color: transparent transparent rgb(82 82 91) transparent; - border-width: 0 4px 5px 4px; - } - - .wms-select2-field .select2-container--default.select2-container--focus .select2-selection--single, - .wms-select2-field .select2-container--default.select2-container--open .select2-selection--single { - border-color: rgb(22 163 74); - background: white; - box-shadow: 0 0 0 4px rgb(34 197 94 / 0.14), 0 10px 24px rgb(15 23 42 / 0.08); - } - - .select2-container--default .select2-dropdown { - overflow: hidden; - margin-top: 0.35rem; - border: 1px solid rgb(212 212 216); - border-radius: 1rem; - background: white; - box-shadow: 0 24px 60px rgb(15 23 42 / 0.18); - } - - .select2-container--default .select2-search--dropdown { - padding: 0.75rem; - border-bottom: 1px solid rgb(244 244 245); - } - - .select2-container--default .select2-search--dropdown .select2-search__field { - min-height: 2.5rem; - border: 1px solid rgb(212 212 216); - border-radius: 0.75rem; - padding: 0.55rem 0.8rem; - background: rgb(250 250 250); - color: rgb(24 24 27); - font-size: 0.875rem; - font-weight: 600; - outline: none; - } - - .select2-container--default .select2-search--dropdown .select2-search__field:focus { - border-color: rgb(22 163 74); - background: white; - box-shadow: 0 0 0 4px rgb(34 197 94 / 0.14); - } - - .select2-container--default .select2-results > .select2-results__options { - max-height: 16rem; - padding: 0.35rem; - } - - .select2-container--default .select2-results__option { - margin: 0.1rem 0; - border-radius: 0.65rem; - padding: 0.65rem 0.8rem; - color: rgb(39 39 42); - font-size: 0.875rem; - font-weight: 650; - } - - .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable { - background: rgb(22 163 74); - color: white; - } - - .select2-container--default .select2-results__option--selected { - background: rgb(240 253 244); - color: rgb(21 128 61); - } - - .dark .wms-select2-field .select2-container--default .select2-selection--single { - border-color: rgb(63 63 70); - background: linear-gradient(180deg, rgb(24 24 27), rgb(9 9 11)); - } - - .dark .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__rendered { - color: white; - } - - .dark .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__placeholder { - color: rgb(161 161 170); - } - - .dark .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__arrow { - border-color: rgb(63 63 70); - background: rgb(39 39 42); - } - - .dark .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__arrow b { - border-color: rgb(212 212 216) transparent transparent transparent; - } - - .dark .wms-select2-field .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { - border-color: transparent transparent rgb(212 212 216) transparent; - } - - .dark .wms-select2-field .select2-container--default.select2-container--focus .select2-selection--single, - .dark .wms-select2-field .select2-container--default.select2-container--open .select2-selection--single { - border-color: rgb(34 197 94); - background: rgb(9 9 11); - box-shadow: 0 0 0 4px rgb(34 197 94 / 0.16); - } - - .dark .select2-container--default .select2-dropdown { - border-color: rgb(63 63 70); - background: rgb(24 24 27); - color: rgb(244 244 245); - box-shadow: 0 24px 60px rgb(0 0 0 / 0.35); - } - - .dark .select2-container--default .select2-search--dropdown { - border-bottom-color: rgb(39 39 42); - } - - .dark .select2-container--default .select2-search--dropdown .select2-search__field { - border-color: rgb(63 63 70); - background: rgb(9 9 11); - color: white; - } - - .dark .select2-container--default .select2-results__option { - color: rgb(244 244 245); - } - - .dark .select2-container--default .select2-results__option--selected { - background: rgb(20 83 45); - color: rgb(220 252 231); - } +/* Company country Select2 */ +.wms-country-select + .select2-container { + display: block; + width: 100% !important; + margin-top: 0.5rem; +} + +.wms-country-select + .select2-container .select2-selection--single { + height: 3rem; + min-height: 3rem; + display: flex; + align-items: center; + border-radius: 0.75rem; + border: 1px solid rgb(212 212 216); + background: rgb(255 255 255); + box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + transition: border-color 150ms ease, box-shadow 150ms ease, background-color 150ms ease; +} + +.dark .wms-country-select + .select2-container .select2-selection--single { + border-color: rgb(63 63 70); + background: rgb(9 9 11); +} + +.wms-country-select + .select2-container .select2-selection--single .select2-selection__rendered { + line-height: 3rem; + width: 100%; + padding-left: 1rem; + padding-right: 2.75rem; + color: rgb(24 24 27); + font-size: 0.875rem; +} + +.dark .wms-country-select + .select2-container .select2-selection--single .select2-selection__rendered { + color: rgb(244 244 245); +} + +.wms-country-select + .select2-container .select2-selection--single .select2-selection__placeholder { + color: rgb(113 113 122); +} + +.dark .wms-country-select + .select2-container .select2-selection--single .select2-selection__placeholder { + color: rgb(161 161 170); +} + +.wms-country-select + .select2-container .select2-selection--single .select2-selection__arrow { + height: 3rem; + right: 0.75rem; + top: 0; +} + +.wms-country-select + .select2-container .select2-selection--single .select2-selection__arrow b { + border-color: rgb(113 113 122) transparent transparent transparent; +} + +.wms-country-select + .select2-container .select2-selection--single .select2-selection__clear { + order: 2; + margin-right: 0.5rem; + color: rgb(113 113 122); + font-size: 1.125rem; + font-weight: 700; +} + +.wms-country-select + .select2-container.select2-container--focus .select2-selection--single, +.wms-country-select + .select2-container.select2-container--open .select2-selection--single { + border-color: rgb(59 130 246); + box-shadow: 0 0 0 3px rgb(59 130 246 / 0.28); +} + +.select2-container--open .select2-dropdown { + border: 1px solid rgb(63 63 70); + border-radius: 0.875rem; + overflow: hidden; + background: rgb(255 255 255); + box-shadow: 0 20px 35px -15px rgb(0 0 0 / 0.45); +} + +.dark .select2-container--open .select2-dropdown { + background: rgb(24 24 27); + border-color: rgb(63 63 70); +} + +.select2-container--default .select2-search--dropdown { + padding: 0.75rem; + border-bottom: 1px solid rgb(228 228 231); +} + +.dark .select2-container--default .select2-search--dropdown { + border-bottom-color: rgb(63 63 70); +} + +.select2-container--default .select2-search--dropdown .select2-search__field { + min-height: 2.5rem; + border-radius: 0.75rem; + border: 1px solid rgb(212 212 216); + padding: 0.5rem 0.75rem; + color: rgb(24 24 27); + outline: none; +} + +.dark .select2-container--default .select2-search--dropdown .select2-search__field { + border-color: rgb(63 63 70); + background: rgb(9 9 11); + color: rgb(244 244 245); +} + +.select2-container--default .select2-results__option { + padding: 0.7rem 0.9rem; + font-size: 0.875rem; + color: rgb(39 39 42); +} + +.dark .select2-container--default .select2-results__option { + color: rgb(228 228 231); +} + +.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable, +.select2-container--default .select2-results__option--highlighted[aria-selected] { + background: rgb(37 99 235); + color: white; +} + +.select2-container--default .select2-results__option--selected, +.select2-container--default .select2-results__option[aria-selected="true"] { + background: rgb(22 101 52); + color: white; } diff --git a/resources/js/app.js b/resources/js/app.js index ee6d0713..900aeba2 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -167,7 +167,7 @@ function initialiseSelect2() { return; } - $('[data-helpdesk-select2], [data-wms-select2]').each(function () { + $('[data-helpdesk-select2], [data-company-country-select2]').each(function () { const $select = $(this); if ($select.hasClass('select2-hidden-accessible')) { @@ -176,15 +176,17 @@ function initialiseSelect2() { const isMultiple = $select.prop('multiple'); + const placeholder = + $select.data('placeholder') || + $select.find('option[value=""]').first().text() || + (isMultiple ? 'Select options' : 'Select option'); + $select.select2({ width: '100%', allowClear: !isMultiple, closeOnSelect: !isMultiple, - dropdownAutoWidth: true, - placeholder: - $select.data('placeholder') || - $select.find('option[value=""]').first().text() || - (isMultiple ? 'Select options' : 'Select option'), + dropdownAutoWidth: false, + placeholder, }); $select.off('change.select2-livewire');