diff --git a/Modules/Analytics/Resources/views/index.blade.php b/Modules/Analytics/Resources/views/index.blade.php index 20ef061c..6f2bc64b 100644 --- a/Modules/Analytics/Resources/views/index.blade.php +++ b/Modules/Analytics/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Analytics') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/Audit/Resources/views/index.blade.php b/Modules/Audit/Resources/views/index.blade.php index ac69eb22..7a5cd5be 100644 --- a/Modules/Audit/Resources/views/index.blade.php +++ b/Modules/Audit/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Audit') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/CarrierManagement/Resources/views/index.blade.php b/Modules/CarrierManagement/Resources/views/index.blade.php index 0d1d727c..2c7c883a 100644 --- a/Modules/CarrierManagement/Resources/views/index.blade.php +++ b/Modules/CarrierManagement/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Carrier Management') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/Configuration/Resources/views/index.blade.php b/Modules/Configuration/Resources/views/index.blade.php index de587d99..f11ec49d 100644 --- a/Modules/Configuration/Resources/views/index.blade.php +++ b/Modules/Configuration/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Configuration') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/DocumentArchive/Resources/views/index.blade.php b/Modules/DocumentArchive/Resources/views/index.blade.php index 37fda98c..a61dc012 100644 --- a/Modules/DocumentArchive/Resources/views/index.blade.php +++ b/Modules/DocumentArchive/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Document Archive') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/InventoryManagement/Resources/views/index.blade.php b/Modules/InventoryManagement/Resources/views/index.blade.php index 80400a3d..959c2910 100644 --- a/Modules/InventoryManagement/Resources/views/index.blade.php +++ b/Modules/InventoryManagement/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Inventory Management') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/M3Integration/Resources/views/index.blade.php b/Modules/M3Integration/Resources/views/index.blade.php index 392de232..78ebf9bb 100644 --- a/Modules/M3Integration/Resources/views/index.blade.php +++ b/Modules/M3Integration/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'M3 Integration') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/Replenishment/Resources/views/index.blade.php b/Modules/Replenishment/Resources/views/index.blade.php index 09cf100d..f953d09b 100644 --- a/Modules/Replenishment/Resources/views/index.blade.php +++ b/Modules/Replenishment/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Replenishment') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/Reporting/Resources/views/index.blade.php b/Modules/Reporting/Resources/views/index.blade.php index 4f6a1485..16a3a37a 100644 --- a/Modules/Reporting/Resources/views/index.blade.php +++ b/Modules/Reporting/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Reporting') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/Security/Resources/views/index.blade.php b/Modules/Security/Resources/views/index.blade.php index bca50513..4a95291b 100644 --- a/Modules/Security/Resources/views/index.blade.php +++ b/Modules/Security/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Security') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/SentinelIntegration/Resources/views/index.blade.php b/Modules/SentinelIntegration/Resources/views/index.blade.php index 5e2d1789..0524bd10 100644 --- a/Modules/SentinelIntegration/Resources/views/index.blade.php +++ b/Modules/SentinelIntegration/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Sentinel Integration') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/Shipping/Resources/views/index.blade.php b/Modules/Shipping/Resources/views/index.blade.php index 5fb9ab7a..ee264448 100644 --- a/Modules/Shipping/Resources/views/index.blade.php +++ b/Modules/Shipping/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Shipping') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/Modules/Transportation/Resources/views/index.blade.php b/Modules/Transportation/Resources/views/index.blade.php index bfd7314d..9591a113 100644 --- a/Modules/Transportation/Resources/views/index.blade.php +++ b/Modules/Transportation/Resources/views/index.blade.php @@ -1,9 +1,5 @@ -@extends('layouts.app') - -@section('title', 'Transportation') - -@section('content') -
+ +
@@ -120,4 +116,4 @@
-@endsection +
diff --git a/composer.json b/composer.json index 53034903..d8ed3822 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,7 @@ "App\\": "app/", "Database\\Factories\\": "database/factories/", "Database\\Seeders\\": "database/seeders/", - "Modules\\": "modules/" + "Modules\\": "Modules/" } }, "autoload-dev": { diff --git a/modules_statuses.json b/modules_statuses.json index 5c160715..a51d6263 100644 --- a/modules_statuses.json +++ b/modules_statuses.json @@ -1,5 +1,18 @@ { + "Analytics": true, + "Audit": true, + "CarrierManagement": true, + "Configuration": true, + "DocumentArchive": true, + "InternalApi": true, + "InventoryManagement": true, + "M3Integration": true, "Platform": true, - "Warehouse": true, - "InternalApi": true + "Replenishment": true, + "Reporting": true, + "Security": true, + "SentinelIntegration": true, + "Shipping": true, + "Transportation": true, + "Warehouse": true } diff --git a/resources/views/layouts/app/sidebar.blade.php b/resources/views/layouts/app/sidebar.blade.php index 5f08ac64..ad9cf677 100644 --- a/resources/views/layouts/app/sidebar.blade.php +++ b/resources/views/layouts/app/sidebar.blade.php @@ -10,93 +10,167 @@ -
-
- - - {{ __('Warehouse Management System') }} - +
+
{{ __('MPBA') }}
+

{{ $title ?? __('Warehouse Management System') }}

+
- - +
+
+ + {{ __('OK') }} +
- - -
-
- - -
- {{ auth()->user()?->name ?? __('User') }} - {{ auth()->user()?->email ?? '' }} + + + + +
+
+ +
+ {{ auth()->user()?->name ?? __('User') }} + {{ auth()->user()?->email ?? '' }} +
-
- - - - - - - {{ __('Settings') }} - - - - - -
- @csrf - - {{ __('Log out') }} - -
- - + + + {{ __('Settings') }} + +
+ @csrf + {{ __('Log out') }} +
+ + +
@@ -238,9 +271,7 @@
@persist('toast') - - - + @endpersist @fluxScripts @@ -252,9 +283,7 @@ function applySidebarState() { const root = document.documentElement; const saved = localStorage.getItem(STORAGE_KEY) || 'expanded'; - root.dataset.wmsSidebar = saved === 'collapsed' ? 'collapsed' : 'expanded'; - document.querySelectorAll('[data-wms-sidebar-toggle]').forEach((button) => { const expanded = root.dataset.wmsSidebar !== 'collapsed'; button.setAttribute('aria-expanded', expanded ? 'true' : 'false'); @@ -262,62 +291,38 @@ }); } - function closeMobileSidebar() { - document.documentElement.dataset.wmsMobileSidebar = 'closed'; - } - - function openMobileSidebar() { - document.documentElement.dataset.wmsMobileSidebar = 'open'; - } + function closeMobileSidebar() { document.documentElement.dataset.wmsMobileSidebar = 'closed'; } + function openMobileSidebar() { document.documentElement.dataset.wmsMobileSidebar = 'open'; } function bindSidebarControls() { applySidebarState(); - document.querySelectorAll('[data-wms-sidebar-toggle]').forEach((button) => { - if (button.dataset.wmsBound === '1') { - return; - } - + if (button.dataset.wmsBound === '1') return; button.dataset.wmsBound = '1'; - button.addEventListener('click', function () { const root = document.documentElement; const next = root.dataset.wmsSidebar === 'collapsed' ? 'expanded' : 'collapsed'; - root.dataset.wmsSidebar = next; localStorage.setItem(STORAGE_KEY, next); applySidebarState(); }); }); - document.querySelectorAll('[data-wms-mobile-open]').forEach((button) => { - if (button.dataset.wmsBound === '1') { - return; - } - + if (button.dataset.wmsBound === '1') return; button.dataset.wmsBound = '1'; button.addEventListener('click', openMobileSidebar); }); - document.querySelectorAll('[data-wms-mobile-close]').forEach((button) => { - if (button.dataset.wmsBound === '1') { - return; - } - + if (button.dataset.wmsBound === '1') return; button.dataset.wmsBound = '1'; button.addEventListener('click', closeMobileSidebar); }); - document.querySelectorAll('.wms-sidebar-link[wire\\:navigate]').forEach((link) => { - if (link.dataset.wmsMobileBound === '1') { - return; - } - + if (link.dataset.wmsMobileBound === '1') return; link.dataset.wmsMobileBound = '1'; link.addEventListener('click', closeMobileSidebar); }); } - document.addEventListener('DOMContentLoaded', bindSidebarControls); document.addEventListener('livewire:navigated', bindSidebarControls); })(); diff --git a/storage/framework/views/01b6a1d69cb0e65c6eb92ca89e9f1362.php b/storage/framework/views/01b6a1d69cb0e65c6eb92ca89e9f1362.php new file mode 100755 index 00000000..d726c5e5 --- /dev/null +++ b/storage/framework/views/01b6a1d69cb0e65c6eb92ca89e9f1362.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Analytics')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Analytics'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Analytics

