From d2c1f744c3ab2127380f8f8a4d08ae9476dad83e Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Tue, 7 Jul 2026 12:14:24 +0100 Subject: [PATCH] interim fix for drop downs, BROKEN FIX --- ...3000_add_country_id_to_companies_table.php | 26 - .../Http/Controllers/CompanyController.php | 141 -- Company/Models/Company.php | 100 -- Company/Resources/views/_form.blade.php | 83 - Company/Resources/views/index.blade.php | 174 -- Modules/Company/Config/.gitkeep | 0 Modules/Company/Config/config.php | 8 - Modules/Company/Console/.gitkeep | 0 Modules/Company/Database/Migrations/.gitkeep | 0 ..._120000_create_company_addresses_table.php | 40 - ..._121000_create_company_divisions_table.php | 34 - ...000_create_company_division_user_table.php | 30 - ...ate_company_director_assignments_table.php | 32 - ...00_add_division_id_to_warehouses_table.php | 27 - Modules/Company/Database/Seeders/.gitkeep | 0 .../Seeders/CompanyDatabaseSeeder.php | 21 - Modules/Company/Database/factories/.gitkeep | 0 Modules/Company/Helpers/.gitkeep | 0 Modules/Company/Helpers/Helper.php | 12 - Modules/Company/Http/Controllers/.gitkeep | 0 .../Controllers/CompanyAddressController.php | 61 - .../Http/Controllers/CompanyController.php | 62 +- .../Controllers/CompanyDivisionController.php | 54 - .../CompanyMembershipController.php | 83 - .../CompanyWarehouseAssignmentController.php | 25 - Modules/Company/Http/Middleware/.gitkeep | 0 Modules/Company/Http/Requests/.gitkeep | 0 Modules/Company/Models/.gitkeep | 0 Modules/Company/Models/CompanyAddress.php | 54 - .../Models/CompanyDirectorAssignment.php | 34 - Modules/Company/Models/CompanyDivision.php | 62 - Modules/Company/Providers/.gitkeep | 0 .../Providers/CompanyServiceProvider.php | 155 -- .../Providers/RouteServiceProvider.php | 70 - Modules/Company/Resources/assets/.gitkeep | 0 Modules/Company/Resources/assets/js/app.js | 0 .../Company/Resources/assets/sass/app.scss | 0 Modules/Company/Resources/lang/.gitkeep | 0 Modules/Company/Resources/views/.gitkeep | 0 .../Company/Resources/views/_form.blade.php | 208 +-- .../Company/Resources/views/create.blade.php | 16 - .../Company/Resources/views/edit.blade.php | 17 - .../Company/Resources/views/index.blade.php | 133 +- .../views/partials/address-form.blade.php | 47 - .../Company/Resources/views/show.blade.php | 140 -- Modules/Company/Routes/.gitkeep | 0 Modules/Company/Routes/api.php | 18 - Modules/Company/Routes/web.php | 27 - Modules/Company/Support/.gitkeep | 0 Modules/Company/Support/ModuleInfo.php | 26 - Modules/Company/Tests/Browser/.gitkeep | 0 .../CompanyAdministrationBrowserTest.php | 36 - Modules/Company/Tests/Feature/.gitkeep | 0 .../CompanyAdministrationRoutesTest.php | 73 - .../Tests/Feature/CompanyStructureTest.php | 70 - Modules/Company/Tests/Unit/.gitkeep | 0 Modules/Company/Traits/.gitkeep | 0 Modules/Company/View/.gitkeep | 0 Modules/Company/changelog.yaml | 32 - Modules/Company/composer.json | 29 - Modules/Company/module.json | 14 - Modules/Company/package.json | 17 - Modules/Company/webpack.mix.js | 11 - css/app.css | 1531 ----------------- js/app.js | 222 --- resources/css/app.css | 204 ++- resources/js/app.js | 14 +- 67 files changed, 168 insertions(+), 4105 deletions(-) delete mode 100644 Company/Database/Migrations/2026_07_07_093000_add_country_id_to_companies_table.php delete mode 100644 Company/Http/Controllers/CompanyController.php delete mode 100644 Company/Models/Company.php delete mode 100644 Company/Resources/views/_form.blade.php delete mode 100644 Company/Resources/views/index.blade.php delete mode 100644 Modules/Company/Config/.gitkeep delete mode 100644 Modules/Company/Config/config.php delete mode 100644 Modules/Company/Console/.gitkeep delete mode 100644 Modules/Company/Database/Migrations/.gitkeep delete mode 100644 Modules/Company/Database/Migrations/2026_07_02_120000_create_company_addresses_table.php delete mode 100644 Modules/Company/Database/Migrations/2026_07_02_121000_create_company_divisions_table.php delete mode 100644 Modules/Company/Database/Migrations/2026_07_02_122000_create_company_division_user_table.php delete mode 100644 Modules/Company/Database/Migrations/2026_07_02_123000_create_company_director_assignments_table.php delete mode 100644 Modules/Company/Database/Migrations/2026_07_02_124000_add_division_id_to_warehouses_table.php delete mode 100644 Modules/Company/Database/Seeders/.gitkeep delete mode 100644 Modules/Company/Database/Seeders/CompanyDatabaseSeeder.php delete mode 100644 Modules/Company/Database/factories/.gitkeep delete mode 100644 Modules/Company/Helpers/.gitkeep delete mode 100644 Modules/Company/Helpers/Helper.php delete mode 100644 Modules/Company/Http/Controllers/.gitkeep delete mode 100644 Modules/Company/Http/Controllers/CompanyAddressController.php delete mode 100644 Modules/Company/Http/Controllers/CompanyDivisionController.php delete mode 100644 Modules/Company/Http/Controllers/CompanyMembershipController.php delete mode 100644 Modules/Company/Http/Controllers/CompanyWarehouseAssignmentController.php delete mode 100644 Modules/Company/Http/Middleware/.gitkeep delete mode 100644 Modules/Company/Http/Requests/.gitkeep delete mode 100644 Modules/Company/Models/.gitkeep delete mode 100644 Modules/Company/Models/CompanyAddress.php delete mode 100644 Modules/Company/Models/CompanyDirectorAssignment.php delete mode 100644 Modules/Company/Models/CompanyDivision.php delete mode 100644 Modules/Company/Providers/.gitkeep delete mode 100644 Modules/Company/Providers/CompanyServiceProvider.php delete mode 100644 Modules/Company/Providers/RouteServiceProvider.php delete mode 100644 Modules/Company/Resources/assets/.gitkeep delete mode 100644 Modules/Company/Resources/assets/js/app.js delete mode 100644 Modules/Company/Resources/assets/sass/app.scss delete mode 100644 Modules/Company/Resources/lang/.gitkeep delete mode 100644 Modules/Company/Resources/views/.gitkeep delete mode 100644 Modules/Company/Resources/views/create.blade.php delete mode 100644 Modules/Company/Resources/views/edit.blade.php delete mode 100644 Modules/Company/Resources/views/partials/address-form.blade.php delete mode 100644 Modules/Company/Resources/views/show.blade.php delete mode 100644 Modules/Company/Routes/.gitkeep delete mode 100644 Modules/Company/Routes/api.php delete mode 100644 Modules/Company/Routes/web.php delete mode 100644 Modules/Company/Support/.gitkeep delete mode 100644 Modules/Company/Support/ModuleInfo.php delete mode 100644 Modules/Company/Tests/Browser/.gitkeep delete mode 100644 Modules/Company/Tests/Browser/CompanyAdministrationBrowserTest.php delete mode 100644 Modules/Company/Tests/Feature/.gitkeep delete mode 100644 Modules/Company/Tests/Feature/CompanyAdministrationRoutesTest.php delete mode 100644 Modules/Company/Tests/Feature/CompanyStructureTest.php delete mode 100644 Modules/Company/Tests/Unit/.gitkeep delete mode 100644 Modules/Company/Traits/.gitkeep delete mode 100644 Modules/Company/View/.gitkeep delete mode 100644 Modules/Company/changelog.yaml delete mode 100644 Modules/Company/composer.json delete mode 100644 Modules/Company/module.json delete mode 100644 Modules/Company/package.json delete mode 100644 Modules/Company/webpack.mix.js delete mode 100644 css/app.css delete mode 100644 js/app.js diff --git a/Company/Database/Migrations/2026_07_07_093000_add_country_id_to_companies_table.php b/Company/Database/Migrations/2026_07_07_093000_add_country_id_to_companies_table.php deleted file mode 100644 index 0d0a61f0..00000000 --- a/Company/Database/Migrations/2026_07_07_093000_add_country_id_to_companies_table.php +++ /dev/null @@ -1,26 +0,0 @@ -foreignId('country_id') - ->nullable() - ->after('billing_email') - ->constrained('countries') - ->nullOnDelete(); - }); - } - - public function down(): void - { - Schema::table('companies', function (Blueprint $table): void { - $table->dropConstrainedForeignId('country_id'); - }); - } -}; diff --git a/Company/Http/Controllers/CompanyController.php b/Company/Http/Controllers/CompanyController.php deleted file mode 100644 index 0880b292..00000000 --- a/Company/Http/Controllers/CompanyController.php +++ /dev/null @@ -1,141 +0,0 @@ - Company::query() - ->with(['address.country', 'country']) - ->withCount(['users', 'warehouses', 'licenseKeys', 'divisions']) - ->latest('id') - ->paginate(20), - 'countries' => $this->countries(), - ]); - } - - /** - * Show the create company screen. - */ - public function create() - { - return view('company::create', [ - 'company' => new Company([ - 'status' => 'active', - 'timezone' => 'Europe/London', - ]), - 'countries' => $this->countries(), - ]); - } - - /** - * Store a company tenant. - */ - public function store(Request $request) - { - $company = Company::create($this->validated($request)); - - return redirect() - ->route('platform.companies.index') - ->with('status', 'Company created.'); - } - - /** - * Display a company workbench. - */ - public function show(Company $company) - { - $company->load([ - 'address.country', - 'divisions.address.country', - 'divisions.warehouses', - 'divisions.users', - 'warehouses.address.country', - 'warehouses.division', - 'directorAssignments.user', - 'directorAssignments.division', - ])->loadCount(['users', 'warehouses', 'licenseKeys', 'payments', 'divisions']); - - return view('company::show', [ - 'company' => $company, - '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']), - 'companyUsers' => $company->users()->orderBy('name')->get(['id', 'name', 'email', 'company_id']), - 'unassignedWarehouses' => $company->warehouses()->whereNull('division_id')->orderBy('name')->get(), - ]); - } - - /** - * Show the edit company screen. - */ - public function edit(Company $company) - { - return view('company::edit', [ - 'company' => $company->loadMissing(['address.country', 'country']), - 'countries' => $this->countries(), - ]); - } - - /** - * Update a company tenant. - */ - public function update(Request $request, Company $company) - { - $company->update($this->validated($request, $company)); - - return redirect() - ->route('platform.companies.show', $company) - ->with('status', 'Company updated.'); - } - - /** - * Delete a company tenant. - */ - public function destroy(Company $company) - { - abort_if($company->is_system_company, 403, 'The system company cannot be deleted.'); - - $company->delete(); - - return redirect() - ->route('platform.companies.index') - ->with('status', 'Company deleted.'); - } - - private function validated(Request $request, ?Company $company = null): array - { - $data = $request->validate([ - '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)], - ]); - - $data['slug'] = Str::slug($data['name']); - $data['timezone'] = $company?->timezone ?: 'Europe/London'; - - return $data; - } - - private function countries() - { - return Country::query() - ->active() - ->ordered() - ->get(['id', 'alpha2', 'name', 'display_name', 'common_name', 'sort_order']); - } -} diff --git a/Company/Models/Company.php b/Company/Models/Company.php deleted file mode 100644 index e3dd2a77..00000000 --- a/Company/Models/Company.php +++ /dev/null @@ -1,100 +0,0 @@ - 'array', - 'is_system_company' => 'boolean', - ]; - - protected static function booted(): void - { - static::creating(function (Company $company): void { - $company->uuid ??= (string) Str::uuid(); - $company->slug ??= Str::slug($company->name); - $company->status ??= 'active'; - $company->timezone ??= 'Europe/London'; - }); - } - - - - public function country(): BelongsTo - { - return $this->belongsTo(Country::class); - } - - public function address(): MorphOne - { - return $this->morphOne(CompanyAddress::class, 'addressable'); - } - - public function divisions(): HasMany - { - return $this->hasMany(CompanyDivision::class); - } - - public function directorAssignments(): HasMany - { - return $this->hasMany(CompanyDirectorAssignment::class); - } - - public function users(): HasMany - { - return $this->hasMany(User::class); - } - - public function licenseKeys(): HasMany - { - return $this->hasMany(LicenseKey::class); - } - - public function payments(): HasMany - { - return $this->hasMany(Payment::class); - } - - public function warehouses(): HasMany - { - return $this->hasMany(Warehouse::class); - } - - public function hasActiveLicense(): bool - { - if ($this->is_system_company) { - return true; - } - - return $this->licenseKeys() - ->where('is_active', true) - ->where(function ($query): void { - $query->whereNull('starts_at')->orWhere('starts_at', '<=', now()); - }) - ->where(function ($query): void { - $query->whereNull('expires_at')->orWhere('expires_at', '>=', now()); - }) - ->whereNull('revoked_at') - ->exists(); - } -} diff --git a/Company/Resources/views/_form.blade.php b/Company/Resources/views/_form.blade.php deleted file mode 100644 index e044d2d7..00000000 --- a/Company/Resources/views/_form.blade.php +++ /dev/null @@ -1,83 +0,0 @@ -@csrf - -
-
- - - @error('name') -

{{ $message }}

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

{{ $message }}

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

{{ $message }}

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

{{ $message }}

- @enderror -
-
- -
- - - - Cancel - -
diff --git a/Company/Resources/views/index.blade.php b/Company/Resources/views/index.blade.php deleted file mode 100644 index b4515449..00000000 --- a/Company/Resources/views/index.blade.php +++ /dev/null @@ -1,174 +0,0 @@ - -
- - - - - @if (session('status')) -
- {{ session('status') }} -
- @endif - -
-
-
-

Create company

-

Add a new tenant without leaving the administration index.

-
- - - Open full form - -
- -
- @csrf - - - - - - - -
- -
- - -
-
-
- - - - - - - - - - - - - - - @forelse ($companies as $company) - - - - - - - - - - - - - - - - @empty - - - - @endforelse - -
NameUsersDivisionsWarehousesLicencesStatusActions
- {{ $company->name }} - - {{ $company->users_count }} - - {{ $company->divisions_count }} - - {{ $company->warehouses_count }} - - {{ $company->license_keys_count }} - - - {{ $company->is_system_company ? 'System' : ucfirst($company->status) }} - - -
- - View - - - - Edit - - - @unless ($company->is_system_company) -
- @csrf - @method('DELETE') - - -
- @endunless -
-
- No companies found. -
-
- - {{ $companies->links() }} -
-
diff --git a/Modules/Company/Config/.gitkeep b/Modules/Company/Config/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Config/config.php b/Modules/Company/Config/config.php deleted file mode 100644 index 2f8ac5ec..00000000 --- a/Modules/Company/Config/config.php +++ /dev/null @@ -1,8 +0,0 @@ - 'Company', - 'features' => - [ - ], -]; diff --git a/Modules/Company/Console/.gitkeep b/Modules/Company/Console/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Database/Migrations/.gitkeep b/Modules/Company/Database/Migrations/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Database/Migrations/2026_07_02_120000_create_company_addresses_table.php b/Modules/Company/Database/Migrations/2026_07_02_120000_create_company_addresses_table.php deleted file mode 100644 index 627f7f17..00000000 --- a/Modules/Company/Database/Migrations/2026_07_02_120000_create_company_addresses_table.php +++ /dev/null @@ -1,40 +0,0 @@ -id(); - $table->morphs('addressable'); - $table->string('label', 80)->default('Registered office'); - $table->string('attention')->nullable(); - $table->string('line_1'); - $table->string('line_2')->nullable(); - $table->string('line_3')->nullable(); - $table->string('locality')->comment('City, town or locality.'); - $table->string('administrative_area')->nullable()->comment('County, state, province or region.'); - $table->string('postal_code', 32)->nullable(); - $table->foreignId('country_id')->constrained('countries')->restrictOnDelete(); - $table->foreignId('country_subdivision_id')->nullable()->constrained('country_subdivisions')->nullOnDelete(); - $table->decimal('latitude', 10, 7)->nullable(); - $table->decimal('longitude', 10, 7)->nullable(); - $table->boolean('is_primary')->default(true); - $table->json('metadata')->nullable(); - $table->timestamps(); - - $table->unique(['addressable_type', 'addressable_id'], 'company_addresses_one_per_owner_unique'); - $table->index(['country_id', 'locality']); - $table->index(['postal_code']); - }); - } - - public function down(): void - { - Schema::dropIfExists('company_addresses'); - } -}; diff --git a/Modules/Company/Database/Migrations/2026_07_02_121000_create_company_divisions_table.php b/Modules/Company/Database/Migrations/2026_07_02_121000_create_company_divisions_table.php deleted file mode 100644 index 44cad595..00000000 --- a/Modules/Company/Database/Migrations/2026_07_02_121000_create_company_divisions_table.php +++ /dev/null @@ -1,34 +0,0 @@ -id(); - $table->foreignId('company_id')->constrained('companies')->cascadeOnDelete(); - $table->uuid('uuid')->unique(); - $table->string('name'); - $table->string('code', 40)->nullable(); - $table->string('slug'); - $table->string('status', 32)->default('active')->index(); - $table->text('description')->nullable(); - $table->json('metadata')->nullable(); - $table->timestamps(); - $table->softDeletes(); - - $table->unique(['company_id', 'slug']); - $table->unique(['company_id', 'code']); - $table->index(['company_id', 'status', 'name']); - }); - } - - public function down(): void - { - Schema::dropIfExists('company_divisions'); - } -}; diff --git a/Modules/Company/Database/Migrations/2026_07_02_122000_create_company_division_user_table.php b/Modules/Company/Database/Migrations/2026_07_02_122000_create_company_division_user_table.php deleted file mode 100644 index 16fed17b..00000000 --- a/Modules/Company/Database/Migrations/2026_07_02_122000_create_company_division_user_table.php +++ /dev/null @@ -1,30 +0,0 @@ -id(); - $table->foreignId('company_id')->constrained('companies')->cascadeOnDelete(); - $table->foreignId('company_division_id')->constrained('company_divisions')->cascadeOnDelete(); - $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); - $table->string('role', 80)->default('member')->index(); - $table->boolean('is_director')->default(false)->index(); - $table->timestamps(); - - $table->unique(['company_division_id', 'user_id']); - $table->index(['company_id', 'user_id']); - $table->index(['company_id', 'is_director']); - }); - } - - public function down(): void - { - Schema::dropIfExists('company_division_user'); - } -}; diff --git a/Modules/Company/Database/Migrations/2026_07_02_123000_create_company_director_assignments_table.php b/Modules/Company/Database/Migrations/2026_07_02_123000_create_company_director_assignments_table.php deleted file mode 100644 index c539c515..00000000 --- a/Modules/Company/Database/Migrations/2026_07_02_123000_create_company_director_assignments_table.php +++ /dev/null @@ -1,32 +0,0 @@ -id(); - $table->foreignId('company_id')->constrained('companies')->cascadeOnDelete(); - $table->foreignId('company_division_id')->nullable()->constrained('company_divisions')->cascadeOnDelete(); - $table->foreignId('user_id')->constrained('users')->cascadeOnDelete(); - $table->string('coverage_type', 32)->default('company')->index(); - $table->date('starts_at')->nullable(); - $table->date('ends_at')->nullable(); - $table->text('notes')->nullable(); - $table->timestamps(); - - $table->unique(['company_id', 'company_division_id', 'user_id'], 'company_director_coverage_unique'); - $table->index(['company_id', 'coverage_type']); - $table->index(['user_id', 'coverage_type']); - }); - } - - public function down(): void - { - Schema::dropIfExists('company_director_assignments'); - } -}; diff --git a/Modules/Company/Database/Migrations/2026_07_02_124000_add_division_id_to_warehouses_table.php b/Modules/Company/Database/Migrations/2026_07_02_124000_add_division_id_to_warehouses_table.php deleted file mode 100644 index b7c52b09..00000000 --- a/Modules/Company/Database/Migrations/2026_07_02_124000_add_division_id_to_warehouses_table.php +++ /dev/null @@ -1,27 +0,0 @@ -foreignId('division_id')->nullable()->after('company_id')->constrained('company_divisions')->nullOnDelete(); - $table->index(['company_id', 'division_id']); - } - }); - } - - public function down(): void - { - Schema::table('warehouses', function (Blueprint $table): void { - if (Schema::hasColumn('warehouses', 'division_id')) { - $table->dropConstrainedForeignId('division_id'); - } - }); - } -}; diff --git a/Modules/Company/Database/Seeders/.gitkeep b/Modules/Company/Database/Seeders/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Database/Seeders/CompanyDatabaseSeeder.php b/Modules/Company/Database/Seeders/CompanyDatabaseSeeder.php deleted file mode 100644 index 49302c7a..00000000 --- a/Modules/Company/Database/Seeders/CompanyDatabaseSeeder.php +++ /dev/null @@ -1,21 +0,0 @@ -call("OthersTableSeeder"); - } -} diff --git a/Modules/Company/Database/factories/.gitkeep b/Modules/Company/Database/factories/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Helpers/.gitkeep b/Modules/Company/Helpers/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Helpers/Helper.php b/Modules/Company/Helpers/Helper.php deleted file mode 100644 index 7693b057..00000000 --- a/Modules/Company/Helpers/Helper.php +++ /dev/null @@ -1,12 +0,0 @@ -upsertAddress($request, $company); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Company address updated.'); - } - - public function updateDivision(Request $request, Company $company, CompanyDivision $division) - { - abort_unless($division->company_id === $company->id, 404); - - $this->upsertAddress($request, $division, 'Operating address'); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Division address updated.'); - } - - public function updateWarehouse(Request $request, Company $company, Warehouse $warehouse) - { - abort_unless($warehouse->company_id === $company->id, 404); - - $this->upsertAddress($request, $warehouse, 'Warehouse address'); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Warehouse address updated.'); - } - - private function upsertAddress(Request $request, object $owner, string $defaultLabel = 'Registered office'): void - { - $data = $request->validate([ - 'label' => ['nullable', 'string', 'max:80'], - 'attention' => ['nullable', 'string', 'max:255'], - 'line_1' => ['required', 'string', 'max:255'], - 'line_2' => ['nullable', 'string', 'max:255'], - 'line_3' => ['nullable', 'string', 'max:255'], - 'locality' => ['required', 'string', 'max:255'], - 'administrative_area' => ['nullable', 'string', 'max:255'], - 'postal_code' => ['nullable', 'string', 'max:32'], - 'country_id' => ['required', Rule::exists('countries', 'id')->where('is_active', true)], - 'country_subdivision_id' => ['nullable', 'exists:country_subdivisions,id'], - 'latitude' => ['nullable', 'numeric', 'between:-90,90'], - 'longitude' => ['nullable', 'numeric', 'between:-180,180'], - ]); - - $data['label'] = $data['label'] ?: $defaultLabel; - $data['is_primary'] = true; - - $owner->address()->updateOrCreate([], $data); - } -} diff --git a/Modules/Company/Http/Controllers/CompanyController.php b/Modules/Company/Http/Controllers/CompanyController.php index 10a583c1..0880b292 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']) + ->with(['address.country', 'country']) ->withCount(['users', 'warehouses', 'licenseKeys', 'divisions']) ->latest('id') ->paginate(20), - 'countries' => $this->countriesForSelect(), + 'countries' => $this->countries(), ]); } @@ -36,7 +36,7 @@ class CompanyController extends Controller 'status' => 'active', 'timezone' => 'Europe/London', ]), - 'countries' => $this->countriesForSelect(), + 'countries' => $this->countries(), ]); } @@ -45,10 +45,7 @@ class CompanyController extends Controller */ public function store(Request $request) { - $validated = $this->validated($request); - - $company = Company::create($validated['company']); - $this->syncAddressCountry($company, $validated['country_id']); + $company = Company::create($this->validated($request)); return redirect() ->route('platform.companies.index') @@ -76,7 +73,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' => $this->countriesForSelect(), + 'countries' => Country::query()->active()->orderBy('sort_order')->orderBy('name')->get(['id', 'alpha2', 'name', 'display_name', 'common_name']), 'companyUsers' => $company->users()->orderBy('name')->get(['id', 'name', 'email', 'company_id']), 'unassignedWarehouses' => $company->warehouses()->whereNull('division_id')->orderBy('name')->get(), ]); @@ -88,8 +85,8 @@ class CompanyController extends Controller public function edit(Company $company) { return view('company::edit', [ - 'company' => $company->loadMissing('address.country'), - 'countries' => $this->countriesForSelect(), + 'company' => $company->loadMissing(['address.country', 'country']), + 'countries' => $this->countries(), ]); } @@ -98,10 +95,7 @@ class CompanyController extends Controller */ public function update(Request $request, Company $company) { - $validated = $this->validated($request, $company); - - $company->update($validated['company']); - $this->syncAddressCountry($company, $validated['country_id']); + $company->update($this->validated($request, $company)); return redirect() ->route('platform.companies.show', $company) @@ -128,44 +122,20 @@ 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', 'integer', Rule::exists('countries', 'id')], + 'country_id' => ['nullable', Rule::exists('countries', 'id')->where('is_active', true)], ]); - 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, - ]; + $data['slug'] = Str::slug($data['name']); + $data['timezone'] = $company?->timezone ?: 'Europe/London'; + + return $data; } - private function countriesForSelect() + private function countries() { return Country::query() ->active() - ->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, - ] - ); + ->ordered() + ->get(['id', 'alpha2', 'name', 'display_name', 'common_name', 'sort_order']); } } diff --git a/Modules/Company/Http/Controllers/CompanyDivisionController.php b/Modules/Company/Http/Controllers/CompanyDivisionController.php deleted file mode 100644 index baf3b8ed..00000000 --- a/Modules/Company/Http/Controllers/CompanyDivisionController.php +++ /dev/null @@ -1,54 +0,0 @@ -divisions()->create($this->validated($request, $company)); - - return redirect()->route('platform.companies.show', $company)->with('status', "Division {$division->name} created."); - } - - public function update(Request $request, Company $company, CompanyDivision $division) - { - abort_unless($division->company_id === $company->id, 404); - - $division->update($this->validated($request, $company, $division)); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Division updated.'); - } - - public function destroy(Company $company, CompanyDivision $division) - { - abort_unless($division->company_id === $company->id, 404); - - abort_if($division->warehouses()->exists(), 422, 'Move warehouses before deleting this division.'); - - $division->delete(); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Division deleted.'); - } - - private function validated(Request $request, Company $company, ?CompanyDivision $division = null): array - { - $data = $request->validate([ - 'name' => ['required', 'string', 'max:255'], - 'code' => ['nullable', 'string', 'max:40', Rule::unique('company_divisions', 'code')->where('company_id', $company->id)->ignore($division?->id)], - 'status' => ['required', Rule::in(['active', 'suspended', 'inactive'])], - 'description' => ['nullable', 'string', 'max:2000'], - ]); - - $data['slug'] = Str::slug($data['name']); - - return $data; - } -} diff --git a/Modules/Company/Http/Controllers/CompanyMembershipController.php b/Modules/Company/Http/Controllers/CompanyMembershipController.php deleted file mode 100644 index 7ce6d23c..00000000 --- a/Modules/Company/Http/Controllers/CompanyMembershipController.php +++ /dev/null @@ -1,83 +0,0 @@ -validate([ - 'user_id' => ['required', Rule::exists('users', 'id')->where('company_id', $company->id)], - 'company_division_id' => ['required', Rule::exists('company_divisions', 'id')->where('company_id', $company->id)], - 'role' => ['nullable', 'string', 'max:80'], - 'is_director' => ['nullable', 'boolean'], - ]); - - $division = CompanyDivision::where('company_id', $company->id)->findOrFail($data['company_division_id']); - $division->users()->syncWithoutDetaching([ - $data['user_id'] => [ - 'company_id' => $company->id, - 'role' => $data['role'] ?? 'member', - 'is_director' => (bool) ($data['is_director'] ?? false), - ], - ]); - - if ((bool) ($data['is_director'] ?? false)) { - CompanyDirectorAssignment::updateOrCreate([ - 'company_id' => $company->id, - 'company_division_id' => $division->id, - 'user_id' => $data['user_id'], - ], ['coverage_type' => 'division']); - } - - return redirect()->route('platform.companies.show', $company)->with('status', 'Division membership updated.'); - } - - public function assignDirector(Request $request, Company $company) - { - $data = $request->validate([ - 'user_id' => ['required', Rule::exists('users', 'id')->where('company_id', $company->id)], - 'coverage_type' => ['required', Rule::in(['company', 'division'])], - 'company_division_id' => ['nullable', Rule::exists('company_divisions', 'id')->where('company_id', $company->id)], - 'starts_at' => ['nullable', 'date'], - 'ends_at' => ['nullable', 'date', 'after_or_equal:starts_at'], - 'notes' => ['nullable', 'string', 'max:2000'], - ]); - - if ($data['coverage_type'] === 'company') { - $data['company_division_id'] = null; - } else { - abort_if(empty($data['company_division_id']), 422, 'Division coverage requires a division.'); - } - - CompanyDirectorAssignment::updateOrCreate([ - 'company_id' => $company->id, - 'company_division_id' => $data['company_division_id'], - 'user_id' => $data['user_id'], - ], [ - 'coverage_type' => $data['coverage_type'], - 'starts_at' => $data['starts_at'] ?? null, - 'ends_at' => $data['ends_at'] ?? null, - 'notes' => $data['notes'] ?? null, - ]); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Director coverage updated.'); - } - - public function removeDirector(Company $company, CompanyDirectorAssignment $assignment) - { - abort_unless($assignment->company_id === $company->id, 404); - - $assignment->delete(); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Director coverage removed.'); - } -} diff --git a/Modules/Company/Http/Controllers/CompanyWarehouseAssignmentController.php b/Modules/Company/Http/Controllers/CompanyWarehouseAssignmentController.php deleted file mode 100644 index 194be483..00000000 --- a/Modules/Company/Http/Controllers/CompanyWarehouseAssignmentController.php +++ /dev/null @@ -1,25 +0,0 @@ -company_id === $company->id, 404); - - $data = $request->validate([ - 'division_id' => ['nullable', Rule::exists('company_divisions', 'id')->where('company_id', $company->id)], - ]); - - $warehouse->update(['division_id' => $data['division_id'] ?? null]); - - return redirect()->route('platform.companies.show', $company)->with('status', 'Warehouse division assignment updated.'); - } -} diff --git a/Modules/Company/Http/Middleware/.gitkeep b/Modules/Company/Http/Middleware/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Http/Requests/.gitkeep b/Modules/Company/Http/Requests/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Models/.gitkeep b/Modules/Company/Models/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Models/CompanyAddress.php b/Modules/Company/Models/CompanyAddress.php deleted file mode 100644 index 1345e8fe..00000000 --- a/Modules/Company/Models/CompanyAddress.php +++ /dev/null @@ -1,54 +0,0 @@ - 'boolean', - 'metadata' => 'array', - 'latitude' => 'decimal:7', - 'longitude' => 'decimal:7', - ]; - - public function addressable(): MorphTo - { - return $this->morphTo(); - } - - public function country(): BelongsTo - { - return $this->belongsTo(Country::class); - } - - public function subdivision(): BelongsTo - { - return $this->belongsTo(CountrySubdivision::class, 'country_subdivision_id'); - } - - public function getFormattedAttribute(): string - { - return collect([ - $this->attention, - $this->line_1, - $this->line_2, - $this->line_3, - $this->locality, - $this->administrative_area, - $this->postal_code, - $this->country?->effective_name ?? $this->country?->name, - ])->filter()->implode(', '); - } -} diff --git a/Modules/Company/Models/CompanyDirectorAssignment.php b/Modules/Company/Models/CompanyDirectorAssignment.php deleted file mode 100644 index 9c2923ea..00000000 --- a/Modules/Company/Models/CompanyDirectorAssignment.php +++ /dev/null @@ -1,34 +0,0 @@ - 'date', - 'ends_at' => 'date', - ]; - - public function company(): BelongsTo - { - return $this->belongsTo(Company::class); - } - - public function division(): BelongsTo - { - return $this->belongsTo(CompanyDivision::class, 'company_division_id'); - } - - public function user(): BelongsTo - { - return $this->belongsTo(User::class); - } -} diff --git a/Modules/Company/Models/CompanyDivision.php b/Modules/Company/Models/CompanyDivision.php deleted file mode 100644 index 20ea2f29..00000000 --- a/Modules/Company/Models/CompanyDivision.php +++ /dev/null @@ -1,62 +0,0 @@ - 'array', - ]; - - protected static function booted(): void - { - static::creating(function (CompanyDivision $division): void { - $division->uuid ??= (string) Str::uuid(); - $division->slug ??= Str::slug($division->name); - $division->status ??= 'active'; - }); - } - - public function company(): BelongsTo - { - return $this->belongsTo(Company::class); - } - - public function address(): MorphOne - { - return $this->morphOne(CompanyAddress::class, 'addressable'); - } - - public function users(): BelongsToMany - { - return $this->belongsToMany(User::class, 'company_division_user') - ->withPivot(['company_id', 'role', 'is_director']) - ->withTimestamps(); - } - - public function warehouses(): HasMany - { - return $this->hasMany(Warehouse::class, 'division_id'); - } - - public function directorAssignments(): HasMany - { - return $this->hasMany(CompanyDirectorAssignment::class, 'company_division_id'); - } -} diff --git a/Modules/Company/Providers/.gitkeep b/Modules/Company/Providers/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Providers/CompanyServiceProvider.php b/Modules/Company/Providers/CompanyServiceProvider.php deleted file mode 100644 index 49f68ce4..00000000 --- a/Modules/Company/Providers/CompanyServiceProvider.php +++ /dev/null @@ -1,155 +0,0 @@ -registerTranslations(); - $this->registerConfig(); - $this->registerViews(); - $this->registerHelpers(); - $this->registerCommands(); - $this->loadMigrationsFrom(module_path(ModuleInfo::name(), 'Database/Migrations')); - } - - /** - * Register the service provider. - * - * @return void - */ - public function register(): void - { - $this->app->register(RouteServiceProvider::class); - } - - /** - * Register the helpers. - * - * @return void - */ - protected function registerHelpers(): void - { - foreach (glob(__DIR__.'/../Helpers/*.php') as $filename) { - require_once $filename; - } - } - - /** - * Dynamically Register Commands. - * - * @return void - */ - public function registerCommands(): void - { - if (!$this->app->runningInConsole()) { - return; - } - $commandPath = __DIR__ . '/../Console'; - $commands = collect(File::allFiles($commandPath)) - ->map(function ($file) { - $class = 'Modules\\' . $this->moduleName . '\\Console\\' . $file->getFilenameWithoutExtension(); - return $class; - }) - ->filter(function ($class) { - return class_exists($class); - }) - ->values() - ->all(); - $this->commands($commands); - } - - /** - * Register config. - * - * @return void - */ - protected function registerConfig(): void - { - $this->publishes([ - module_path(ModuleInfo::name(), 'Config/config.php') => config_path( ModuleInfo::nameLower(). '.php'), - ], 'config'); - $this->mergeConfigFrom( - module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower() - ); - } - - /** - * Register views. - * - * @return void - */ - public function registerViews(): void - { - $viewPath = resource_path('views/modules/' . ModuleInfo::nameLower()); - $sourcePath = module_path(ModuleInfo::name(), 'Resources/views'); - $this->publishes([ - $sourcePath => $viewPath - ], ['views', ModuleInfo::nameLower() . '-module-views']); - $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower()); - } - - /** - * Register translations. - * - * @return void - */ - public function registerTranslations(): void - { - $langPath = resource_path('lang/modules/' . ModuleInfo::nameLower()); - - if (is_dir($langPath)) { - $this->loadTranslationsFrom($langPath, ModuleInfo::nameLower()); - } else { - $this->loadTranslationsFrom(module_path(ModuleInfo::name(), 'Resources/lang'), ModuleInfo::nameLower()); - } - } - - /** - * Regsiter View Paths - * @return array - */ - private function getPublishableViewPaths(): array - { - $paths = []; - foreach (Config::get('view.paths') as $path) { - if (is_dir($path . '/modules/' . ModuleInfo::nameLower())) { - $paths[] = $path . '/modules/' . ModuleInfo::nameLower(); - } - } - return $paths; - } -} diff --git a/Modules/Company/Providers/RouteServiceProvider.php b/Modules/Company/Providers/RouteServiceProvider.php deleted file mode 100644 index 8d38c074..00000000 --- a/Modules/Company/Providers/RouteServiceProvider.php +++ /dev/null @@ -1,70 +0,0 @@ -mapApiRoutes(); - - $this->mapWebRoutes(); - } - - /** - * Define the "web" routes for the application. - * - * These routes all receive session state, CSRF protection, etc. - * - * @return void - */ - protected function mapWebRoutes() - { - Route::middleware('web') - ->namespace($this->moduleNamespace) - ->group(module_path('Company', '/Routes/web.php')); - } - - /** - * Define the "api" routes for the application. - * - * These routes are typically stateless. - * - * @return void - */ - protected function mapApiRoutes() - { - Route::prefix('api') - ->middleware('api') - ->namespace($this->moduleNamespace) - ->group(module_path('Company', '/Routes/api.php')); - } -} diff --git a/Modules/Company/Resources/assets/.gitkeep b/Modules/Company/Resources/assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Resources/assets/js/app.js b/Modules/Company/Resources/assets/js/app.js deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Resources/assets/sass/app.scss b/Modules/Company/Resources/assets/sass/app.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Resources/lang/.gitkeep b/Modules/Company/Resources/lang/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Resources/views/.gitkeep b/Modules/Company/Resources/views/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Resources/views/_form.blade.php b/Modules/Company/Resources/views/_form.blade.php index 340639fe..e044d2d7 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 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" + class="mt-2 w-full rounded-xl border-zinc-300 dark:border-zinc-700 dark:bg-zinc-950 dark:text-white" > @error('billing_email')

