phpvms/resources/views/admin/menu.blade.php
Nabeel Shahzad 28f49b97c3
feat(schema-modernization): foundations for RouteForge
Schema, model, Filament, cron, and visibility-semantics foundations
required by the RouteForge change. Four phases delivered as one
coherent change. Verified against four spec files (flight-bundles,
flight-time-storage, subfleet-capability, flight-visibility).

Phase 1.1 \xe2\x80\x94 Flight time columns
- flights.departure_time, flights.arrival_time TIME NULL
- Flight model accessors (Hi-format) + mutators (FlightTimeParser)
- FlightTimeParser supports 9 formats + Z/L/tz suffix stripping
- Migration backfills inline via chunked raw DB::table->update()
- Legacy dpt_time/arr_time VARCHAR columns preserved for one release

Phase 1.2 \xe2\x80\x94 Subfleet capability columns
- subfleets.cruise_speed, max_range_nm, route_types (CSV VARCHAR(64))
- FlightTypesCast splits/sorts/dedupes/logs-on-invalid
- routeforge defaults in config/phpvms.php
- SubfleetForm Operational Capability section

Phase 1.3 \xe2\x80\x94 Flight Bundles
- flight_bundles table + flights.bundle_id NOT NULL FK
- Default bundle seeded inline; existing flights backfilled
- FlightBundle model + factory + BundleObserver (queued recompute)
- FlightBundleResource as sole Flights nav entry (slug=flights,
  icon=OutlinedMap, sort=2)
- Nested FlightResource under FlightBundleResource (slug=flight,
  parentResource, create+edit only, no nav entry)
- FlightsRelationManager with relatedResource for full-page row actions
- FlightForm: bundle selector removed (route-bound), date inputs
  hidden when parent bundle owns dates, XSS-safe placeholder link
- Date columns stored as TIMESTAMP UTC, UI converts to local tz
- Soft-deleting a bundle leaves children non-deleted; visibility
  recompute sets visible=false (no cascade delete)
- Spatie Shield permissions: flight_bundle.* seeded

Phase 1.4 \xe2\x80\x94 Visibility semantics
- flights.active renamed to flights.enabled (admin source of truth)
- flights.visible is cron-managed combined state
- SetActiveFlights cron deleted; SetVisibleFlights cron added
- Two-pass bulk SQL UPDATE scoped by bundle, chunks of 500
- RecomputeBundleVisibility queued job dispatched on bundle save/restore
- Flight::scopeVisible added; scopeActive retained as plain alias
- Pilot-facing read sites use ->visible() (AirportController fix)
- FlightResource JSON + FlightExporter CSV expose both enabled+active
- Four-state status badge (Disabled / Disabled by Bundle / Enabled
  Out of Window / Enabled & In Window) replaces Visible toggle
- 5 visibility indexes added

Verification
- 469 Pest tests passed (2058 assertions)
- phpstan level 5 clean (646 files)
- pint --test clean
- rector --dry-run clean
- All four spec files PASS (28 requirements + 36 scenarios verified
  by kimi-spec-reviewer)

Migration path documented in docs/UPGRADING.md and openspec/changes/
schema-modernization-for-routeforge/.
2026-05-23 12:05:09 -05:00

133 lines
4.7 KiB
PHP