+

Display KPIs, warehouse performance trends, executive summaries, and OTTC analysis.

+
+ + +
+
+
+
+ +
+
+
+
01
+

KPI1 Bar

+

Placeholder area for kpi1 bar.

+
+
+
+
+
+
+
02
+

KPI1 Line

+

Placeholder area for kpi1 line.

+
+
+
+
+
+
+
03
+

OTTC

+

Placeholder area for ottc.

+
+
+
+
+
+
+
04
+

Warehouse Performance

+

Placeholder area for warehouse performance.

+
+
+
+ +
+
+
+
+
+

Analytics workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MetricTodayWeekAction
KPI1 BarSample 1ActiveOpen
KPI1 LineSample 2ActiveOpen
OTTCSample 3ActiveOpen
Warehouse PerformanceSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/0be6009d8e895f6591baf39f8626f3e9.php b/storage/framework/views/0be6009d8e895f6591baf39f8626f3e9.php new file mode 100755 index 00000000..b7e88bba --- /dev/null +++ b/storage/framework/views/0be6009d8e895f6591baf39f8626f3e9.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Audit')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Audit'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Audit

+

Review activity logs, data changes, interface events, and administrative actions.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Activity Log

+

Placeholder area for activity log.

+
+
+
+
+
+
+
02
+