{{ $message }}

@@ -35,7 +35,7 @@ - - @foreach($countries as $country) - + @foreach ($countries as $country) + @endforeach @@ -73,189 +69,15 @@
-
- Cancel
- - -@once - - - - - -@endonce diff --git a/Modules/Company/Resources/views/create.blade.php b/Modules/Company/Resources/views/create.blade.php deleted file mode 100644 index a9274e88..00000000 --- a/Modules/Company/Resources/views/create.blade.php +++ /dev/null @@ -1,16 +0,0 @@ - -
- - -
-
- @include('company::_form') -
-
-
-
diff --git a/Modules/Company/Resources/views/edit.blade.php b/Modules/Company/Resources/views/edit.blade.php deleted file mode 100644 index 1fd4ea26..00000000 --- a/Modules/Company/Resources/views/edit.blade.php +++ /dev/null @@ -1,17 +0,0 @@ - -
- - -
-
- @method('PUT') - @include('company::_form') -
-
-
-
diff --git a/Modules/Company/Resources/views/index.blade.php b/Modules/Company/Resources/views/index.blade.php index ab6fc316..b4515449 100644 --- a/Modules/Company/Resources/views/index.blade.php +++ b/Modules/Company/Resources/views/index.blade.php @@ -30,86 +30,45 @@
-
+ @csrf -
-
- - - @error('name') -

