Repo rebuild

This commit is contained in:
Mark Gregory
2026-09-15 12:33:28 +01:00
commit be28c4feec
5629 changed files with 317074 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
@php
$helpdeskManifestPath = public_path('build/helpdesk/manifest.json');
@endphp
@if (request()->routeIs('helpdesk.*') && file_exists($helpdeskManifestPath))
@vite(['Resources/assets/js/app.js', 'Resources/assets/css/app.css'], 'build/helpdesk')
@endif
<x-layouts::app.sidebar :title="$title ?? null">
{{ $slot }}
</x-layouts::app.sidebar>
@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
@include('partials.head')
<body class="min-h-screen bg-white dark:bg-zinc-800">
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
aria-hidden="true"></div>
<flux:header container class="border-b border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
<flux:sidebar.toggle class="lg:hidden mr-2" icon="bars-2" inset="left" />
<x-app-logo href="{{ route('dashboard') }}" wire:navigate />
<flux:navbar class="-mb-px max-lg:hidden">
<flux:navbar.item icon="layout-grid" :href="route('dashboard')" :current="request()->routeIs('dashboard')"
wire:navigate>
{{ __('Dashboard') }}
</flux:navbar.item>
</flux:navbar>
<flux:spacer />
<flux:navbar class="me-1.5 space-x-0.5 rtl:space-x-reverse py-0!">
<flux:tooltip :content="__('Search')" position="bottom">
<flux:navbar.item class="!h-10 [&>div>svg]:size-5" icon="magnifying-glass" href="#"
:label="__('Search')" />
</flux:tooltip>
<flux:tooltip :content="__('Repository')" position="bottom">
<flux:navbar.item class="h-10 max-lg:hidden [&>div>svg]:size-5" icon="folder-git-2"
href="https://github.com/laravel/livewire-starter-kit" target="_blank" :label="__('Repository')" />
</flux:tooltip>
<flux:tooltip :content="__('Documentation')" position="bottom">
<flux:navbar.item class="h-10 max-lg:hidden [&>div>svg]:size-5" icon="book-open-text"
href="https://laravel.com/docs/starter-kits#livewire" target="_blank"
:label="__('Documentation')" />
</flux:tooltip>
</flux:navbar>
<x-desktop-user-menu />
</flux:header>
<!-- Mobile Menu -->
<flux:sidebar collapsible="mobile" sticky
class="lg:hidden border-e border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900">
<flux:sidebar.header>
<x-app-logo :sidebar="true" href="{{ route('dashboard') }}" wire:navigate />
<flux:sidebar.collapse
class="in-data-flux-sidebar-on-desktop:not-in-data-flux-sidebar-collapsed-desktop:-mr-2" />
</flux:sidebar.header>
<flux:sidebar.nav>
<flux:sidebar.group :heading="__('Platform')">
<flux:sidebar.item icon="layout-grid" :href="route('dashboard')"
:current="request()->routeIs('dashboard')" wire:navigate>
{{ __('Dashboard') }}
</flux:sidebar.item>
</flux:sidebar.group>
</flux:sidebar.nav>
<flux:spacer />
<flux:sidebar.nav>
<flux:sidebar.item icon="folder-git-2" href="https://github.com/laravel/livewire-starter-kit"
target="_blank">
{{ __('Repository') }}
</flux:sidebar.item>
<flux:sidebar.item icon="book-open-text" href="https://laravel.com/docs/starter-kits#livewire"
target="_blank">
{{ __('Documentation') }}
</flux:sidebar.item>
</flux:sidebar.nav>
</flux:sidebar>
{{ $slot }}
@persist('toast')
<flux:toast.group>
<flux:toast />
</flux:toast.group>
@endpersist
@fluxScripts
</body>
</html>
@@ -0,0 +1,913 @@
<!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">
<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-plug-circle-bolt wms-sidebar-icon" aria-hidden="true"></i>
<span data-wms-sidebar-label>{{ __('M3 Integration') }}</span>
</a>
@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>
+3
View File
@@ -0,0 +1,3 @@
<x-layouts::auth.simple :title="$title ?? null">
{{ $slot }}
</x-layouts::auth.simple>
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
@include('partials.head')
</head>
<body class="min-h-screen bg-neutral-100 antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
aria-hidden="true"></div>
<div class="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div class="flex w-full max-w-md flex-col gap-6">
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
<span class="flex h-9 w-9 items-center justify-center rounded-md">
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
</span>
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
</a>
<div class="flex flex-col gap-6">
<div
class="rounded-xl border bg-white dark:bg-stone-950 dark:border-stone-800 text-stone-800 shadow-xs">
<div class="px-10 py-8">{{ $slot }}</div>
</div>
</div>
</div>
</div>
@persist('toast')
<flux:toast.group>
<flux:toast />
</flux:toast.group>
@endpersist
@fluxScripts
</body>
</html>
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
@include('partials.head')
</head>
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
aria-hidden="true"></div>
<div class="bg-background flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div class="flex w-full max-w-sm flex-col gap-2">
<a href="{{ route('home') }}" class="flex flex-col items-center gap-2 font-medium" wire:navigate>
<span class="flex h-9 w-9 mb-1 items-center justify-center rounded-md">
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
</span>
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
</a>
<div class="flex flex-col gap-6">
{{ $slot }}
</div>
</div>
</div>
@persist('toast')
<flux:toast.group>
<flux:toast />
</flux:toast.group>
@endpersist
@fluxScripts
</body>
</html>
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="dark">
<head>
@include('partials.head')
</head>
<body class="min-h-screen bg-white antialiased dark:bg-linear-to-b dark:from-neutral-950 dark:to-neutral-900">
<div data-dusk="page-ready" style="position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);"
aria-hidden="true"></div>
<div
class="relative grid h-dvh flex-col items-center justify-center px-8 sm:px-0 lg:max-w-none lg:grid-cols-2 lg:px-0">
<div
class="bg-muted relative hidden h-full flex-col p-10 text-white lg:flex dark:border-e dark:border-neutral-800">
<div class="absolute inset-0 bg-neutral-900"></div>
<a href="{{ route('home') }}" class="relative z-20 flex items-center text-lg font-medium" wire:navigate>
<span class="flex h-10 w-10 items-center justify-center rounded-md">
<x-app-logo-icon class="me-2 h-7 fill-current text-white" />
</span>
{{ config('app.name', 'Laravel') }}
</a>
@php
[$message, $author] = str(Illuminate\Foundation\Inspiring::quotes()->random())->explode('-');
@endphp
<div class="relative z-20 mt-auto">
<blockquote class="space-y-2">
<flux:heading size="lg">&ldquo;{{ trim($message) }}&rdquo;</flux:heading>
<footer>
<flux:heading>{{ trim($author) }}</flux:heading>
</footer>
</blockquote>
</div>
</div>
<div class="w-full lg:p-8">
<div class="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-[350px]">
<a href="{{ route('home') }}" class="z-20 flex flex-col items-center gap-2 font-medium lg:hidden"
wire:navigate>
<span class="flex h-9 w-9 items-center justify-center rounded-md">
<x-app-logo-icon class="size-9 fill-current text-black dark:text-white" />
</span>
<span class="sr-only">{{ config('app.name', 'Laravel') }}</span>
</a>
{{ $slot }}
</div>
</div>
</div>
@persist('toast')
<flux:toast.group>
<flux:toast />
</flux:toast.group>
@endpersist
@fluxScripts
</body>
</html>
@@ -0,0 +1,105 @@
{{-- Shared Application Top Bar --}}
@php
$user = auth()->user();
$dashboardRoute =
Route::has('platform.dashboard') && method_exists($user, 'isMpbaPlatformAdmin') && $user?->isMpbaPlatformAdmin()
? 'platform.dashboard'
: (Route::has('dashboard')
? 'dashboard'
: null);
@endphp
<header class="sticky top-0 z-40 border-b border-white/10 bg-[#111118]/95 backdrop-blur" role="banner">
<div class="flex h-16 w-full items-center justify-between gap-3 px-3 sm:px-4 lg:px-6">
<div class="flex min-w-0 items-center gap-3 lg:hidden">
<button type="button"
class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-white/10 bg-white/5 text-white transition hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2 focus:ring-offset-[#111118] lg:hidden"
data-wms-mobile-open aria-label="{{ __('Open navigation menu') }}" aria-controls="wms-main-sidebar"
aria-expanded="false" data-test="mobile-menu-button">
<i class="fa-solid fa-bars" aria-hidden="true"></i>
</button>
<a href="{{ $dashboardRoute ? route($dashboardRoute) : '#' }}" wire:navigate
class="flex min-w-0 items-center gap-3" aria-label="{{ __('Warehouse Management System') }}">
<span
class="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-cyan-600 text-sm font-bold text-white shadow-lg shadow-cyan-950/40">
W
</span>
<span class="hidden min-w-0 sm:block">
<span class="block truncate text-sm font-semibold text-white sm:text-base">
{{ __('Warehouse Management System') }}
</span>
<span class="block text-xs font-semibold uppercase tracking-wide text-cyan-400">
MPBA
</span>
</span>
</a>
</div>
<div id="userControls" class="relative ml-auto flex shrink-0 items-center gap-3" x-data="{ open: false }"
x-on:keydown.escape.window="open = false">
<button type="button"
class="flex items-center gap-3 rounded-2xl border border-white/10 bg-white/5 px-2 py-2 text-left transition hover:bg-white/10 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2 focus:ring-offset-[#111118] sm:px-3"
x-on:click="open = ! open" x-bind:aria-expanded="open.toString()" aria-haspopup="menu"
data-test="top-user-menu-button">
<span class="hidden text-right md:block">
<span
class="block max-w-48 truncate text-sm font-semibold text-white">{{ $user?->name ?? __('User') }}</span>
<span class="block max-w-48 truncate text-xs text-white/50">{{ $user?->email ?? '' }}</span>
</span>
<span
class="flex h-10 w-10 items-center justify-center rounded-full bg-blue-700 text-xs font-bold text-white shadow-lg shadow-blue-950/40">
{{ $user?->initials() ?? 'U' }}
</span>
<i class="fa-solid fa-chevron-down hidden text-white/50 sm:block" aria-hidden="true"></i>
</button>
<div x-cloak x-show="open" x-transition.origin.top.right x-on:click.outside="open = false"
class="absolute right-0 top-full mt-2 w-[calc(100vw-1.5rem)] max-w-72 overflow-hidden rounded-2xl border border-white/10 bg-[#181820] shadow-2xl shadow-black/40 sm:w-72"
role="menu">
<div class="border-b border-white/10 px-4 py-3">
<div class="truncate text-sm font-semibold text-white">{{ $user?->name ?? __('User') }}</div>
<div class="truncate text-xs text-white/50">{{ $user?->email ?? '' }}</div>
</div>
<div class="p-2">
@if ($dashboardRoute)
<a href="{{ route($dashboardRoute) }}" wire:navigate
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
role="menuitem">
{{ __('Dashboard') }}
</a>
@endif
@if (Route::has('profile.edit'))
<a href="{{ route('profile.edit') }}" wire:navigate
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
role="menuitem">
{{ __('Profile') }}
</a>
@endif
@if (Route::has('settings.index'))
<a href="{{ route('settings.index') }}" wire:navigate
class="block rounded-xl px-3 py-2 text-sm text-white/80 hover:bg-white/10 hover:text-white"
role="menuitem">
{{ __('Settings') }}
</a>
@endif
<form method="POST" action="{{ route('logout') }}" class="mt-2 border-t border-white/10 pt-2">
@csrf
<button type="submit"
class="w-full rounded-xl px-3 py-2 text-left text-sm text-red-300 hover:bg-red-500/10 hover:text-red-200"
role="menuitem" data-test="logout-button">
{{ __('Sign out') }}
</button>
</form>
</div>
</div>
</div>
</div>
</header>