Data Changes

+

Placeholder area for data changes.

+
+
+
+
+
+
+
03
+

API Audit

+

Placeholder area for api audit.

+
+
+
+
+
+
+
04
+

User Actions

+

Placeholder area for user actions.

+
+
+
+ +
+
+
+
+
+

Audit workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WhenUserActionAction
Activity LogSample 1ActiveOpen
Data ChangesSample 2ActiveOpen
API AuditSample 3ActiveOpen
User ActionsSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/1f834b69caf8bd1d2a365b093c8d319a.php b/storage/framework/views/1f834b69caf8bd1d2a365b093c8d319a.php new file mode 100755 index 00000000..cf2615c3 --- /dev/null +++ b/storage/framework/views/1f834b69caf8bd1d2a365b093c8d319a.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Inventory Management')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Inventory Management'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Inventory Management

+

Track inventory movement, stock visibility, pallet quantities, and product availability.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Stock Overview

+

Placeholder area for stock overview.

+
+
+
+
+
+
+
02
+

Pallet Quantity Rules

+

Placeholder area for pallet quantity rules.

+
+
+
+
+
+
+
03
+

Inventory Exceptions

+

Placeholder area for inventory exceptions.

+
+
+
+
+
+
+
04
+

Product Movement

+

Placeholder area for product movement.

+
+
+
+ +
+
+
+
+
+

Inventory Management workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Part NumberDescriptionPallet QtyAction
Stock OverviewSample 1ActiveOpen
Pallet Quantity RulesSample 2ActiveOpen
Inventory ExceptionsSample 3ActiveOpen
Product MovementSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/2120c11a2bda9c77b0dd624ade769be1.php b/storage/framework/views/2120c11a2bda9c77b0dd624ade769be1.php new file mode 100755 index 00000000..620f9664 --- /dev/null +++ b/storage/framework/views/2120c11a2bda9c77b0dd624ade769be1.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Sentinel Integration')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Sentinel Integration'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Sentinel Integration

+

Track Sentinel API activity, inbound receiver events, and interface status.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Sentinel Receiver

+

Placeholder area for sentinel receiver.

+
+
+
+
+
+
+
02
+

API Activity

+

Placeholder area for api activity.

+
+
+
+
+
+
+
03
+

Interface Health

+

Placeholder area for interface health.