{{ $message }}

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

{{ $message }}

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

{{ $message }}

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

{{ $message }}

- @enderror -
- - +
+
+ +
@@ -212,30 +171,4 @@ {{ $companies->links() }}
- - @push('scripts') - - - - @endpush diff --git a/Modules/Company/Resources/views/partials/address-form.blade.php b/Modules/Company/Resources/views/partials/address-form.blade.php deleted file mode 100644 index 9f9cb438..00000000 --- a/Modules/Company/Resources/views/partials/address-form.blade.php +++ /dev/null @@ -1,47 +0,0 @@ -@php($address = $address ?? null) -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- -
diff --git a/Modules/Company/Resources/views/show.blade.php b/Modules/Company/Resources/views/show.blade.php deleted file mode 100644 index d489c447..00000000 --- a/Modules/Company/Resources/views/show.blade.php +++ /dev/null @@ -1,140 +0,0 @@ - -
- - -
-

Users

{{ $company->users_count }}

-

Divisions

{{ $company->divisions_count }}

-

Warehouses

{{ $company->warehouses_count }}

-

Licences

{{ $company->license_keys_count }}

-

Payments

{{ $company->payments_count }}

-
- -
-
-
-

Company profile

Core tenant identity and operational status.

- Edit company -
-
-
Billing email
{{ $company->billing_email ?? '—' }}
-
Status
{{ $company->is_system_company ? 'System' : ucfirst($company->status) }}
-
Timezone
{{ $company->timezone }}
-
Slug
{{ $company->slug }}
-
-
- -
-