<li>
<a href="{{ url('/admin') }}"><i class="pe-7s-display1"></i>dashboard</a>
</li>
<li>
<a data-toggle="collapse" href="#operations_menu" class="menu operations_menu" aria-expanded="true">
<h5>operations&nbsp;<b class="pe-7s-angle-right"></b></h5>
</a>
<div class="collapse" id="operations_menu" aria-expanded="true">
<ul class="nav">
@can('view-any:pirep')
<li><a href="{{ \App\Filament\Resources\Pireps\PirepResource::getUrl() }}"><i class="pe-7s-cloud-upload"></i>pireps
<span data-toggle="tooltip" title="3 New" class="badge bg-light-blue pull-right">3</span>
</a>
</li>
@endcan
@can('view-any:flight')
<li><a href="{{ \App\Filament\Resources\FlightBundles\FlightBundleResource::getUrl() }}"><i class="pe-7s-vector"></i>flights</a>
</li>
@endcan
@can('view-any:subfleet')
<li><a href="{{ \App\Filament\Resources\Subfleets\SubfleetResource::getUrl() }}"><i class="pe-7s-plane"></i>fleet</a>
</li>
@endcan
@can('view-any:fare')
<li><a href="{{ \App\Filament\Resources\Fares\FareResource::getUrl() }}"><i class="pe-7s-graph2"></i>fares</a></li>
@endcan
@can('view:finances')
<li><a href="{{ \App\Filament\Pages\Finances::getUrl() }}"><i class="pe-7s-display1"></i>finances</a></li>
@endcan
@can('view-any:user')
<li><a href="{{ \App\Filament\Resources\Users\UserResource::getUrl() }}"><i class="pe-7s-users"></i>users</a>
</li>
@endcan
</ul>
</div>
</li>
<li>
<a data-toggle="collapse" href="#config_menu" class="menu config_menu" aria-expanded="true">
<h5>config&nbsp;<b class="pe-7s-angle-right"></b></h5>
</a>
<div class="collapse" id="config_menu" aria-expanded="true">
<ul class="nav">
@can('view-any:airline')
<li><a href="{{ \App\Filament\Resources\Airlines\AirlineResource::getUrl() }}"><i class="pe-7s-paper-plane"></i>airlines</a>
</li>
@endcan
@can('view-any:sim-brief-airframe')
<li><a href="{{ \App\Filament\Resources\SimBriefAirframes\SimBriefAirframeResource::getUrl() }}"><i class="pe-7s-plane"></i>sb airframes</a></li>
@endcan
@can('view-any:airport')
<li><a href="{{ \App\Filament\Resources\Airports\AirportResource::getUrl() }}"><i
class="pe-7s-map-marker"></i>airports</a></li>
@endcan
@can('view-any:expense')
<li><a href="{{ \App\Filament\Resources\Expenses\ExpenseResource::getUrl() }}"><i class="pe-7s-cash"></i>expenses</a>
</li>
@endcan
@can('view-any:rank')
<li><a href="{{ \App\Filament\Resources\Ranks\RankResource::getUrl() }}"><i class="pe-7s-graph1"></i>ranks</a>
</li>
@endcan
@can('view-any:typerating')
<li><a href="{{ \App\Filament\Resources\Typeratings\TyperatingResource::getUrl() }}"><i class="pe-7s-plane"></i>type
ratings</a>
</li>
@endcan
@can('view-any:award')
<li><a href="{!! \App\Filament\Resources\Awards\AwardResource::getUrl() !!}"><i class="pe-7s-diamond"></i>awards</a>
</li>
@endcan
@can('view-any:role')
<li><a href="{{ \BezhanSalleh\FilamentShield\Resources\Roles\RoleResource::getUrl() }}"><i
class="pe-7s-network"></i>roles</a></li>
@endcan
@can('view-any:page')
<li><a href="{{ \App\Filament\Resources\Pages\PageResource::getUrl() }}"><i class="pe-7s-note"></i>pages/links</a>
</li>
@endcan
@can('view:modules')
<li><a href="{!! \App\Filament\Resources\Modules\ModuleResource::getUrl() !!}"><i class="pe-7s-box2"></i>addons/modules</a>
</li>
@endcan
@can('view:maintenance')
<li><a href="{{ \App\Filament\Pages\Maintenance::getUrl() }}"><i class="pe-7s-tools"></i>maintenance</a></li>
@endcan
@can('view-any:activity')
<li><a href="{{ \App\Filament\Resources\ActivityLogs\ActivityLogResource::getUrl() }}"><i class="pe-7s-news-paper"></i>activities</a></li>
@endcan
@can('view:settings')
<li><a href="{{ \App\Filament\Pages\Settings::getUrl() }}"><i class="pe-7s-config"></i>settings</a></li>
@endcan
</ul>
</div>
</li>
<li>
<a data-toggle="collapse" href="#addons_menu" class="menu addons_menu" aria-expanded="true">
<h5>addons&nbsp;<b class="pe-7s-angle-right"></b></h5>
</a>
<div class="collapse" id="addons_menu" aria-expanded="true">
<ul class="nav">
@can('view:modules')
@foreach($moduleSvc->getAdminLinks() as &$link)
<li><a href="{{ url($link['url']) }}"><i class="{{ $link['icon'] }}"></i>{{ $link['title'] }}</a></li>
@endforeach
@endcan
</ul>
</div>
</li>