+
+
+
+
+
+
+
04
+

Payload Archive

+

Placeholder area for payload archive.

+
+
+
+ +
+
+
+
+
+

Sentinel Integration workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EndpointReferenceHTTPAction
Sentinel ReceiverSample 1ActiveOpen
API ActivitySample 2ActiveOpen
Interface HealthSample 3ActiveOpen
Payload ArchiveSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/26ce670a412c2b24fc8c72f505ca037a.php b/storage/framework/views/26ce670a412c2b24fc8c72f505ca037a.php new file mode 100755 index 00000000..00fea0ec --- /dev/null +++ b/storage/framework/views/26ce670a412c2b24fc8c72f505ca037a.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Replenishment')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Replenishment'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Replenishment

+

Configure replenishment thresholds, review stock gaps, and monitor refill activity.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Replenishment Rules

+

Placeholder area for replenishment rules.

+
+
+
+
+
+
+
02
+

Minimum Stock

+

Placeholder area for minimum stock.

+
+
+
+
+
+
+
03
+

Suggested Transfers

+

Placeholder area for suggested transfers.

+
+
+
+
+
+
+
04
+

Exception Queue

+

Placeholder area for exception queue.

+
+
+
+ +
+
+
+
+
+

Replenishment workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ItemMinimumCurrentAction
Replenishment RulesSample 1ActiveOpen
Minimum StockSample 2ActiveOpen
Suggested TransfersSample 3ActiveOpen
Exception QueueSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/3d7c4a50df1d313410bd5444267891cf.php b/storage/framework/views/3d7c4a50df1d313410bd5444267891cf.php index 74094875..c31241bd 100755 --- a/storage/framework/views/3d7c4a50df1d313410bd5444267891cf.php +++ b/storage/framework/views/3d7c4a50df1d313410bd5444267891cf.php @@ -3,7 +3,6 @@ - @@ -18,7 +17,6 @@ - @@ -37,93 +35,167 @@ -
-
- - - +
+
+

+
-
+
+
+ + +
- + - ensureRequired('/Users/mag/Sites/WMS/Core/vendor/livewire/flux/src/../stubs/resources/views/flux/profile.blade.php', $__blaze->compiledPath.'/986fb95c0ca2c23afb55cc89b1b07e48.php'); ?> + ensureRequired('/Users/mag/Sites/WMS/Core/vendor/livewire/flux/src/../stubs/resources/views/flux/profile.blade.php', $__blaze->compiledPath.'/986fb95c0ca2c23afb55cc89b1b07e48.php'); ?> pushData(['initials' => auth()->user()?->initials() ?? 'U','iconTrailing' => 'chevron-down']); ?> auth()->user()?->initials() ?? 'U','iconTrailing' => 'chevron-down'], [], ['initials'], ['iconTrailing' => 'icon-trailing'], $__this ?? (isset($this) ? $this : null)); ?> popData(); ?> - - - + -
-
- auth()->user()?->name ?? 'User', 'initials' => auth()->user()?->initials() ?? 'U']); ?>ensureRequired('/Users/mag/Sites/WMS/Core/vendor/livewire/flux/src/../stubs/resources/views/flux/avatar/index.blade.php', $__blaze->compiledPath.'/110c1c2a55a80ff8b2b4fbed62b1a7d3.php'); ?> +
+
+ auth()->user()?->name ?? 'User', 'initials' => auth()->user()?->initials() ?? 'U']); ?>ensureRequired('/Users/mag/Sites/WMS/Core/vendor/livewire/flux/src/../stubs/resources/views/flux/avatar/index.blade.php', $__blaze->compiledPath.'/110c1c2a55a80ff8b2b4fbed62b1a7d3.php'); ?> pushData(['name' => auth()->user()?->name ?? 'User','initials' => auth()->user()?->initials() ?? 'U']); ?> auth()->user()?->name ?? 'User','initials' => auth()->user()?->initials() ?? 'U'], [], ['name', 'initials'], [], $__this ?? (isset($this) ? $this : null)); ?> popData(); ?> - -
-
user()?->name ?? __('User')); ?>
+
+
user()?->name ?? __('User')); ?>
-

user()?->email ?? ''); ?>