Quick actions

-
- Back to companies - @unless ($company->is_system_company) -
@csrf @method('DELETE')
- @endunless -
-
-
- -
-

Company address

- @if ($company->address) -

{{ $company->address->formatted }}

- @else -

No company address has been recorded.

- @endif -
- @csrf @method('PUT') - @include('company::partials.address-form', ['address' => $company->address, 'defaultLabel' => 'Registered office']) -
-
- -
-
-

Create division

-
- @csrf - - - - - -
-
- -
-

Director coverage

-
- @csrf - - - - -
-
- @forelse($company->directorAssignments as $assignment) -
{{ $assignment->user?->name }} — {{ $assignment->coverage_type === 'company' ? 'Whole company' : $assignment->division?->name }}
@csrf @method('DELETE')
- @empty

No director coverage recorded.

@endforelse -
-
-
- - -
-

Warehouse division and address alignment

-

Warehouses remain tenant-scoped to the company. Assign a division only when that operating structure exists and record an optional warehouse operating address where needed.

-
- @forelse($company->warehouses as $warehouse) -
-
-
-

{{ $warehouse->name }}

-

{{ $warehouse->code }} · {{ $warehouse->division?->name ?? 'Company-level warehouse' }}

- @if ($warehouse->address) -

{{ $warehouse->address->formatted }}

- @else -

No warehouse address recorded.

- @endif -
-
- @csrf @method('PUT') - - -
-
- -
- Edit warehouse address -
- @csrf @method('PUT') - @include('company::partials.address-form', ['address' => $warehouse->address, 'defaultLabel' => 'Warehouse address']) -
-
-
- @empty -

No warehouses exist for this company yet.

- @endforelse -
-
- -
-

Divisions

- @forelse($company->divisions as $division) -
-
-

{{ $division->name }}

{{ $division->code ?? 'No code' }} · {{ ucfirst($division->status) }} · {{ $division->warehouses->count() }} warehouses

-
@csrf @method('DELETE')
-
-
@csrf @method('PUT')
-

Division address

