Files

940 lines
48 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
@include('partials.head')
<script>
(function() {
const SIDEBAR_STATE_KEY = 'wms-sidebar-state';
const SIDEBAR_WIDTH_KEY = 'wms-sidebar-width';
const DEFAULT_WIDTH = 288;
const MIN_WIDTH = 224;
const MAX_WIDTH = 448;
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
const savedWidth = Number.parseInt(localStorage.getItem(SIDEBAR_WIDTH_KEY) || DEFAULT_WIDTH, 10);
const width = clamp(Number.isFinite(savedWidth) ? savedWidth : DEFAULT_WIDTH, MIN_WIDTH, MAX_WIDTH);
const savedState = localStorage.getItem(SIDEBAR_STATE_KEY) || 'expanded';
document.documentElement.style.setProperty('--wms-sidebar-width', `${width}px`);
document.documentElement.dataset.wmsSidebar = savedState === 'collapsed' ? 'collapsed' : 'expanded';
})();
</script>
<body class="min-h-screen bg-white font-sans text-zinc-950 antialiased dark:bg-zinc-950 dark:text-zinc-100">
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
aria-hidden="true"></div>
@vite(['resources/css/app.css'])
<div class="wms-app-shell" data-wms-shell data-wms-mobile-shell>
<button type="button" class="wms-mobile-overlay" data-wms-mobile-close
aria-label="{{ __('Close navigation menu') }}"></button>
<aside id="wms-main-sidebar" class="wms-sidebar" data-wms-sidebar aria-label="{{ __('Main navigation') }}">
<div class="wms-sidebar-header">
<a href="{{ auth()->user()?->isMpbaPlatformAdmin() && Route::has('platform.dashboard') ? route('platform.dashboard') : route('dashboard') }}"
wire:navigate class="wms-brand" aria-label="{{ __('Warehouse Management System') }}">
<span class="wms-brand-mark" data-wms-brand-toggle title="{{ __('Expand sidebar') }}">
<span class="wms-brand-mark-inner">W</span>
</span>
<span class="wms-brand-copy" data-wms-sidebar-label>
<span class="wms-brand-title">{{ __('Warehouse Management System') }}</span>
<span class="wms-brand-subtitle">MPBA</span>
</span>
</a>
<button type="button" class="wms-sidebar-toggle" data-wms-sidebar-toggle
aria-label="{{ __('Collapse sidebar') }}" aria-expanded="true">
<span class="wms-toggle-expanded">‹</span>
</button>
<button type="button" class="wms-mobile-close" data-wms-mobile-close
aria-label="{{ __('Close navigation') }}">×</button>
</div>
<nav class="wms-sidebar-nav" aria-label="{{ __('Main navigation') }}">
@php
$platformRoute =
auth()->user()?->isMpbaPlatformAdmin() && Route::has('platform.dashboard')
? 'platform.dashboard'
: (Route::has('dashboard')
? 'dashboard'
: null);
@endphp
@if ($platformRoute)
<div class="wms-sidebar-group" data-wms-menu-group="platform"
@if (request()->routeIs('platform.dashboard') || request()->routeIs('dashboard')) data-wms-selected="true" data-wms-has-active="true" @endif>
<a href="{{ route($platformRoute) }}" wire:navigate
class="wms-sidebar-heading wms-sidebar-heading-button wms-sidebar-primary-link"
title="{{ __('Platform') }}" data-wms-sidebar-label>
<i class="fa-solid fa-screwdriver-wrench wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Platform') }}</span>
</a>
</div>
@endif
<div class="wms-sidebar-group" data-wms-menu-group="operations">
<button type="button" class="wms-sidebar-heading wms-sidebar-heading-button" data-wms-menu-toggle
data-wms-sidebar-label aria-expanded="false">
<span class="wms-sidebar-heading-content">
<i class="fa-solid fa-chart-simple wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Operations') }}</span>
</span>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@if (Route::has('analytics.index'))
<a href="{{ route('analytics.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('analytics.*')) is-active @endif"
title="{{ __('Analytics') }}">
<i class="fa-solid fa-chart-line wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Analytics') }}</span>
</a>
@endif
@if (Route::has('inventory-management.index'))
<a href="{{ route('inventory-management.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('inventory-management.*')) is-active @endif"
title="{{ __('Inventory') }}">
<i class="fa-solid fa-boxes-stacked wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Inventory') }}</span>
</a>
@endif
@if (Route::has('replenishment.index'))
<a href="{{ route('replenishment.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('replenishment.*')) is-active @endif"
title="{{ __('Replenishment') }}">
<i class="fa-solid fa-arrows-rotate wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Replenishment') }}</span>
</a>
@endif
@if (Route::has('shipping.index'))
<a href="{{ route('shipping.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('shipping.*')) is-active @endif"
title="{{ __('Shipping') }}">
<i class="fa-solid fa-truck-fast wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Shipping') }}</span>
</a>
@endif
@if (Route::has('transportation.index'))
<a href="{{ route('transportation.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('transportation.*')) is-active @endif"
title="{{ __('Transportation') }}">
<i class="fa-solid fa-truck-ramp-box wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Transportation') }}</span>
</a>
@endif
@if (Route::has('carrier-management.index'))
<a href="{{ route('carrier-management.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('carrier-management.*')) is-active @endif"
title="{{ __('Carriers') }}">
<i class="fa-solid fa-route wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Carriers') }}</span>
</a>
@endif
</div>
</div>
@php
$helpdeskEnabled = Route::has('helpdesk.index') || Route::has('helpdesk.tickets.index');
$helpdeskSettingsRoute = Route::has('helpdesk.settings')
? 'helpdesk.settings'
: (Route::has('helpdesk.settings.index')
? 'helpdesk.settings.index'
: null);
$helpdeskGitRoute = Route::has('helpdesk.git.index') ? 'helpdesk.git.index' : null;
$helpdeskWorkflowRoute = Route::has('helpdesk.workflow-templates.index')
? 'helpdesk.workflow-templates.index'
: null;
$documentationEnabled =
Route::has('documentation.index') || Route::has('documentation.documents.index');
$documentationRoute = Route::has('documentation.index')
? 'documentation.index'
: (Route::has('documentation.documents.index')
? 'documentation.documents.index'
: null);
@endphp
@if ($helpdeskEnabled || $documentationEnabled)
<div class="wms-sidebar-group" data-wms-menu-group="support">
@if (Route::has('helpdesk.index'))
<a href="{{ route('helpdesk.index') }}" wire:navigate
class="wms-sidebar-heading wms-sidebar-heading-button @if (request()->routeIs('helpdesk.*')) is-active @endif"
data-wms-sidebar-label title="{{ __('Support') }}">
<span class="wms-sidebar-heading-content">
<i class="fa-solid fa-headset wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Support') }}</span>
</span>
</a>
@else
<button type="button" class="wms-sidebar-heading wms-sidebar-heading-button"
data-wms-menu-toggle data-wms-sidebar-label aria-expanded="false">
<span class="wms-sidebar-heading-content">
<i class="fa-solid fa-headset wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Support') }}</span>
</span>
</button>
@endif
<div class="wms-sidebar-group-items mt-2 space-y-1" data-wms-menu-items>
@if ($helpdeskGitRoute)
<a href="{{ route($helpdeskGitRoute) }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('helpdesk.git.*')) is-active @endif"
title="{{ __('Git Tracking') }}">
<i class="fa-solid fa-code-commit wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Git Tracking') }}</span>
</a>
@endif
@if ($helpdeskWorkflowRoute)
<a href="{{ route($helpdeskWorkflowRoute) }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('helpdesk.workflow-templates.*')) is-active @endif"
title="{{ __('Workflow Emails') }}">
<i class="fa-solid fa-envelope-circle-check wms-sidebar-icon"
aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Workflow Emails') }}</span>
</a>
@endif
@if ($documentationRoute)
<a href="{{ route($documentationRoute) }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('documentation.*')) is-active @endif"
title="{{ __('Documentation') }}">
<i class="fa-solid fa-book-open wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Documentation') }}</span>
</a>
@endif
@if ($helpdeskSettingsRoute)
<a href="{{ route($helpdeskSettingsRoute) }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('helpdesk.settings') || request()->routeIs('helpdesk.settings.index')) is-active @endif"
title="{{ __('Helpdesk Settings') }}">
<i class="fa-solid fa-gears wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Settings') }}</span>
</a>
@endif
</div>
</div>
@endif
<div class="wms-sidebar-group" data-wms-menu-group="integration-reporting" @if (request()->routeIs('m3.*')) data-wms-selected="true" data-wms-has-active="true" data-wms-menu-open="true" @endif>
<button type="button" class="wms-sidebar-heading wms-sidebar-heading-button" data-wms-menu-toggle
data-wms-sidebar-label aria-expanded="false">
<span class="wms-sidebar-heading-content">
<i class="fa-solid fa-plug-circle-bolt wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Integration & Reporting') }}</span>
</span>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@if (Route::has('m3-integration.index'))
<a href="{{ route('m3-integration.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('m3-integration.*')) is-active @endif"
title="{{ __('M3 Integration') }}">
<i class="fa-solid fa-arrows-rotate wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('M3 Integration') }}</span>
</a>
@endif
@if (Route::has('m3.index'))
<a href="{{ route('m3.index') }}"
class="wms-sidebar-link @if (request()->routeIs('m3.*')) is-active @endif"
title="{{ __('M3') }}">
<i class="fa-solid fa-cubes-stacked wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('M3') }}</span>
</a>
@if (request()->routeIs('m3.*'))
<div class="wms-m3-submenu space-y-1 border-l border-zinc-200/70 pl-3 dark:border-zinc-700/70" data-wms-sidebar-label>
<a href="{{ route('m3.index') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.index')) is-active @endif"><i class="fa-solid fa-gauge-high wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('Dashboard') }}</span></a>
@if (Route::has('m3.replenishmentSettings.index'))<a href="{{ route('m3.replenishmentSettings.index') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.replenishmentSettings.*')) is-active @endif"><i class="fa-solid fa-sliders wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('Replenishment Settings') }}</span></a>@endif
@if (Route::has('m3.M3ErrorsDisplay.index'))<a href="{{ route('m3.M3ErrorsDisplay.index') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.M3ErrorsDisplay*')) is-active @endif"><i class="fa-solid fa-list-check wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('Transaction Log') }}</span></a>@endif
@if (Route::has('m3.M3GetPDFFromCache.index'))<a href="{{ route('m3.M3GetPDFFromCache.index') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.M3GetPDFFromCache*')) is-active @endif"><i class="fa-solid fa-box-archive wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('Sentinel Doc Archive') }}</span></a>@endif
@if (Route::has('m3.userControl'))<a href="{{ route('m3.userControl') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.userControl*')) is-active @endif"><i class="fa-solid fa-users-gear wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('User Control') }}</span></a>@endif
@if (Route::has('m3.palletQty'))<a href="{{ route('m3.palletQty') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.palletQty*')) is-active @endif"><i class="fa-solid fa-boxes-stacked wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('ERP/TMS Part Data') }}</span></a>@endif
@if (Route::has('m3.M3Delivery'))<a href="{{ route('m3.M3Delivery') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.M3Delivery*')) is-active @endif"><i class="fa-solid fa-truck-fast wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('M3 Delivery Methods') }}</span></a>@endif
@if (Route::has('m3.OrderLinesOfClosedOrder'))<a href="{{ route('m3.OrderLinesOfClosedOrder') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.OrderLinesOfClosedOrder')) is-active @endif"><i class="fa-solid fa-file-excel wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('Closed Order Lines') }}</span></a>@endif
@if (Route::has('m3.carrierMaint'))<a href="{{ route('m3.carrierMaint') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.carrierMaint*')) is-active @endif"><i class="fa-solid fa-location-dot wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('Distribution WHS Addresses') }}</span></a>@endif
@if (Route::has('m3.UserPickingStats4'))<a href="{{ route('m3.UserPickingStats4') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.UserPickingStats4')) is-active @endif"><i class="fa-solid fa-chart-column wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('KPI1 Bar') }}</span></a>@endif
@if (Route::has('m3.UserPickingStats5'))<a href="{{ route('m3.UserPickingStats5') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.UserPickingStats5')) is-active @endif"><i class="fa-solid fa-chart-line wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('KPI1 Line') }}</span></a>@endif
@if (Route::has('m3.execsummary'))<a href="{{ route('m3.execsummary') }}" class="wms-sidebar-link wms-m3-submenu-link @if (request()->routeIs('m3.execsummary*')) is-active @endif"><i class="fa-solid fa-chart-simple wms-sidebar-icon" aria-hidden="true"></i><span>{{ __('OTTC') }}</span></a>@endif
</div>
@endif
@endif
@if (Route::has('sentinel-integration.index'))
<a href="{{ route('sentinel-integration.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('sentinel-integration.*')) is-active @endif"
title="{{ __('Sentinel') }}">
<i class="fa-solid fa-satellite-dish wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Sentinel') }}</span>
</a>
@endif
@if (Route::has('document-archive.index'))
<a href="{{ route('document-archive.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('document-archive.*')) is-active @endif"
title="{{ __('Documents') }}">
<i class="fa-solid fa-box-archive wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Documents') }}</span>
</a>
@endif
@if (Route::has('reporting.index'))
<a href="{{ route('reporting.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('reporting.*')) is-active @endif"
title="{{ __('Reporting') }}">
<i class="fa-solid fa-file-lines wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Reporting') }}</span>
</a>
@endif
</div>
</div>
@if (auth()->user()?->isMpbaPlatformAdmin())
<div class="wms-sidebar-group" data-wms-menu-group="platform-administration">
<button type="button" class="wms-sidebar-heading wms-sidebar-heading-button"
data-wms-menu-toggle data-wms-sidebar-label aria-expanded="false">
<span class="wms-sidebar-heading-content">
<i class="fa-solid fa-building-shield wms-sidebar-icon" aria-hidden="true"></i>
<span>{{ __('Platform Administration') }}</span>
</span>
</button>
<div class="wms-sidebar-group-items" data-wms-menu-items>
@if (Route::has('platform.companies.index'))
<a href="{{ route('platform.companies.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('platform.companies.*')) is-active @endif"
title="{{ __('Companies') }}">
<i class="fa-solid fa-building wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Companies') }}</span>
</a>
@endif
@if (Route::has('platform.licenses.index'))
<a href="{{ route('platform.licenses.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('platform.licenses.*')) is-active @endif"
title="{{ __('Licences') }}">
<i class="fa-solid fa-key wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Licences') }}</span>
</a>
@endif
@if (Route::has('platform.payments.index'))
<a href="{{ route('platform.payments.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('platform.payments.*')) is-active @endif"
title="{{ __('Payments') }}">
<i class="fa-solid fa-credit-card wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Payments') }}</span>
</a>
@endif
@if (Route::has('countries.index'))
<a href="{{ route('countries.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('countries.*')) is-active @endif"
title="{{ __('Countries') }}">
<i class="fa-solid fa-globe wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Countries') }}</span>
</a>
@endif
@if (Route::has('warehouses.index'))
<a href="{{ route('warehouses.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('warehouses.*')) is-active @endif"
title="{{ __('Warehouses') }}">
<i class="fa-solid fa-warehouse wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Warehouses') }}</span>
</a>
@endif
@if (Route::has('warehouse-rules.index'))
<a href="{{ route('warehouse-rules.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('warehouse-rules.*')) is-active @endif"
title="{{ __('Validation Rules') }}">
<i class="fa-solid fa-shield-halved wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Validation Rules') }}</span>
</a>
@endif
@if (auth()->user()?->isMpbaPlatformAdmin() && Route::has('platform.prices.index'))
<a href="{{ route('platform.prices.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('platform.prices.*')) is-active @endif"
title="{{ __('Prices') }}">
<i class="fa-solid fa-tags wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Prices') }}</span>
</a>
@endif
@if (Route::has('configuration.index'))
<a href="{{ route('configuration.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('configuration.*')) is-active @endif"
title="{{ __('Configuration') }}">
<i class="fa-solid fa-sliders wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Configuration') }}</span>
</a>
@endif
@if (Route::has('security.index'))
<a href="{{ route('security.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('security.*')) is-active @endif"
title="{{ __('Security') }}">
<i class="fa-solid fa-user-shield wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Security') }}</span>
</a>
@endif
@if (Route::has('audit.index'))
<a href="{{ route('audit.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('audit.*')) is-active @endif"
title="{{ __('Audit') }}">
<i class="fa-solid fa-clipboard-check wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Audit') }}</span>
</a>
@endif
@if (Route::has('modules.control.index'))
<a href="{{ route('modules.control.index') }}" wire:navigate
class="wms-sidebar-link @if (request()->routeIs('modules.control.*')) is-active @endif"
title="{{ __('Module Control') }}">
<i class="fa-solid fa-puzzle-piece wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('Module Control') }}</span>
</a>
@endif
</div>
</div>
@endif
</nav>
<button type="button" class="wms-sidebar-resizer" data-wms-sidebar-resizer
aria-label="{{ __('Resize sidebar') }}" title="{{ __('Drag to resize sidebar') }}"></button>
</aside>
<div class="wms-main-panel">
@include('layouts.partials.topbar')
<main class="wms-content">
{{ $slot }}
</main>
</div>
</div>
<div id="wms-toast-stack"
class="fixed right-4 top-4 z-[9999] flex w-[min(24rem,calc(100vw-2rem))] flex-col gap-3 font-sans"
aria-live="polite" aria-atomic="true"></div>
<script>
window.wmsToast = function(options) {
const stack = document.getElementById('wms-toast-stack');
if (!stack) {
return;
}
const config = {
type: options.type || 'info',
title: options.title || '',
message: options.message || '',
timeout: Number(options.timeout || 4500),
};
const palette = {
success: 'border-emerald-400/40 bg-emerald-950/95 text-emerald-50',
error: 'border-red-400/40 bg-red-950/95 text-red-50',
warning: 'border-amber-400/40 bg-amber-950/95 text-amber-50',
info: 'border-blue-400/40 bg-blue-950/95 text-blue-50',
};
const icon = {
success: 'fa-circle-check',
error: 'fa-circle-xmark',
warning: 'fa-triangle-exclamation',
info: 'fa-circle-info',
};
const toast = document.createElement('div');
toast.className = [
'wms-toast pointer-events-auto select-none rounded-2xl border px-4 py-3 font-sans shadow-2xl backdrop-blur',
'transition duration-200 ease-out translate-y-0 opacity-100',
palette[config.type] || palette.info,
].join(' ');
toast.innerHTML = `
<div class="flex items-start gap-3">
<i class="fa-solid ${icon[config.type] || icon.info} mt-0.5 text-lg" aria-hidden="true"></i>
<div class="min-w-0 flex-1">
${config.title ? `<div class="text-sm font-bold">${config.title}</div>` : ''}
<div class="text-sm leading-5">${config.message}</div>
</div>
<button type="button" class="ml-2 text-white/70 hover:text-white" aria-label="Close notification">
<i class="fa-solid fa-xmark" aria-hidden="true"></i>
</button>
</div>
`;
const close = function() {
toast.classList.add('translate-y-[-0.35rem]', 'opacity-0');
window.setTimeout(function() {
toast.remove();
}, 200);
};
toast.querySelector('button').addEventListener('click', close);
stack.appendChild(toast);
if (config.timeout > 0) {
window.setTimeout(close, config.timeout);
}
};
window.addEventListener('wms:toast', function(event) {
window.wmsToast(event.detail || {});
});
function showSessionToasts() {
@if (session('success'))
window.wmsToast({
type: 'success',
title: @json(__('Success')),
message: @json(session('success')),
});
@endif
@if (session('error'))
window.wmsToast({
type: 'error',
title: @json(__('Error')),
message: @json(session('error')),
});
@endif
@if (session('warning'))
window.wmsToast({
type: 'warning',
title: @json(__('Warning')),
message: @json(session('warning')),
});
@endif
@if (session('info'))
window.wmsToast({
type: 'info',
title: @json(__('Information')),
message: @json(session('info')),
});
@endif
}
window.__wmsToastSessionKeys = window.__wmsToastSessionKeys || new Set();
window.showSessionToastsOnce = function () {
const key = [
@json(session('success')),
@json(session('error')),
@json(session('warning')),
@json(session('info')),
].filter(Boolean).join('|');
if (!key || window.__wmsToastSessionKeys.has(key)) {
return;
}
window.__wmsToastSessionKeys.add(key);
window.showSessionToasts();
};
document.addEventListener('DOMContentLoaded', showSessionToastsOnce);
document.addEventListener('livewire:navigated', showSessionToastsOnce);
</script>
@livewireScripts
@fluxScripts
<script>
(function() {
const SIDEBAR_STATE_KEY = 'wms-sidebar-state';
const SIDEBAR_WIDTH_KEY = 'wms-sidebar-width';
const PLATFORM_FIRST_OPEN_KEY = 'wms-platform-first-opened';
const DEFAULT_WIDTH = 288;
const MIN_WIDTH = 224;
const MAX_WIDTH = 448;
function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
}
function getSavedSidebarWidth() {
const savedWidth = Number.parseInt(localStorage.getItem(SIDEBAR_WIDTH_KEY) || DEFAULT_WIDTH, 10);
return clamp(Number.isFinite(savedWidth) ? savedWidth : DEFAULT_WIDTH, MIN_WIDTH, MAX_WIDTH);
}
function applySidebarWidth() {
const nextWidth = `${getSavedSidebarWidth()}px`;
document.documentElement.style.setProperty('--wms-sidebar-width', nextWidth);
}
function applySidebarState() {
const root = document.documentElement;
const saved = localStorage.getItem(SIDEBAR_STATE_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');
button.setAttribute('aria-label', expanded ? '{{ __('Collapse sidebar') }}' :
'{{ __('Expand sidebar') }}');
});
}
function getMenuItems(group) {
return Array.from(group.children).find((child) => child.matches('[data-wms-menu-items]')) || null;
}
function getMenuToggle(group) {
return Array.from(group.children).find((child) => child.matches('[data-wms-menu-toggle]')) || null;
}
function groupHasActiveLink(group) {
const items = getMenuItems(group);
const activeHeading = Array.from(group.children).some((child) => {
return child.matches('.wms-sidebar-heading-button.is-active');
});
const routeSelectedGroup = group.dataset.wmsSelected === 'true' && group.dataset.wmsHasActive ===
'true';
return routeSelectedGroup || activeHeading || (items ? items.querySelector(
'.wms-sidebar-link.is-active') !== null : false);
}
function setMenuGroupOpen(group, isOpen) {
group.dataset.wmsMenuOpen = isOpen ? 'true' : 'false';
const toggle = getMenuToggle(group);
if (toggle) {
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
}
}
function setMenuGroupSelected(group, isSelected) {
group.dataset.wmsSelected = isSelected ? 'true' : 'false';
group.dataset.wmsHasActive = isSelected ? 'true' : 'false';
}
function resetAllMenuGroups() {
document.querySelectorAll('[data-wms-menu-group]').forEach((group) => {
setMenuGroupOpen(group, false);
setMenuGroupSelected(group, false);
group.dataset.wmsManualActive = 'false';
});
}
function selectMenuGroup(group, openGroup = true) {
resetAllMenuGroups();
setMenuGroupSelected(group, true);
setMenuGroupOpen(group, openGroup);
}
function applyMenuState() {
const groups = Array.from(document.querySelectorAll('[data-wms-menu-group]'));
const selectedByRoute = groups.find((group) => groupHasActiveLink(group)) || null;
const selectedByManualClick = groups.find((group) => group.dataset.wmsManualActive === 'true') || null;
const selectedGroup = selectedByRoute || selectedByManualClick;
const shouldOpenPlatformForFirstVisit = !selectedGroup && localStorage.getItem(
PLATFORM_FIRST_OPEN_KEY) !== '1';
resetAllMenuGroups();
if (selectedGroup) {
setMenuGroupSelected(selectedGroup, true);
setMenuGroupOpen(selectedGroup, true);
} else if (shouldOpenPlatformForFirstVisit) {
const platformGroup = groups.find((group) => group.dataset.wmsMenuGroup === 'platform');
if (platformGroup) {
setMenuGroupOpen(platformGroup, true);
}
localStorage.setItem(PLATFORM_FIRST_OPEN_KEY, '1');
}
}
function bindMenuControls() {
document.querySelectorAll('[data-wms-menu-toggle]').forEach((button) => {
if (button.dataset.wmsBound === '1') {
return;
}
button.dataset.wmsBound = '1';
button.addEventListener('click', function() {
const group = button.closest('[data-wms-menu-group]');
if (!group) {
return;
}
const nextOpen = group.dataset.wmsMenuOpen !== 'true';
resetAllMenuGroups();
if (nextOpen) {
group.dataset.wmsManualActive = 'true';
setMenuGroupSelected(group, true);
setMenuGroupOpen(group, true);
}
});
});
}
function bindSidebarResize() {
document.querySelectorAll('[data-wms-sidebar-resizer]').forEach((handle) => {
if (handle.dataset.wmsBound === '1') {
return;
}
handle.dataset.wmsBound = '1';
let startX = 0;
let startWidth = DEFAULT_WIDTH;
let isResizing = false;
function getClientX(event) {
if (event.touches && event.touches.length > 0) {
return event.touches[0].clientX;
}
return event.clientX;
}
function startResize(event) {
if (document.documentElement.dataset.wmsSidebar === 'collapsed') {
return;
}
isResizing = true;
startX = getClientX(event);
const currentWidth = Number.parseInt(
getComputedStyle(document.documentElement).getPropertyValue(
'--wms-sidebar-width'),
10
);
startWidth = Number.isFinite(currentWidth) ? currentWidth : DEFAULT_WIDTH;
document.body.classList.add('wms-sidebar-is-resizing');
event.preventDefault();
}
function moveResize(event) {
if (!isResizing) {
return;
}
const delta = getClientX(event) - startX;
const nextWidth = clamp(startWidth + delta, MIN_WIDTH, MAX_WIDTH);
document.documentElement.style.setProperty('--wms-sidebar-width', `${nextWidth}px`);
localStorage.setItem(SIDEBAR_WIDTH_KEY, String(nextWidth));
event.preventDefault();
}
function stopResize() {
if (!isResizing) {
return;
}
isResizing = false;
document.body.classList.remove('wms-sidebar-is-resizing');
}
handle.addEventListener('mousedown', startResize);
handle.addEventListener('touchstart', startResize, {
passive: false
});
window.addEventListener('mousemove', moveResize);
window.addEventListener('touchmove', moveResize, {
passive: false
});
window.addEventListener('mouseup', stopResize);
window.addEventListener('touchend', stopResize);
window.addEventListener('touchcancel', stopResize);
});
}
function setMobileToggleState(isOpen) {
document.querySelectorAll('[data-wms-mobile-open]').forEach((button) => {
button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
});
document.body.classList.toggle('wms-mobile-nav-open', isOpen);
}
function closeMobileSidebar() {
document.documentElement.dataset.wmsMobileSidebar = 'closed';
setMobileToggleState(false);
}
function openMobileSidebar() {
document.documentElement.dataset.wmsMobileSidebar = 'open';
setMobileToggleState(true);
}
function toggleMobileSidebar() {
if (document.documentElement.dataset.wmsMobileSidebar === 'open') {
closeMobileSidebar();
return;
}
openMobileSidebar();
}
function bindSidebarControls() {
applySidebarWidth();
applySidebarState();
applyMenuState();
bindMenuControls();
bindSidebarResize();
document.querySelectorAll('[data-wms-sidebar-toggle]').forEach((button) => {
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(SIDEBAR_STATE_KEY, next);
applySidebarState();
});
});
document.querySelectorAll('[data-wms-brand-toggle]').forEach((button) => {
if (button.dataset.wmsBound === '1') {
return;
}
button.dataset.wmsBound = '1';
button.addEventListener('click', function(event) {
const root = document.documentElement;
if (root.dataset.wmsSidebar !== 'collapsed') {
return;
}
event.preventDefault();
event.stopPropagation();
root.dataset.wmsSidebar = 'expanded';
localStorage.setItem(SIDEBAR_STATE_KEY, 'expanded');
applySidebarState();
applyMenuState();
});
});
closeMobileSidebar();
document.querySelectorAll('[data-wms-mobile-open]').forEach((button) => {
if (button.dataset.wmsBound === '1') {
return;
}
button.dataset.wmsBound = '1';
button.addEventListener('click', function(event) {
event.preventDefault();
toggleMobileSidebar();
});
});
document.querySelectorAll('[data-wms-mobile-close]').forEach((button) => {
if (button.dataset.wmsBound === '1') {
return;
}
button.dataset.wmsBound = '1';
button.addEventListener('click', closeMobileSidebar);
});
document.querySelectorAll(
'.wms-sidebar-link[wire\\:navigate], .wms-sidebar-heading-button[wire\\:navigate]').forEach((
link) => {
if (link.dataset.wmsMobileBound === '1') {
return;
}
link.dataset.wmsMobileBound = '1';
link.addEventListener('click', function() {
document.querySelectorAll(
'.wms-sidebar-link.is-active, .wms-sidebar-heading-button.is-active')
.forEach((activeLink) => {
activeLink.classList.remove('is-active');
});
link.classList.add('is-active');
const selectedGroup = link.closest('[data-wms-menu-group]');
if (selectedGroup) {
selectMenuGroup(selectedGroup, true);
}
applySidebarWidth();
closeMobileSidebar();
});
});
}
applySidebarWidth();
applySidebarState();
document.addEventListener('DOMContentLoaded', bindSidebarControls);
document.addEventListener('livewire:navigated', bindSidebarControls);
})();
</script>
</body>
</html>