+

user()?->email ?? ''); ?>

+
-
- + - -
+
- - - - - - - + - - - - - -
+
- - - - - - + + - + +
@@ -391,8 +428,7 @@ forceAssetInjection(); ?>
- - + - - + @@ -460,9 +495,7 @@ function applySidebarState() { const root = document.documentElement; const saved = localStorage.getItem(STORAGE_KEY) || 'expanded'; - root.dataset.wmsSidebar = saved === 'collapsed' ? 'collapsed' : 'expanded'; - document.querySelectorAll('[data-wms-sidebar-toggle]').forEach((button) => { const expanded = root.dataset.wmsSidebar !== 'collapsed'; button.setAttribute('aria-expanded', expanded ? 'true' : 'false'); @@ -470,62 +503,38 @@ }); } - function closeMobileSidebar() { - document.documentElement.dataset.wmsMobileSidebar = 'closed'; - } - - function openMobileSidebar() { - document.documentElement.dataset.wmsMobileSidebar = 'open'; - } + function closeMobileSidebar() { document.documentElement.dataset.wmsMobileSidebar = 'closed'; } + function openMobileSidebar() { document.documentElement.dataset.wmsMobileSidebar = 'open'; } function bindSidebarControls() { applySidebarState(); - document.querySelectorAll('[data-wms-sidebar-toggle]').forEach((button) => { - if (button.dataset.wmsBound === '1') { - return; - } - + if (button.dataset.wmsBound === '1') return; button.dataset.wmsBound = '1'; - button.addEventListener('click', function () { const root = document.documentElement; const next = root.dataset.wmsSidebar === 'collapsed' ? 'expanded' : 'collapsed'; - root.dataset.wmsSidebar = next; localStorage.setItem(STORAGE_KEY, next); applySidebarState(); }); }); - document.querySelectorAll('[data-wms-mobile-open]').forEach((button) => { - if (button.dataset.wmsBound === '1') { - return; - } - + if (button.dataset.wmsBound === '1') return; button.dataset.wmsBound = '1'; button.addEventListener('click', openMobileSidebar); }); - document.querySelectorAll('[data-wms-mobile-close]').forEach((button) => { - if (button.dataset.wmsBound === '1') { - return; - } - + if (button.dataset.wmsBound === '1') return; button.dataset.wmsBound = '1'; button.addEventListener('click', closeMobileSidebar); }); - document.querySelectorAll('.wms-sidebar-link[wire\\:navigate]').forEach((link) => { - if (link.dataset.wmsMobileBound === '1') { - return; - } - + if (link.dataset.wmsMobileBound === '1') return; link.dataset.wmsMobileBound = '1'; link.addEventListener('click', closeMobileSidebar); }); } - document.addEventListener('DOMContentLoaded', bindSidebarControls); document.addEventListener('livewire:navigated', bindSidebarControls); })(); diff --git a/storage/framework/views/41544f6a1ba43d56811ba56686106b08.php b/storage/framework/views/41544f6a1ba43d56811ba56686106b08.php new file mode 100755 index 00000000..3c74a2ed --- /dev/null +++ b/storage/framework/views/41544f6a1ba43d56811ba56686106b08.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Security')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Security'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Security

+

Control roles, permissions, audit visibility, and protected administration actions.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Roles

+

Placeholder area for roles.

+
+
+
+
+
+
+
02
+

Permissions

+

Placeholder area for permissions.

+
+
+
+
+
+
+
03
+

Admin Access

+

Placeholder area for admin access.

+
+
+
+
+
+
+
04
+

Security Audit

+

Placeholder area for security audit.

+
+
+
+ +
+
+
+
+
+

Security workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AreaPermissionRoleAction
RolesSample 1ActiveOpen
PermissionsSample 2ActiveOpen
Admin AccessSample 3ActiveOpen
Security AuditSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/5899f91fce72098f59725f5cb819c13e.php b/storage/framework/views/5899f91fce72098f59725f5cb819c13e.php new file mode 100755 index 00000000..6d888242 --- /dev/null +++ b/storage/framework/views/5899f91fce72098f59725f5cb819c13e.php @@ -0,0 +1,40 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Payments')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Payments'))]); ?> + + +
+
+