@csrf @method('PUT')@include('company::partials.address-form', ['address' => $division->address, 'defaultLabel' => 'Operating address'])
-
- @empty -
No divisions exist. Warehouses can remain directly under the company until divisions are introduced.
- @endforelse -
-
-
diff --git a/Modules/Company/Routes/.gitkeep b/Modules/Company/Routes/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Routes/api.php b/Modules/Company/Routes/api.php deleted file mode 100644 index 4367b461..00000000 --- a/Modules/Company/Routes/api.php +++ /dev/null @@ -1,18 +0,0 @@ -get('/company', function (Request $request) { - return $request->user(); -}); \ No newline at end of file diff --git a/Modules/Company/Routes/web.php b/Modules/Company/Routes/web.php deleted file mode 100644 index ef9d49f6..00000000 --- a/Modules/Company/Routes/web.php +++ /dev/null @@ -1,27 +0,0 @@ -prefix('admin') - ->name('platform.') - ->group(function () { - Route::resource('companies', CompanyController::class); - - Route::put('companies/{company}/address', [CompanyAddressController::class, 'updateCompany'])->name('companies.address.update'); - Route::post('companies/{company}/divisions', [CompanyDivisionController::class, 'store'])->name('companies.divisions.store'); - Route::put('companies/{company}/divisions/{division}', [CompanyDivisionController::class, 'update'])->name('companies.divisions.update'); - Route::delete('companies/{company}/divisions/{division}', [CompanyDivisionController::class, 'destroy'])->name('companies.divisions.destroy'); - Route::put('companies/{company}/divisions/{division}/address', [CompanyAddressController::class, 'updateDivision'])->name('companies.divisions.address.update'); - - Route::post('companies/{company}/division-memberships', [CompanyMembershipController::class, 'assignDivision'])->name('companies.division-memberships.store'); - Route::post('companies/{company}/director-assignments', [CompanyMembershipController::class, 'assignDirector'])->name('companies.director-assignments.store'); - Route::delete('companies/{company}/director-assignments/{assignment}', [CompanyMembershipController::class, 'removeDirector'])->name('companies.director-assignments.destroy'); - - Route::put('companies/{company}/warehouses/{warehouse}/division', [CompanyWarehouseAssignmentController::class, 'update'])->name('companies.warehouses.division.update'); - Route::put('companies/{company}/warehouses/{warehouse}/address', [CompanyAddressController::class, 'updateWarehouse'])->name('companies.warehouses.address.update'); - }); diff --git a/Modules/Company/Support/.gitkeep b/Modules/Company/Support/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Support/ModuleInfo.php b/Modules/Company/Support/ModuleInfo.php deleted file mode 100644 index 97bc1744..00000000 --- a/Modules/Company/Support/ModuleInfo.php +++ /dev/null @@ -1,26 +0,0 @@ -firstOrCreate(['slug' => 'mpba-limited'], ['name' => 'MPBA Limited', 'is_system_company' => true, 'status' => 'active']); - $tenant = Company::query()->firstOrCreate(['slug' => 'sentinel'], ['name' => 'Sentinel', 'status' => 'active']); - Country::query()->firstOrCreate(['alpha2' => 'GB'], ['alpha3' => 'GBR', 'numeric' => '826', 'name' => 'United Kingdom', 'is_active' => true]); - $admin = User::query()->firstOrCreate(['email' => 'company-admin-browser@example.test'], [ - 'company_id' => $system->id, - 'name' => 'Company Browser Admin', - 'password' => bcrypt('password'), - 'is_platform_admin' => true, - 'status' => 'active', - ]); - - $this->browse(function (Browser $browser) use ($admin, $tenant): void { - $browser->loginAs($admin) - ->visitRoute('platform.companies.show', $tenant) - ->assertSee('Company address') - ->assertSee('Create division') - ->assertSee('Director coverage') - ->assertSee('Warehouse division and address alignment') - ->assertSee('Divisions'); - }); - } -} diff --git a/Modules/Company/Tests/Feature/.gitkeep b/Modules/Company/Tests/Feature/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Tests/Feature/CompanyAdministrationRoutesTest.php b/Modules/Company/Tests/Feature/CompanyAdministrationRoutesTest.php deleted file mode 100644 index e4666c5a..00000000 --- a/Modules/Company/Tests/Feature/CompanyAdministrationRoutesTest.php +++ /dev/null @@ -1,73 +0,0 @@ - 'MPBA Limited', 'is_system_company' => true]); - $tenant = Company::create(['name' => 'Sentinel', 'billing_email' => 'sentinel@example.test']); - $country = Country::create(['alpha2' => 'GB', 'alpha3' => 'GBR', 'numeric' => '826', 'name' => 'United Kingdom', 'is_active' => true]); - $admin = User::create(['company_id' => $system->id, 'name' => 'Platform Admin', 'email' => 'admin@example.test', 'password' => 'password', 'is_platform_admin' => true]); - $director = User::create(['company_id' => $tenant->id, 'name' => 'Division Director', 'email' => 'director@example.test', 'password' => 'password']); - - $this->actingAs($admin)->put(route('platform.companies.address.update', $tenant), [ - 'line_1' => '1 Example Street', - 'locality' => 'London', - 'postal_code' => 'SW1A 1AA', - 'country_id' => $country->id, - ])->assertRedirect(route('platform.companies.show', $tenant)); - - $this->actingAs($admin)->post(route('platform.companies.divisions.store', $tenant), [ - 'name' => 'Northern Operations', - 'code' => 'NORTH', - 'status' => 'active', - ])->assertRedirect(route('platform.companies.show', $tenant)); - - $division = $tenant->divisions()->where('code', 'NORTH')->firstOrFail(); - - $this->actingAs($admin)->post(route('platform.companies.director-assignments.store', $tenant), [ - 'user_id' => $director->id, - 'coverage_type' => 'division', - 'company_division_id' => $division->id, - ])->assertRedirect(route('platform.companies.show', $tenant)); - - $this->assertDatabaseHas('company_addresses', ['line_1' => '1 Example Street', 'country_id' => $country->id]); - $this->assertDatabaseHas('company_divisions', ['company_id' => $tenant->id, 'code' => 'NORTH']); - $this->assertDatabaseHas('company_director_assignments', ['company_id' => $tenant->id, 'company_division_id' => $division->id, 'user_id' => $director->id]); - } - public function test_platform_admin_can_update_warehouse_address(): void - { - $system = Company::create(['name' => 'MPBA Limited', 'is_system_company' => true]); - $tenant = Company::create(['name' => 'Sentinel', 'billing_email' => 'sentinel@example.test']); - $country = Country::create(['alpha2' => 'GB', 'alpha3' => 'GBR', 'numeric' => '826', 'name' => 'United Kingdom', 'is_active' => true]); - $warehouse = Warehouse::create(['company_id' => $tenant->id, 'code' => 'MAIN', 'name' => 'Main Warehouse']); - $admin = User::create(['company_id' => $system->id, 'name' => 'Platform Admin', 'email' => 'warehouse-admin@example.test', 'password' => 'password', 'is_platform_admin' => true]); - - $this->actingAs($admin)->put(route('platform.companies.warehouses.address.update', [$tenant, $warehouse]), [ - 'label' => 'Warehouse address', - 'line_1' => '100 Distribution Park', - 'locality' => 'Birmingham', - 'postal_code' => 'B1 1AA', - 'country_id' => $country->id, - ])->assertRedirect(route('platform.companies.show', $tenant)); - - $this->assertDatabaseHas('company_addresses', [ - 'addressable_type' => Warehouse::class, - 'addressable_id' => $warehouse->id, - 'line_1' => '100 Distribution Park', - 'country_id' => $country->id, - ]); - } - -} diff --git a/Modules/Company/Tests/Feature/CompanyStructureTest.php b/Modules/Company/Tests/Feature/CompanyStructureTest.php deleted file mode 100644 index c6e14706..00000000 --- a/Modules/Company/Tests/Feature/CompanyStructureTest.php +++ /dev/null @@ -1,70 +0,0 @@ - 'Abbott Logistics Limited', 'billing_email' => 'billing@abbott.example']); - $country = Country::create(['alpha2' => 'GB', 'alpha3' => 'GBR', 'numeric' => '826', 'name' => 'United Kingdom', 'is_active' => true]); - - $company->address()->create([ - 'line_1' => '1 Warehouse Way', - 'locality' => 'London', - 'postal_code' => 'SW1A 1AA', - 'country_id' => $country->id, - ]); - - $company->address()->update(['line_1' => '2 Warehouse Way']); - - $this->assertSame(1, CompanyAddress::count()); - $this->assertSame('2 Warehouse Way', $company->fresh()->address->line_1); - $this->assertSame('United Kingdom', $company->fresh()->address->country->name); - } - - public function test_company_division_has_address_users_directors_and_optional_warehouses(): void - { - $company = Company::create(['name' => 'Sentinel', 'billing_email' => 'sentinel@example.test']); - $country = Country::create(['alpha2' => 'GB', 'alpha3' => 'GBR', 'numeric' => '826', 'name' => 'United Kingdom', 'is_active' => true]); - $user = User::create(['company_id' => $company->id, 'name' => 'Jane Director', 'email' => 'jane@example.test', 'password' => 'password']); - $division = $company->divisions()->create(['name' => 'North Operations', 'code' => 'NORTH']); - $warehouse = Warehouse::create(['company_id' => $company->id, 'division_id' => $division->id, 'code' => 'NTH', 'name' => 'North DC']); - - $division->address()->create([ - 'label' => 'Operating address', - 'line_1' => 'North Road', - 'locality' => 'Manchester', - 'country_id' => $country->id, - ]); - $division->users()->attach($user->id, ['company_id' => $company->id, 'role' => 'director', 'is_director' => true]); - CompanyDirectorAssignment::create(['company_id' => $company->id, 'company_division_id' => $division->id, 'user_id' => $user->id, 'coverage_type' => 'division']); - - $this->assertTrue($company->fresh()->divisions()->exists()); - $this->assertSame('North Operations', $warehouse->fresh()->division->name); - $this->assertSame('Manchester', $division->fresh()->address->locality); - $this->assertTrue($user->fresh()->divisions()->whereKey($division->id)->exists()); - $this->assertTrue($user->fresh()->directorAssignments()->where('company_division_id', $division->id)->exists()); - } - - public function test_warehouse_can_remain_at_company_level_without_division(): void - { - $company = Company::create(['name' => 'Company Level Warehousing']); - $warehouse = Warehouse::create(['company_id' => $company->id, 'code' => 'MAIN', 'name' => 'Main Warehouse']); - - $this->assertNull($warehouse->division_id); - $this->assertSame('Company Level Warehousing', $warehouse->company->name); - } -} diff --git a/Modules/Company/Tests/Unit/.gitkeep b/Modules/Company/Tests/Unit/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/Traits/.gitkeep b/Modules/Company/Traits/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/View/.gitkeep b/Modules/Company/View/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/Modules/Company/changelog.yaml b/Modules/Company/changelog.yaml deleted file mode 100644 index 5f29b1fe..00000000 --- a/Modules/Company/changelog.yaml +++ /dev/null @@ -1,32 +0,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: - - Added one-address-per-owner company address records using an industry-standard address layout backed by the Countries module. - - Added company divisions with optional codes, status, soft deletes, one address per division, user memberships and director coverage. - - Added optional division assignment and proper address relationship support for warehouses. - - Added Company module migrations with foreign keys, unique constraints and performance indexes. - - Added feature and Dusk coverage for company addresses, divisions, director coverage and warehouse division behaviour. - - version: 1.1.0 - date: 2026-07-02 - changes: - - Added full company create, show, edit, update and delete workflow. - - Added company workbench show page with profile, KPI cards, recent users, warehouses and licences. - - Added guarded deletion so the system company cannot be deleted. - - Standardised Company module views on x-layouts::app and company::_form includes. - - version: 1.0.2 - date: 2026-07-02 - changes: - - Fixed company create/update validation when timezone is omitted from request payloads. - - version: 1.0.0 - date: 2026-07-02 - changes: - - Initial Company module skeleton. diff --git a/Modules/Company/composer.json b/Modules/Company/composer.json deleted file mode 100644 index a29a3300..00000000 --- a/Modules/Company/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "mpba/company", - "description": "", - "authors": [ - { - "name": "Mark Gregory", - "email": "mark.gregory@mpba.co.uk" - } - ], - "extra": { - "laravel": { - "providers": [], - "aliases": { - - } - } - }, - "autoload": { - "psr-4": { - "Modules\\Company\\": "" - }, - "autoload-dev": { - "psr-4": { - "Tests\\": "tests/", - "Modules\\Company\\Tests\\": "modules/Company/Tests/" - } - } - } -} diff --git a/Modules/Company/module.json b/Modules/Company/module.json deleted file mode 100644 index 6c43da16..00000000 --- a/Modules/Company/module.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "Company", - "alias": "company", - "version": "1.2.1", - "description": "", - "keywords": [], - "priority": 0, - "providers": [ - "Modules\\Company\\Providers\\CompanyServiceProvider" - ], - "aliases": {}, - "files": [], - "requires": [] -} diff --git a/Modules/Company/package.json b/Modules/Company/package.json deleted file mode 100644 index 4599509f..00000000 --- a/Modules/Company/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "private": true, - "scripts": { - "dev": "npm run development", - "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", - "watch-poll": "npm run watch -- --watch-poll", - "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", - "prod": "npm run production", - "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" - }, - "devDependencies": { - "cross-env": "^7.0", - "laravel-mix": "^5.0.1", - "laravel-mix-merge-manifest": "^0.1.2" - } -} diff --git a/Modules/Company/webpack.mix.js b/Modules/Company/webpack.mix.js deleted file mode 100644 index 04ac3e46..00000000 --- a/Modules/Company/webpack.mix.js +++ /dev/null @@ -1,11 +0,0 @@ -const dotenvExpand = require('dotenv-expand'); -dotenvExpand(require('dotenv').config({ path: '../../.env'/*, debug: true*/})); - -const mix = require('laravel-mix'); - -mix.js(__dirname + '/Resources/assets/js/app.js', 'js/company.js') - .sass( __dirname + '/Resources/assets/sass/app.scss', 'css/company.css'); - -if (mix.inProduction()) { - mix.version(); -} diff --git a/css/app.css b/css/app.css deleted file mode 100644 index 5a7ff4d3..00000000 --- a/css/app.css +++ /dev/null @@ -1,1531 +0,0 @@ - -@import 'tailwindcss'; -@import '../../vendor/livewire/flux/dist/flux.css'; -@import 'flatpickr/dist/flatpickr.css'; - -@source '../views'; -@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; -@source '../../vendor/livewire/flux-pro/stubs/**/*.blade.php'; -@source '../../vendor/livewire/flux/stubs/**/*.blade.php'; - -@custom-variant dark (&:where(.dark, .dark *)); - -@theme { - --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; - - --color-zinc-50: #fafafa; - --color-zinc-100: #f5f5f5; - --color-zinc-200: #e5e5e5; - --color-zinc-300: #d4d4d4; - --color-zinc-400: #a3a3a3; - --color-zinc-500: #737373; - --color-zinc-600: #525252; - --color-zinc-700: #404040; - --color-zinc-800: #262626; - --color-zinc-900: #171717; - --color-zinc-950: #0a0a0a; - - --color-accent: var(--color-neutral-800); - --color-accent-content: var(--color-neutral-800); - --color-accent-foreground: var(--color-white); -} - -@layer theme { - .dark { - --color-accent: var(--color-white); - --color-accent-content: var(--color-white); - --color-accent-foreground: var(--color-neutral-800); - } -} - -@layer base { - - *, - ::after, - ::before, - ::backdrop, - ::file-selector-button { - border-color: var(--color-gray-200, currentColor); - } -} - -[data-flux-field]:not(ui-radio, ui-checkbox) { - @apply grid gap-2; -} - -[data-flux-label] { - @apply !mb-0 !leading-tight; -} - -input:focus[data-flux-control], -textarea:focus[data-flux-control], -select:focus[data-flux-control] { - @apply outline-hidden ring-2 ring-accent ring-offset-2 ring-offset-accent-foreground; -} - -/* \[:where(&)\]:size-4 { - @apply size-4; -} */ - -@layer components { - .wms-app-shell { - min-height: 100vh; - display: flex; - background: rgb(250 250 250); - } - - .dark .wms-app-shell { - background: rgb(9 9 11); - } - - .wms-sidebar { - position: sticky; - top: 0; - z-index: 40; - display: flex; - width: 23.5rem; - min-width: 23.5rem; - height: 100vh; - flex-direction: column; - border-right: 1px solid rgb(228 228 231); - background: rgb(250 250 250); - transition: width 180ms ease, min-width 180ms ease, transform 180ms ease; - } - - .dark .wms-sidebar { - border-right-color: rgb(63 63 70); - background: rgb(24 24 27); - } - - .wms-sidebar-header { - display: flex; - align-items: center; - gap: 0.75rem; - min-height: 4.75rem; - padding: 0.95rem; - border-bottom: 1px solid rgb(228 228 231); - } - - .dark .wms-sidebar-header { - border-bottom-color: rgb(63 63 70); - } - - .wms-brand { - display: flex; - align-items: center; - gap: 0.75rem; - min-width: 0; - flex: 1; - color: inherit; - text-decoration: none; - } - - .wms-brand-mark { - display: grid; - width: 2.5rem; - height: 2.5rem; - flex: 0 0 auto; - place-items: center; - border-radius: 0.85rem; - background: linear-gradient(135deg, rgb(15 23 42), rgb(14 116 144)); - color: white; - box-shadow: 0 12px 24px rgb(15 23 42 / 0.22); - } - - .wms-brand-mark-inner { - font-size: 1rem; - font-weight: 800; - letter-spacing: -0.04em; - } - - .wms-brand-copy { - min-width: 0; - display: grid; - line-height: 1.1; - } - - .wms-brand-title { - max-width: 18rem; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.98rem; - font-weight: 800; - color: rgb(24 24 27); - } - - .dark .wms-brand-title { - color: rgb(244 244 245); - } - - .wms-brand-subtitle { - margin-top: 0.18rem; - font-size: 0.72rem; - font-weight: 800; - letter-spacing: 0.14em; - color: rgb(8 145 178); - } - - .wms-sidebar-toggle, - .wms-mobile-close, - .wms-mobile-menu { - display: inline-grid; - place-items: center; - border-radius: 0.65rem; - border: 1px solid rgb(228 228 231); - background: white; - color: rgb(63 63 70); - box-shadow: 0 10px 24px rgb(0 0 0 / 0.08); - transition: background 120ms ease, color 120ms ease, border-color 120ms ease, transform 120ms ease; - } - - .wms-mobile-menu { - border-color: rgb(14 116 144 / 0.22); - background: linear-gradient(135deg, rgb(255 255 255), rgb(236 254 255)); - color: rgb(14 116 144); - } - - .dark .wms-mobile-menu { - border-color: rgb(103 232 249 / 0.22); - background: linear-gradient(135deg, rgb(24 24 27), rgb(8 47 73)); - color: rgb(165 243 252); - } - - .wms-sidebar-toggle { - width: 2.15rem; - height: 2.15rem; - flex: 0 0 auto; - font-size: 1.35rem; - font-weight: 800; - line-height: 1; - } - - .wms-sidebar-toggle:hover, - .wms-mobile-close:hover, - .wms-mobile-menu:hover { - border-color: rgb(14 116 144); - color: rgb(14 116 144); - } - - .dark .wms-sidebar-toggle, - .dark .wms-mobile-close, - .dark .wms-mobile-menu { - border-color: rgb(63 63 70); - background: rgb(39 39 42); - color: rgb(228 228 231); - } - - .wms-toggle-collapsed { - display: none; - } - - .wms-mobile-close { - display: none; - width: 2.15rem; - height: 2.15rem; - font-size: 1.4rem; - line-height: 1; - } - - .wms-sidebar-nav { - flex: 1; - overflow-y: auto; - padding: 0.9rem; - } - - .wms-sidebar-group + .wms-sidebar-group { - margin-top: 1.1rem; - } - - .wms-sidebar-heading { - padding: 0 0.625rem 0.45rem; - font-size: 0.69rem; - font-weight: 800; - letter-spacing: 0.11em; - text-transform: uppercase; - color: rgb(113 113 122); - } - - .wms-sidebar-link { - position: relative; - display: flex; - min-height: 2.65rem; - align-items: center; - gap: 0.78rem; - border-radius: 0.8rem; - padding: 0.65rem 0.75rem; - color: rgb(63 63 70); - font-size: 0.92rem; - font-weight: 650; - text-decoration: none; - transition: background 120ms ease, color 120ms ease; - } - - .wms-sidebar-link:hover { - background: rgb(244 244 245); - color: rgb(14 116 144); - } - - .wms-sidebar-link.is-active { - background: rgb(236 254 255); - color: rgb(14 116 144); - } - - .dark .wms-sidebar-link { - color: rgb(212 212 216); - } - - .dark .wms-sidebar-link:hover { - background: rgb(39 39 42); - color: rgb(103 232 249); - } - - .dark .wms-sidebar-link.is-active { - background: rgb(8 47 73 / 0.55); - color: rgb(103 232 249); - } - - .wms-sidebar-icon { - width: 1.25rem; - min-width: 1.25rem; - text-align: center; - font-size: 1rem; - } - - .wms-sidebar-footer { - display: grid; - gap: 0.25rem; - border-top: 1px solid rgb(228 228 231); - padding: 0.9rem; - } - - .dark .wms-sidebar-footer { - border-top-color: rgb(63 63 70); - } - - .wms-user-card { - margin-top: 0.5rem; - display: flex; - min-width: 0; - align-items: center; - gap: 0.65rem; - border-radius: 0.85rem; - border: 1px solid rgb(228 228 231); - background: white; - padding: 0.65rem; - } - - .wms-user-card-button { - width: 100%; - cursor: pointer; - text-align: left; - } - - .wms-user-card-button:hover { - border-color: rgb(14 116 144); - background: rgb(244 244 245); - } - - .dark .wms-user-card-button:hover { - border-color: rgb(14 116 144); - background: rgb(48 48 52); - } - - .wms-user-chevron { - margin-left: auto; - color: rgb(113 113 122); - font-size: 0.75rem; - } - - .wms-user-rail-button { - display: none; - width: 2.5rem; - height: 2.5rem; - place-items: center; - border-radius: 999px; - background: rgb(15 23 42); - color: white; - font-size: 0.8rem; - font-weight: 800; - } - - .dark .wms-user-card { - border-color: rgb(63 63 70); - background: rgb(39 39 42); - } - - .wms-user-avatar { - display: grid; - width: 2rem; - height: 2rem; - flex: 0 0 auto; - place-items: center; - border-radius: 999px; - background: rgb(15 23 42); - color: white; - font-size: 0.75rem; - font-weight: 800; - } - - .wms-user-copy { - min-width: 0; - } - - .wms-user-name, - .wms-user-email { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .wms-user-name { - font-size: 0.82rem; - font-weight: 750; - } - - .wms-user-email { - font-size: 0.72rem; - color: rgb(113 113 122); - } - - .wms-main-column { - min-width: 0; - flex: 1; - display: flex; - min-height: 100vh; - flex-direction: column; - } - - .wms-content { - display: block; - width: 100%; - min-width: 0; - flex: 1; - padding: 1.5rem; - } - - .wms-main-panel { - min-width: 0; - flex: 1; - display: flex; - min-height: 100vh; - flex-direction: column; - } - - .wms-page-hero { - overflow: hidden; - margin-bottom: 1.5rem; - border-radius: 1.5rem; - border: 1px solid rgb(228 228 231); - background: rgb(255 255 255); - box-shadow: 0 1px 2px rgb(0 0 0 / 0.06), 0 12px 28px rgb(0 0 0 / 0.08); - } - - .dark .wms-page-hero { - border-color: rgb(63 63 70); - background: rgb(24 24 27); - } - - .wms-page-hero-inner { - padding: 1.75rem 1.5rem; - background: linear-gradient(135deg, rgb(2 6 23), rgb(15 23 42) 52%, rgb(23 37 84)); - color: rgb(255 255 255); - } - - .wms-page-hero-row { - display: flex; - flex-direction: column; - gap: 1rem; - } - - .wms-page-hero-copy { - min-width: 0; - max-width: 48rem; - } - - .wms-page-hero-kicker { - margin: 0; - color: rgb(191 219 254); - font-size: 0.75rem; - font-weight: 650; - letter-spacing: 0.28em; - line-height: 1rem; - text-transform: uppercase; - } - - .wms-page-hero-title { - margin: 0.5rem 0 0; - color: rgb(255 255 255); - font-size: 1.875rem; - font-weight: 650; - line-height: 2.25rem; - letter-spacing: -0.025em; - } - - .wms-page-hero-description { - max-width: 48rem; - margin: 0.5rem 0 0; - color: rgb(219 234 254 / 0.90); - font-size: 0.875rem; - line-height: 1.35rem; - } - - .wms-page-hero-actions { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0.5rem; - } - - .wms-page-hero-stat { - display: grid; - min-width: 8.5rem; - gap: 0.25rem; - border-radius: 0.85rem; - border: 1px solid rgb(255 255 255 / 0.18); - background: rgb(255 255 255 / 0.10); - padding: 0.7rem 0.85rem; - color: rgb(219 234 254 / 0.90); - box-shadow: 0 10px 24px rgb(0 0 0 / 0.18); - } - - .wms-page-hero-stat span { - font-size: 0.68rem; - font-weight: 800; - letter-spacing: 0.14em; - text-transform: uppercase; - } - - .wms-page-hero-stat strong { - display: flex; - align-items: center; - gap: 0.55rem; - color: white; - font-size: 1.25rem; - font-weight: 800; - line-height: 1; - } - - .wms-page-hero-stat strong i { - color: rgb(134 239 172); - } - - .wms-page-hero-icon-card { - min-width: 3.25rem; - width: 3.25rem; - height: 3.25rem; - place-items: center; - padding: 0; - text-align: center; - } - - .wms-page-hero-icon-card strong { - justify-content: center; - font-size: 1.25rem; - } - - @media (min-width: 1024px) { - .wms-page-hero-row { - flex-direction: row; - align-items: center; - justify-content: space-between; - } - - .wms-page-hero-actions { - justify-content: flex-end; - } - } - - .wms-hero-mobile-menu { - display: none; - width: 2.5rem; - height: 2.5rem; - place-items: center; - align-self: flex-start; - border-radius: 0.85rem; - background: rgb(255 255 255 / 0.12); - color: white; - } - - - .wms-desktop-topbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - min-height: 4.25rem; - border-bottom: 1px solid rgb(228 228 231); - background: rgb(250 250 250 / 0.94); - padding: 0.75rem 1.5rem; - backdrop-filter: blur(14px); - } - - .dark .wms-desktop-topbar { - border-bottom-color: rgb(63 63 70); - background: rgb(15 15 19 / 0.94); - } - - .wms-topbar-brand { - display: inline-flex; - min-width: 0; - align-items: center; - gap: 0.75rem; - color: inherit; - text-decoration: none; - } - - .wms-topbar-user-button { - display: inline-flex; - align-items: center; - gap: 0.7rem; - border-radius: 999px; - border: 1px solid transparent; - padding: 0.35rem 0.45rem 0.35rem 0.85rem; - text-align: right; - transition: background 120ms ease, border-color 120ms ease; - } - - .wms-topbar-user-button:hover { - border-color: rgb(228 228 231); - background: rgb(244 244 245); - } - - .dark .wms-topbar-user-button:hover { - border-color: rgb(63 63 70); - background: rgb(39 39 42); - } - - .wms-topbar-user-copy { - display: grid; - min-width: 0; - } - - .wms-mobile-header { - display: none; - } - - .wms-mobile-overlay { - display: none; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar { - width: 5.5rem; - min-width: 5.5rem; - } - - html[data-wms-sidebar='collapsed'] [data-wms-sidebar-label] { - display: none !important; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar-header, - html[data-wms-sidebar='collapsed'] .wms-sidebar-footer, - html[data-wms-sidebar='collapsed'] .wms-sidebar-nav { - align-items: center; - padding-left: 0.7rem; - padding-right: 0.7rem; - } - - html[data-wms-sidebar='collapsed'] .wms-brand { - flex: 0 0 auto; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar-header { - justify-content: center; - flex-wrap: wrap; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar-link { - justify-content: center; - padding-left: 0.7rem; - padding-right: 0.7rem; - } - - - html[data-wms-sidebar='collapsed'] .wms-user-rail-button { - display: grid; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar-footer { - justify-items: center; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar-toggle { - display: none; - } - - html[data-wms-sidebar='collapsed'] .wms-brand-mark { - cursor: pointer; - } - - @media (max-width: 1023px) { - .wms-desktop-topbar { - display: none; - } - - .wms-app-shell { - display: block; - } - - .wms-sidebar { - position: fixed; - inset: 0 auto 0 0; - width: min(23.5rem, calc(100vw - 2.5rem)); - min-width: min(23.5rem, calc(100vw - 2.5rem)); - transform: translateX(-105%); - box-shadow: 22px 0 60px rgb(0 0 0 / 0.24); - } - - html[data-wms-mobile-sidebar='open'] .wms-sidebar { - transform: translateX(0); - } - - html[data-wms-mobile-sidebar='open'] .wms-mobile-overlay { - position: fixed; - inset: 0; - z-index: 35; - display: block; - background: rgb(0 0 0 / 0.48); - backdrop-filter: blur(2px); - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar { - width: min(23.5rem, calc(100vw - 2.5rem)); - min-width: min(23.5rem, calc(100vw - 2.5rem)); - } - - html[data-wms-sidebar='collapsed'] [data-wms-sidebar-label] { - display: initial !important; - } - - html[data-wms-sidebar='collapsed'] .wms-brand-copy, - html[data-wms-sidebar='collapsed'] .wms-sidebar-heading, - html[data-wms-sidebar='collapsed'] .wms-sidebar-link span { - display: block !important; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar-link { - justify-content: flex-start; - } - - .wms-sidebar-toggle { - display: none; - } - - .wms-mobile-close { - display: inline-grid; - } - - .wms-mobile-header { - position: sticky; - top: 0; - z-index: 30; - display: flex; - min-height: 4rem; - align-items: center; - gap: 0.75rem; - border-bottom: 1px solid rgb(228 228 231); - background: rgb(255 255 255 / 0.86); - padding: 0.75rem 1rem; - backdrop-filter: blur(14px); - } - - .dark .wms-mobile-header { - border-bottom-color: rgb(63 63 70); - background: rgb(24 24 27 / 0.86); - } - - .wms-mobile-menu { - width: 2.35rem; - height: 2.35rem; - flex: 0 0 auto; - } - - .wms-mobile-title { - min-width: 0; - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: inherit; - font-size: 0.96rem; - font-weight: 800; - text-decoration: none; - } - - .wms-mobile-badge { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 3.1rem; - height: 2rem; - border-radius: 999px; - border: 1px solid rgb(14 116 144 / 0.24); - background: linear-gradient(135deg, rgb(14 116 144 / 0.14), rgb(37 99 235 / 0.12)); - color: rgb(14 116 144); - font-size: 0.72rem; - font-weight: 900; - letter-spacing: 0.12em; - } - - .dark .wms-mobile-badge { - border-color: rgb(103 232 249 / 0.26); - background: linear-gradient(135deg, rgb(8 145 178 / 0.26), rgb(37 99 235 / 0.20)); - color: rgb(165 243 252); - } - - .wms-page-hero { - max-width: 100%; - margin-bottom: 1rem; - border-radius: 1rem; - } - - .wms-page-hero-inner { - padding: 1.25rem; - } - - .wms-page-hero-actions { - width: 100%; - flex-wrap: wrap; - } - - .wms-page-hero-stat { - min-width: min(10rem, 100%); - flex: 1 1 10rem; - } - - .wms-hero-mobile-menu { - display: inline-grid; - } - - .wms-content { - padding: 1rem; - } - } -} - - -@layer components { - body.wms-mobile-nav-open { - overflow: hidden; - } - - @media (max-width: 1023px) { - .wms-app-shell { - display: block !important; - min-height: 100vh; - } - - .wms-main-panel { - min-height: 100vh; - } - - .wms-mobile-header { - position: sticky !important; - top: 0; - z-index: 50; - display: flex !important; - min-height: 4.25rem; - align-items: center; - gap: 0.8rem; - border-bottom: 1px solid rgb(228 228 231); - background: rgb(255 255 255 / 0.94); - padding: 0.75rem 1rem; - box-shadow: 0 14px 34px rgb(15 23 42 / 0.08); - backdrop-filter: blur(18px); - } - - .dark .wms-mobile-header { - border-bottom-color: rgb(63 63 70); - background: rgb(24 24 27 / 0.94); - box-shadow: 0 14px 34px rgb(0 0 0 / 0.28); - } - - .wms-mobile-menu { - display: inline-grid !important; - width: 2.65rem; - height: 2.65rem; - flex: 0 0 auto; - place-items: center; - border-radius: 0.9rem; - border: 1px solid rgb(14 116 144 / 0.28); - background: linear-gradient(135deg, rgb(255 255 255), rgb(236 254 255)); - color: rgb(14 116 144); - font-size: 1.05rem; - box-shadow: 0 12px 28px rgb(14 116 144 / 0.16); - } - - .dark .wms-mobile-menu { - border-color: rgb(103 232 249 / 0.30); - background: linear-gradient(135deg, rgb(24 24 27), rgb(8 47 73)); - color: rgb(165 243 252); - } - - .wms-mobile-title { - min-width: 0; - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: rgb(24 24 27); - font-size: 0.98rem; - font-weight: 850; - letter-spacing: -0.02em; - text-decoration: none; - } - - .dark .wms-mobile-title { - color: rgb(244 244 245); - } - - .wms-mobile-badge { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 3.15rem; - height: 2rem; - border-radius: 999px; - border: 1px solid rgb(14 116 144 / 0.24); - background: linear-gradient(135deg, rgb(14 116 144 / 0.14), rgb(37 99 235 / 0.12)); - color: rgb(14 116 144); - font-size: 0.72rem; - font-weight: 900; - letter-spacing: 0.12em; - } - - .dark .wms-mobile-badge { - border-color: rgb(103 232 249 / 0.26); - background: linear-gradient(135deg, rgb(8 145 178 / 0.26), rgb(37 99 235 / 0.20)); - color: rgb(165 243 252); - } - - .wms-sidebar { - position: fixed !important; - inset: 0 auto 0 0; - z-index: 70; - width: min(23.5rem, calc(100vw - 2.25rem)) !important; - min-width: min(23.5rem, calc(100vw - 2.25rem)) !important; - height: 100dvh; - transform: translateX(-110%); - box-shadow: 28px 0 70px rgb(0 0 0 / 0.30); - } - - html[data-wms-mobile-sidebar='open'] .wms-sidebar { - transform: translateX(0) !important; - } - - .wms-mobile-overlay { - position: fixed; - inset: 0; - z-index: 60; - display: none; - width: 100%; - height: 100%; - border: 0; - background: transparent; - padding: 0; - } - - html[data-wms-mobile-sidebar='open'] .wms-mobile-overlay { - display: block !important; - background: rgb(0 0 0 / 0.52); - backdrop-filter: blur(3px); - } - - .wms-mobile-close { - display: inline-grid !important; - } - - .wms-sidebar-toggle { - display: none !important; - } - - html[data-wms-sidebar='collapsed'] [data-wms-sidebar-label], - html[data-wms-sidebar='collapsed'] .wms-brand-copy, - html[data-wms-sidebar='collapsed'] .wms-sidebar-heading, - html[data-wms-sidebar='collapsed'] .wms-sidebar-link span { - display: block !important; - } - - html[data-wms-sidebar='collapsed'] .wms-sidebar-link { - justify-content: flex-start !important; - } - - .wms-content { - padding-top: 1rem; - } - } - - @media (min-width: 1024px) { - .wms-mobile-header, - .wms-mobile-overlay, - .wms-mobile-close { - display: none !important; - } - } -} - -@layer components { - .wms-content, - .wms-main-panel, - .wms-app-shell { - min-width: 0; - } - - .wms-content canvas { - max-width: 100%; - } - - @media (max-width: 1023px) { - .wms-content { - width: 100%; - max-width: 100vw; - overflow-x: hidden; - padding-inline: 0.875rem; - } - - .wms-page-hero { - max-width: 100%; - } - - .wms-sidebar-nav { - -webkit-overflow-scrolling: touch; - } - } -} - -:root { - --wms-sidebar-width: 288px; - --wms-sidebar-min-width: 224px; - --wms-sidebar-max-width: 448px; - --wms-sidebar-collapsed-width: 76px; - - --wms-active-parent-blue: #4164dc; - --wms-active-child-gold: #e7b72f; - --wms-active-child-gold-dark: #e7b72f; -} - -.wms-app-shell { - display: grid; - grid-template-columns: var(--wms-sidebar-width) minmax(0, 1fr); - min-height: 100vh; - transition-property: none; -} - -html[data-wms-sidebar="expanded"] .wms-app-shell { - grid-template-columns: var(--wms-sidebar-width) minmax(0, 1fr); -} - -html[data-wms-sidebar="collapsed"] .wms-app-shell { - grid-template-columns: var(--wms-sidebar-collapsed-width) minmax(0, 1fr); -} - -.wms-sidebar { - position: relative; - width: var(--wms-sidebar-width); - min-width: var(--wms-sidebar-width); - max-width: var(--wms-sidebar-width); - transition-property: none; -} - -.wms-main-panel { - transition-property: none; -} - -html[data-wms-sidebar="collapsed"] .wms-sidebar { - width: var(--wms-sidebar-collapsed-width); - min-width: var(--wms-sidebar-collapsed-width); - max-width: var(--wms-sidebar-collapsed-width); -} - -.wms-sidebar-resizer { - position: absolute; - top: 0; - right: -4px; - z-index: 30; - width: 8px; - height: 100%; - cursor: col-resize; - border: 0; - background: transparent; -} - -.wms-sidebar-resizer::after { - position: absolute; - top: 0; - right: 3px; - width: 1px; - height: 100%; - content: ""; - background: rgba(148, 163, 184, 0.22); - transition: background 160ms ease; -} - -.wms-sidebar-resizer:hover::after, -body.wms-sidebar-is-resizing .wms-sidebar-resizer::after { - background: rgba(59, 130, 246, 0.7); -} - -body.wms-sidebar-is-resizing { - cursor: col-resize; - user-select: none; -} - -html[data-wms-sidebar="collapsed"] .wms-sidebar-resizer { - display: none; -} - -.wms-sidebar-nav { - overflow-x: hidden; -} - -.wms-sidebar-heading-button { - display: flex; - width: 100%; - align-items: center; - justify-content: flex-start; - gap: 0.75rem; - border: 0; - background: transparent; - cursor: pointer; - text-align: left; -} - -.wms-sidebar-heading-content { - display: inline-flex; - align-items: center; - gap: 0.7rem; - min-width: 0; -} - -.wms-sidebar-heading-content > span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.wms-sidebar-heading-chevron { - display: none; -} - -.wms-sidebar-group-items { - display: none; -} - -.wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-group-items { - display: block; -} - -.wms-sidebar-group[data-wms-selected="true"] > .wms-sidebar-heading-button { - position: relative; - width: calc(100% - 1.25rem); - min-height: 2.9rem; - margin: 0.25rem 0.65rem 0.35rem; - padding: 0.6rem 0.85rem; - border-radius: 1.35rem; - background: linear-gradient(135deg, #4667df 0%, #3f5fd2 100%); - color: #ffffff; - font-weight: 700; - letter-spacing: 0.035em; - box-shadow: 0 8px 18px rgba(65, 100, 220, 0.22); -} - -.wms-sidebar-group[data-wms-selected="true"] > .wms-sidebar-heading-button .wms-sidebar-icon { - color: #ffffff; -} - - -.wms-sidebar-primary-link { - margin-bottom: 0.35rem; - min-height: 3.25rem; - font-size: 0.8rem; - font-weight: 800; - letter-spacing: 0.16em; - text-transform: uppercase; -} - -.wms-sidebar-primary-link span { - letter-spacing: 0.16em; -} -.wms-sidebar-link.is-active { - position: relative; - isolation: isolate; - width: calc(100% - 2.05rem); - min-height: 2.85rem; - margin: 0.2rem 1.25rem 0.25rem 0.8rem; - padding: 0.6rem 0.95rem; - border-radius: 1.65rem 0.75rem 0.75rem 1.65rem; - background: var(--wms-active-child-gold); - color: #ffffff; - font-weight: 700; - box-shadow: 0 8px 18px rgba(231, 183, 47, 0.24); -} - -.wms-sidebar-link.is-active::after { - position: absolute; - top: 0; - right: -1.05rem; - z-index: -1; - width: 1.75rem; - height: 100%; - content: ""; - background: var(--wms-active-child-gold); - clip-path: polygon(0 0, 64% 0, 100% 50%, 64% 100%, 0 100%); - border-radius: 0 0.9rem 0.9rem 0; -} - -.wms-sidebar-link.is-active .wms-sidebar-icon, -.wms-sidebar-link.is-active i, -.wms-sidebar-link.is-active span { - position: relative; - z-index: 1; - color: #ffffff; -} - -html[data-wms-sidebar="collapsed"] .wms-sidebar-heading-chevron, -html[data-wms-sidebar="collapsed"] .wms-sidebar-group-items { - display: none; -} - -html[data-wms-sidebar="collapsed"] .wms-sidebar-heading-button { - pointer-events: none; -} - -html[data-wms-sidebar="collapsed"] .wms-sidebar-group[data-wms-selected="true"] > .wms-sidebar-heading-button, -html[data-wms-sidebar="collapsed"] .wms-sidebar-link.is-active { - width: 2.75rem; - min-height: 2.75rem; - margin: 0.25rem auto; - justify-content: center; - border-radius: 9999px; - padding: 0.65rem; -} - -html[data-wms-sidebar="collapsed"] .wms-sidebar-link.is-active::after { - display: none; -} - -@media (max-width: 1023px) { - .wms-app-shell { - display: block; - } - - .wms-sidebar, - html[data-wms-sidebar="collapsed"] .wms-sidebar { - width: min(88vw, 22rem); - min-width: min(88vw, 22rem); - max-width: min(88vw, 22rem); - } - - .wms-sidebar-resizer { - display: none; - } - - .wms-sidebar-group[data-wms-selected="true"] > .wms-sidebar-heading-button { - width: calc(100% - 1.25rem); - min-height: 2.85rem; - margin: 0.25rem 0.65rem 0.35rem; - padding: 0.6rem 0.85rem; - } - - .wms-sidebar-link.is-active { - width: calc(100% - 2.05rem); - min-height: 2.8rem; - margin: 0.2rem 1.25rem 0.25rem 0.8rem; - padding: 0.6rem 0.95rem; - } - - html[data-wms-sidebar="collapsed"] .wms-sidebar-group-items { - display: none; - } - - html[data-wms-sidebar="collapsed"] .wms-sidebar-group[data-wms-menu-open="true"] > .wms-sidebar-group-items { - display: block; - } - - html[data-wms-sidebar="collapsed"] .wms-sidebar-heading-button { - pointer-events: auto; - } -} - -/* -|-------------------------------------------------------------------------- -| WMS fixed sidebar / scrollable content correction -|-------------------------------------------------------------------------- -| -| The application shell must own the viewport. The browser document must not -| scroll; only the right-hand WMS content pane should scroll. Keep this block -| at the end of the stylesheet so it wins over earlier shell declarations. -| -*/ - -html, -body { - width: 100%; - height: 100%; - min-height: 100%; - overflow: hidden !important; - overscroll-behavior: none; -} - -body { - position: fixed; - inset: 0; -} - -.wms-app-shell { - position: fixed !important; - inset: 0 !important; - display: grid !important; - grid-template-columns: var(--wms-sidebar-width) minmax(0, 1fr) !important; - width: 100vw !important; - height: 100vh !important; - min-width: 0 !important; - min-height: 0 !important; - max-height: 100vh !important; - overflow: hidden !important; -} - -@supports (height: 100dvh) { - .wms-app-shell { - height: 100dvh !important; - max-height: 100dvh !important; - } -} - -html[data-wms-sidebar="expanded"] .wms-app-shell { - grid-template-columns: var(--wms-sidebar-width) minmax(0, 1fr) !important; -} - -html[data-wms-sidebar="collapsed"] .wms-app-shell { - grid-template-columns: var(--wms-sidebar-collapsed-width) minmax(0, 1fr) !important; -} - -.wms-sidebar { - position: relative !important; - display: flex !important; - flex-direction: column !important; - width: var(--wms-sidebar-width) !important; - min-width: var(--wms-sidebar-width) !important; - max-width: var(--wms-sidebar-width) !important; - height: 100% !important; - min-height: 0 !important; - max-height: 100% !important; - overflow: hidden !important; -} - -html[data-wms-sidebar="collapsed"] .wms-sidebar { - width: var(--wms-sidebar-collapsed-width) !important; - min-width: var(--wms-sidebar-collapsed-width) !important; - max-width: var(--wms-sidebar-collapsed-width) !important; -} - -.wms-sidebar-header { - flex: 0 0 auto !important; -} - -.wms-sidebar-nav { - flex: 1 1 auto !important; - min-height: 0 !important; - overflow-y: auto !important; - overflow-x: hidden !important; - overscroll-behavior: contain; - -webkit-overflow-scrolling: touch; -} - -.wms-main-panel { - display: flex !important; - flex-direction: column !important; - min-width: 0 !important; - min-height: 0 !important; - width: 100% !important; - height: 100% !important; - max-height: 100% !important; - overflow: hidden !important; -} - -.wms-main-panel > header, -.wms-main-panel > .wms-topbar, -.wms-main-panel > [data-wms-topbar] { - position: relative !important; - top: auto !important; - flex: 0 0 auto !important; - min-height: 0 !important; -} - -.wms-content { - display: block !important; - flex: 1 1 auto !important; - min-width: 0 !important; - min-height: 0 !important; - width: 100% !important; - height: auto !important; - max-height: none !important; - overflow-y: auto !important; - overflow-x: hidden !important; - overscroll-behavior: contain; - -webkit-overflow-scrolling: touch; -} - -@media (max-width: 1023px) { - .wms-app-shell { - display: block !important; - height: 100vh !important; - max-height: 100vh !important; - overflow: hidden !important; - } - - @supports (height: 100dvh) { - .wms-app-shell { - height: 100dvh !important; - max-height: 100dvh !important; - } - } - - .wms-main-panel { - height: 100% !important; - max-height: 100% !important; - overflow: hidden !important; - } - - .wms-sidebar, - html[data-wms-sidebar="collapsed"] .wms-sidebar { - position: fixed !important; - inset: 0 auto 0 0 !important; - z-index: 70 !important; - width: min(88vw, 22rem) !important; - min-width: min(88vw, 22rem) !important; - max-width: min(88vw, 22rem) !important; - height: 100vh !important; - max-height: 100vh !important; - overflow: hidden !important; - } - - @supports (height: 100dvh) { - .wms-sidebar, - html[data-wms-sidebar="collapsed"] .wms-sidebar { - height: 100dvh !important; - max-height: 100dvh !important; - } - } -} - - - -@layer components { - .wms-select2-field .select2-container { - width: 100% !important; - } - - .wms-select2-field .select2-container--default .select2-selection--single { - min-height: 2.875rem; - border-radius: 0.75rem; - border-color: rgb(212 212 216); - background: white; - box-shadow: 0 1px 2px rgb(15 23 42 / 0.04); - transition: border-color 140ms ease, box-shadow 140ms ease, background 140ms ease; - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__rendered { - padding-left: 0.95rem; - padding-right: 2.75rem; - color: rgb(24 24 27); - font-size: 0.875rem; - font-weight: 600; - line-height: 2.875rem; - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__placeholder { - color: rgb(113 113 122); - font-weight: 500; - } - - .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__arrow { - top: 0.45rem; - right: 0.65rem; - width: 1.75rem; - height: 1.75rem; - border-radius: 0.55rem; - background: rgb(244 244 245); - } - - .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(8 145 178); - box-shadow: 0 0 0 3px rgb(103 232 249 / 0.28); - } - - .select2-container--default .select2-dropdown { - overflow: hidden; - border-radius: 1rem; - border-color: rgb(212 212 216); - background: white; - box-shadow: 0 24px 60px rgb(15 23 42 / 0.18); - } - - .select2-container--default .select2-search--dropdown { - padding: 0.75rem; - } - - .select2-container--default .select2-search--dropdown .select2-search__field { - border-radius: 0.75rem; - border-color: rgb(212 212 216); - padding: 0.55rem 0.75rem; - font-size: 0.875rem; - outline: none; - } - - .select2-container--default .select2-search--dropdown .select2-search__field:focus { - border-color: rgb(8 145 178); - box-shadow: 0 0 0 3px rgb(103 232 249 / 0.28); - } - - .select2-container--default .select2-results__option { - padding: 0.65rem 0.85rem; - font-size: 0.875rem; - font-weight: 600; - } - - .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable { - background: rgb(8 145 178); - color: white; - } - - .select2-container--default .select2-results__option--selected { - background: rgb(236 254 255); - color: rgb(14 116 144); - } - - .dark .wms-select2-field .select2-container--default .select2-selection--single { - border-color: rgb(63 63 70); - background: 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 { - background: rgb(39 39 42); - } - - .dark .select2-container--default .select2-dropdown { - border-color: rgb(63 63 70); - background: rgb(24 24 27); - color: rgb(244 244 245); - } - - .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--selected { - background: rgb(8 47 73); - color: rgb(165 243 252); - } -} diff --git a/js/app.js b/js/app.js deleted file mode 100644 index ee6d0713..00000000 --- a/js/app.js +++ /dev/null @@ -1,222 +0,0 @@ -require('flatpickr'); - -import '@fortawesome/fontawesome-free/css/all.min.css'; -import Chart from 'chart.js/auto'; -import $ from 'jquery'; - -window.$ = $; -window.jQuery = $; - -import 'select2'; -import 'select2/dist/css/select2.min.css'; - -const chartColours = [ - 'rgb(8, 145, 178)', - 'rgb(37, 99, 235)', - 'rgb(79, 70, 229)', - 'rgb(14, 116, 144)', -]; - -/** - * Parse JSON attribute safely. - * - * @param {HTMLElement} element - * @param {string} attribute - * @param {Array} fallback - * @returns {*} - */ -function parseJsonAttribute(element, attribute, fallback = []) { - try { - return JSON.parse( - element.getAttribute(attribute) || JSON.stringify(fallback) - ); - } catch (error) { - return fallback; - } -} - -/** - * Render dashboard charts. - * - * @returns {void} - */ -function renderWmsCharts() { - document.querySelectorAll('[data-wms-chart]').forEach((canvas) => { - if (canvas.dataset.rendered === 'true') { - return; - } - - const type = canvas.getAttribute('data-wms-chart'); - const labels = parseJsonAttribute(canvas, 'data-labels'); - const series = parseJsonAttribute(canvas, 'data-series'); - const values = parseJsonAttribute(canvas, 'data-values'); - - const datasets = series.length - ? series.map((item, index) => ({ - label: item.label, - data: item.data, - backgroundColor: - chartColours[index % chartColours.length], - borderColor: - chartColours[index % chartColours.length], - borderWidth: 2, - borderRadius: 8, - })) - : [ - { - label: 'Total', - data: values, - backgroundColor: chartColours, - borderColor: 'rgb(255,255,255)', - borderWidth: 2, - }, - ]; - - new Chart(canvas, { - type, - data: { - labels, - datasets, - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - labels: { - color: document.documentElement.classList.contains( - 'dark' - ) - ? '#e4e4e7' - : '#3f3f46', - boxWidth: 12, - boxHeight: 12, - }, - }, - }, - scales: - type === 'doughnut' - ? {} - : { - x: { - ticks: { - color: - document.documentElement.classList.contains( - 'dark' - ) - ? '#d4d4d8' - : '#52525b', - }, - grid: { - color: - 'rgba(113,113,122,0.18)', - }, - }, - y: { - beginAtZero: true, - ticks: { - precision: 0, - color: - document.documentElement.classList.contains( - 'dark' - ) - ? '#d4d4d8' - : '#52525b', - }, - grid: { - color: - 'rgba(113,113,122,0.18)', - }, - }, - }, - }, - }); - - canvas.dataset.rendered = 'true'; - }); -} - - -function livewireComponentFor(element) { - const componentRoot = element.closest('[wire\:id]'); - - if (!componentRoot || !window.Livewire) { - return null; - } - - return window.Livewire.find(componentRoot.getAttribute('wire:id')); -} - -function setLivewireProperty(element, model, value) { - const component = livewireComponentFor(element); - - if (!component || !model) { - return; - } - - component.set(model, value === '' ? null : value); -} - -/** - * Initialise Select2 controls. - * - * @returns {void} - */ -function initialiseSelect2() { - if (!window.jQuery?.fn?.select2) { - return; - } - - $('[data-helpdesk-select2], [data-wms-select2]').each(function () { - const $select = $(this); - - if ($select.hasClass('select2-hidden-accessible')) { - $select.select2('destroy'); - } - - const isMultiple = $select.prop('multiple'); - - $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'), - }); - - $select.off('change.select2-livewire'); - - $select.on('change.select2-livewire', function () { - setLivewireProperty(this, this.dataset.wireModel, $select.val()); - - this.dispatchEvent( - new Event('change', { - bubbles: true, - }) - ); - }); - }); -} - -/** - * Boot page UI. - * - * @returns {void} - */ -function bootUi() { - renderWmsCharts(); - initialiseSelect2(); -} - -document.addEventListener('DOMContentLoaded', bootUi); - -document.addEventListener('livewire:navigated', bootUi); - -document.addEventListener('livewire:init', () => { - Livewire.hook('morphed', () => { - initialiseSelect2(); - }); -}); diff --git a/resources/css/app.css b/resources/css/app.css index 280713c7..5a7ff4d3 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1410,130 +1410,122 @@ html[data-wms-sidebar="collapsed"] .wms-sidebar { } -/* 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; -} +@layer components { + .wms-select2-field .select2-container { + width: 100% !important; + } -.dark .wms-country-select + .select2-container .select2-selection--single { - border-color: rgb(63 63 70); - background: rgb(9 9 11); -} + .wms-select2-field .select2-container--default .select2-selection--single { + min-height: 2.875rem; + border-radius: 0.75rem; + border-color: rgb(212 212 216); + background: white; + box-shadow: 0 1px 2px rgb(15 23 42 / 0.04); + transition: border-color 140ms ease, box-shadow 140ms ease, background 140ms ease; + } -.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; -} + .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__rendered { + padding-left: 0.95rem; + padding-right: 2.75rem; + color: rgb(24 24 27); + font-size: 0.875rem; + font-weight: 600; + line-height: 2.875rem; + } -.dark .wms-country-select + .select2-container .select2-selection--single .select2-selection__rendered { - color: rgb(244 244 245); -} + .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__placeholder { + color: rgb(113 113 122); + font-weight: 500; + } -.wms-country-select + .select2-container .select2-selection--single .select2-selection__placeholder { - color: rgb(113 113 122); -} + .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__arrow { + top: 0.45rem; + right: 0.65rem; + width: 1.75rem; + height: 1.75rem; + border-radius: 0.55rem; + background: rgb(244 244 245); + } -.dark .wms-country-select + .select2-container .select2-selection--single .select2-selection__placeholder { - color: rgb(161 161 170); -} + .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(8 145 178); + box-shadow: 0 0 0 3px rgb(103 232 249 / 0.28); + } -.wms-country-select + .select2-container .select2-selection--single .select2-selection__arrow { - height: 3rem; - right: 0.75rem; - top: 0; -} + .select2-container--default .select2-dropdown { + overflow: hidden; + border-radius: 1rem; + border-color: rgb(212 212 216); + background: white; + box-shadow: 0 24px 60px rgb(15 23 42 / 0.18); + } -.wms-country-select + .select2-container .select2-selection--single .select2-selection__arrow b { - border-color: rgb(113 113 122) transparent transparent transparent; -} + .select2-container--default .select2-search--dropdown { + padding: 0.75rem; + } -.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; -} + .select2-container--default .select2-search--dropdown .select2-search__field { + border-radius: 0.75rem; + border-color: rgb(212 212 216); + padding: 0.55rem 0.75rem; + font-size: 0.875rem; + outline: none; + } -.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--default .select2-search--dropdown .select2-search__field:focus { + border-color: rgb(8 145 178); + box-shadow: 0 0 0 3px rgb(103 232 249 / 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); -} + .select2-container--default .select2-results__option { + padding: 0.65rem 0.85rem; + font-size: 0.875rem; + font-weight: 600; + } -.dark .select2-container--open .select2-dropdown { - background: rgb(24 24 27); - border-color: rgb(63 63 70); -} + .select2-container--default .select2-results__option--highlighted.select2-results__option--selectable { + background: rgb(8 145 178); + color: white; + } -.select2-container--default .select2-search--dropdown { - padding: 0.75rem; - border-bottom: 1px solid rgb(228 228 231); -} + .select2-container--default .select2-results__option--selected { + background: rgb(236 254 255); + color: rgb(14 116 144); + } -.dark .select2-container--default .select2-search--dropdown { - border-bottom-color: rgb(63 63 70); -} + .dark .wms-select2-field .select2-container--default .select2-selection--single { + border-color: rgb(63 63 70); + background: rgb(9 9 11); + } -.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 .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__rendered { + color: white; + } -.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); -} + .dark .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__placeholder { + color: rgb(161 161 170); + } -.select2-container--default .select2-results__option { - padding: 0.7rem 0.9rem; - font-size: 0.875rem; - color: rgb(39 39 42); -} + .dark .wms-select2-field .select2-container--default .select2-selection--single .select2-selection__arrow { + background: rgb(39 39 42); + } -.dark .select2-container--default .select2-results__option { - color: rgb(228 228 231); -} + .dark .select2-container--default .select2-dropdown { + border-color: rgb(63 63 70); + background: rgb(24 24 27); + color: rgb(244 244 245); + } -.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; -} + .dark .select2-container--default .select2-search--dropdown .select2-search__field { + border-color: rgb(63 63 70); + background: rgb(9 9 11); + 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; + .dark .select2-container--default .select2-results__option--selected { + background: rgb(8 47 73); + color: rgb(165 243 252); + } } diff --git a/resources/js/app.js b/resources/js/app.js index 900aeba2..ee6d0713 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -167,7 +167,7 @@ function initialiseSelect2() { return; } - $('[data-helpdesk-select2], [data-company-country-select2]').each(function () { + $('[data-helpdesk-select2], [data-wms-select2]').each(function () { const $select = $(this); if ($select.hasClass('select2-hidden-accessible')) { @@ -176,17 +176,15 @@ 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: false, - placeholder, + dropdownAutoWidth: true, + placeholder: + $select.data('placeholder') || + $select.find('option[value=""]').first().text() || + (isMultiple ? 'Select options' : 'Select option'), }); $select.off('change.select2-livewire');