Payments and overrides

+
+ + + + + + + +
+
+
addLoop($__currentLoopData); foreach($__currentLoopData as $payment): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>popLoop(); $loop = $__env->getLastLoop(); ?>
CompanyAmountStatusDescription
company->name); ?>currency)); ?> amount / 100, 2)); ?>status); ?>description); ?>
+ links()); ?> + +
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/78043e263c465d241c83bb3e6bd6c3b0.php b/storage/framework/views/78043e263c465d241c83bb3e6bd6c3b0.php new file mode 100755 index 00000000..2785b601 --- /dev/null +++ b/storage/framework/views/78043e263c465d241c83bb3e6bd6c3b0.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Carrier Management')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Carrier Management'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Carrier Management

+

Maintain carriers, services, and shipment routing master data.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Carrier Directory

+

Placeholder area for carrier directory.

+
+
+
+
+
+
+
02
+

Carrier Service Levels

+

Placeholder area for carrier service levels.

+
+
+
+
+
+
+
03
+

Carrier Rules

+

Placeholder area for carrier rules.

+
+
+
+
+
+
+
04
+

Carrier Audit

+

Placeholder area for carrier audit.

+
+
+
+ +
+
+
+
+
+

Carrier Management workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CarrierServiceEnabledAction
Carrier DirectorySample 1ActiveOpen
Carrier Service LevelsSample 2ActiveOpen
Carrier RulesSample 3ActiveOpen
Carrier AuditSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/b0a77c3b0b358414aecdb8282d030b3a.php b/storage/framework/views/b0a77c3b0b358414aecdb8282d030b3a.php new file mode 100755 index 00000000..e01283e0 --- /dev/null +++ b/storage/framework/views/b0a77c3b0b358414aecdb8282d030b3a.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Shipping')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Shipping'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Shipping

+

Monitor shipment activity, closed orders, pending loads, and dispatch status.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Open Shipments

+

Placeholder area for open shipments.

+
+
+
+
+
+
+
02
+

Closed Orders

+

Placeholder area for closed orders.

+
+
+
+
+
+
+
03
+

Shipment Close Calls

+

Placeholder area for shipment close calls.

+
+
+
+
+
+
+
04
+

Dispatch Exceptions

+

Placeholder area for dispatch exceptions.

+
+
+
+ +
+
+
+
+
+

Shipping workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ShipmentOrderCustomerAction
Open ShipmentsSample 1ActiveOpen
Closed OrdersSample 2ActiveOpen
Shipment Close CallsSample 3ActiveOpen
Dispatch ExceptionsSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/bd0087de9fe1acf6295244bef8cc153f.php b/storage/framework/views/bd0087de9fe1acf6295244bef8cc153f.php new file mode 100755 index 00000000..072d6b02 --- /dev/null +++ b/storage/framework/views/bd0087de9fe1acf6295244bef8cc153f.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('M3 Integration')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('M3 Integration'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

M3 Integration

+

Observe M3 transactions, delivery methods, close shipment calls, and API exchanges.

+
+ + +
+
+
+
+ +
+
+
+
01
+

M3 Transaction Log

+

Placeholder area for m3 transaction log.

+
+
+
+
+
+
+
02
+

M3 Delivery Methods

+

Placeholder area for m3 delivery methods.

+
+
+
+
+
+
+
03
+

Close Shipment API

+

Placeholder area for close shipment api.

+
+
+
+
+
+
+
04
+

M3 Error Queue

+

Placeholder area for m3 error queue.

+
+
+
+ +
+
+
+
+
+

M3 Integration workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TransactionProgramMessageAction
M3 Transaction LogSample 1ActiveOpen
M3 Delivery MethodsSample 2ActiveOpen
Close Shipment APISample 3ActiveOpen
M3 Error QueueSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/c31163ad25911d9723704c61e825eda9.php b/storage/framework/views/c31163ad25911d9723704c61e825eda9.php new file mode 100755 index 00000000..4ffe5f28 --- /dev/null +++ b/storage/framework/views/c31163ad25911d9723704c61e825eda9.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Reporting')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Reporting'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Reporting

+

Provide operational reports for closed orders, user efficiency, and transaction history.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Closed Order Lines

+

Placeholder area for closed order lines.

+
+
+
+
+
+
+
02
+

Daily User Efficiency

+

Placeholder area for daily user efficiency.

+
+
+
+
+
+
+
03
+

Export Centre

+

Placeholder area for export centre.

+
+
+
+
+
+
+
04
+

Report Filters

+

Placeholder area for report filters.

+
+
+
+ +
+
+
+
+
+

Reporting workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ReportPeriodRowsAction
Closed Order LinesSample 1ActiveOpen
Daily User EfficiencySample 2ActiveOpen
Export CentreSample 3ActiveOpen
Report FiltersSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/d72a6b0b1c3068e86c42fb68f1c7446c.php b/storage/framework/views/d72a6b0b1c3068e86c42fb68f1c7446c.php new file mode 100755 index 00000000..96f89539 --- /dev/null +++ b/storage/framework/views/d72a6b0b1c3068e86c42fb68f1c7446c.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Configuration')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Configuration'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Configuration

+

Centralise system settings, warehouse defaults, interface flags, and feature switches.

+
+ + +
+
+
+
+ +
+
+
+
01
+

System Settings

+

Placeholder area for system settings.

+
+
+
+
+
+
+
02
+

Feature Flags

+

Placeholder area for feature flags.

+
+
+
+
+
+
+
03
+

Warehouse Defaults

+

Placeholder area for warehouse defaults.

+
+
+
+
+
+
+
04
+

Interface Settings

+

Placeholder area for interface settings.

+
+
+
+ +
+
+
+
+
+

Configuration workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SettingValueScopeAction
System SettingsSample 1ActiveOpen
Feature FlagsSample 2ActiveOpen
Warehouse DefaultsSample 3ActiveOpen
Interface SettingsSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/d82dcf8506aa75b75605cd2014e2effa.php b/storage/framework/views/d82dcf8506aa75b75605cd2014e2effa.php new file mode 100755 index 00000000..f64ef94e --- /dev/null +++ b/storage/framework/views/d82dcf8506aa75b75605cd2014e2effa.php @@ -0,0 +1,40 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Licence Keys')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Licence Keys'))]); ?> + + +
+
New key:
+
+

Licence keys

+
+ + + + + + +
+
+
addLoop($__currentLoopData); foreach($__currentLoopData as $license): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>popLoop(); $loop = $__env->getLastLoop(); ?>
CompanyLabelPlanPrefixExpires
company->name); ?>label); ?>plan); ?>key_prefix); ?>expires_at)->toDateString()); ?>
+ links()); ?> + +
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/f415e345a640eb9f187225d05f164521.php b/storage/framework/views/f415e345a640eb9f187225d05f164521.php new file mode 100755 index 00000000..989076ec --- /dev/null +++ b/storage/framework/views/f415e345a640eb9f187225d05f164521.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Document Archive')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Document Archive'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Document Archive

+

Search cached Sentinel documents, shipment PDFs, labels, and BOL records.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Sentinel Doc Archive

+

Placeholder area for sentinel doc archive.

+
+
+
+
+
+
+
02
+

Shipment PDFs

+

Placeholder area for shipment pdfs.

+
+
+
+
+
+
+
03
+

Bill of Lading

+

Placeholder area for bill of lading.

+
+
+
+
+
+
+
04
+

Document Search

+

Placeholder area for document search.

+
+
+
+ +
+
+
+
+
+

Document Archive workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DocumentShipmentTypeAction
Sentinel Doc ArchiveSample 1ActiveOpen
Shipment PDFsSample 2ActiveOpen
Bill of LadingSample 3ActiveOpen
Document SearchSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/storage/framework/views/fb4daa35e4a9b8b624a59e9cc5576e1d.php b/storage/framework/views/fb4daa35e4a9b8b624a59e9cc5576e1d.php new file mode 100755 index 00000000..5c61debf --- /dev/null +++ b/storage/framework/views/fb4daa35e4a9b8b624a59e9cc5576e1d.php @@ -0,0 +1,140 @@ + + + 'f4ac99e09542ff494432bc959d4fee61::app','data' => ['title' => __('Transportation')]] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?> +withName('layouts::app'); ?> +shouldRender()): ?> +startComponent($component->resolveView(), $component->data()); ?> + +except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> + +withAttributes(['title' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(__('Transportation'))]); ?> + + +
+
+
+
+
+
+
+
Replacement module
+

Transportation

+

Manage truck closure, freight terms, trailers, and transport operations.

+
+ + +
+
+
+
+ +
+
+
+
01
+

Pending Truck Closure

+

Placeholder area for pending truck closure.

+
+
+
+
+
+
+
02
+

Trailer Activity

+

Placeholder area for trailer activity.

+
+
+
+
+
+
+
03
+

Freight Terms

+

Placeholder area for freight terms.

+
+
+
+
+
+
+
04
+

Transport Exceptions

+

Placeholder area for transport exceptions.

+
+
+
+ +
+
+
+
+
+

Transportation workbench

+

Sample table layout ready to wire into controllers, queries, DataTables, or Livewire.

+
+ +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TrailerCarrierClose StatusAction
Pending Truck ClosureSample 1ActiveOpen
Trailer ActivitySample 2ActiveOpen
Freight TermsSample 3ActiveOpen
Transport ExceptionsSample 4ActiveOpen
+
+
+
+
+
+
+ renderComponent(); ?> + + + + + + + + + + \ No newline at end of file diff --git a/tests/Feature/Modules/ModuleSidebarAndStatusTest.php b/tests/Feature/Modules/ModuleSidebarAndStatusTest.php new file mode 100644 index 00000000..3ad96ced --- /dev/null +++ b/tests/Feature/Modules/ModuleSidebarAndStatusTest.php @@ -0,0 +1,77 @@ + + */ + public static function moduleRoutes(): array + { + return [ + ['analytics.index', 'Analytics'], + ['audit.index', 'Audit'], + ['carrier-management.index', 'Carrier Management'], + ['configuration.index', 'Configuration'], + ['document-archive.index', 'Document Archive'], + ['inventory-management.index', 'Inventory Management'], + ['m3-integration.index', 'M3 Integration'], + ['replenishment.index', 'Replenishment'], + ['reporting.index', 'Reporting'], + ['security.index', 'Security'], + ['sentinel-integration.index', 'Sentinel Integration'], + ['shipping.index', 'Shipping'], + ['transportation.index', 'Transportation'], + ]; + } + + /** + * @dataProvider moduleRoutes + */ + public function test_enabled_module_route_renders(string $routeName, string $label): void + { + $this->assertTrue(Route::has($routeName), "Missing route [{$routeName}]."); + + $user = UserFactory::new()->mpbaPlatformAdmin()->create(); + + $this->actingAs($user) + ->get(route($routeName)) + ->assertOk() + ->assertSee($label, false) + ->assertSee('Warehouse Management System', false); + } + + public function test_sidebar_contains_all_enabled_web_modules(): void + { + $user = UserFactory::new()->mpbaPlatformAdmin()->create(); + + $response = $this->actingAs($user)->get(route('platform.dashboard'))->assertOk(); + + foreach (self::moduleRoutes() as [$routeName, $label]) { + $response->assertSee(route($routeName), false); + } + + foreach (['Analytics', 'Inventory', 'Replenishment', 'Shipping', 'Transportation', 'Carriers', 'M3 Integration', 'Sentinel', 'Documents', 'Reporting', 'Configuration', 'Security', 'Audit'] as $label) { + $response->assertSee($label, false); + } + } + + public function test_modules_statuses_file_enables_all_modules(): void + { + $statuses = json_decode(file_get_contents(base_path('modules_statuses.json')), true, 512, JSON_THROW_ON_ERROR); + + foreach ([ + 'Analytics', 'Audit', 'CarrierManagement', 'Configuration', 'DocumentArchive', 'InternalApi', + 'InventoryManagement', 'M3Integration', 'Platform', 'Replenishment', 'Reporting', 'Security', + 'SentinelIntegration', 'Shipping', 'Transportation', 'Warehouse', + ] as $module) { + $this->assertArrayHasKey($module, $statuses); + $this->assertTrue($statuses[$module], "Module [{$module}] is not enabled."); + } + } +}