@extends('admin.layouts.app') @section('title', __('admin.investors_admin.list_title') . ' — ' . __('admin.brand.name')) @section('page_title', __('admin.investors_admin.list_title')) @php $dash = '—'; @endphp @push('styles') @endpush @section('content') @include('admin.partials.flash') {{-- ── Group chips (single horizontal scrollable row) ───────────── --}} @if (! empty($groupChips)) @php $preserve = collect(request()->query())->except(['group_id', 'page'])->all(); $chipUrl = fn ($groupId = null) => $groupId === null ? url()->current() . (count($preserve) ? '?' . http_build_query($preserve) : '') : url()->current() . '?' . http_build_query(array_merge($preserve, ['group_id' => $groupId])); // Group chip colour map — only used by the top chip strip. $groupColors = \App\Models\Group::query() ->whereIn('id', collect($groupChips)->pluck('id')->all()) ->get(['id','name','color']) ->mapWithKeys(fn ($g) => [$g->id => $g->resolveColor()]) ->all(); // Favorites chip URL — toggles `?favorites=1`. Preserves the // other filter state via the same $preserve baseline. $favsActive = request()->boolean('favorites'); $favsBase = collect(request()->query())->except(['favorites', 'page'])->all(); $favsChipUrl = $favsActive ? url()->current() . (count($favsBase) ? '?' . http_build_query($favsBase) : '') : url()->current() . '?' . http_build_query(array_merge($favsBase, ['favorites' => 1])); @endphp
{{ __('admin.investors_admin.all_groups') }} {{ $investors->total() }} @foreach ($groupChips as $g) @php $gc = $groupColors[$g['id']] ?? '#6366f1'; @endphp {{ $g['name'] }} {{ $g['count'] }} @endforeach {{-- Favorites chip — sits in the same strip as the groups, on the far right. Clicking toggles ?favorites=1 → server-side filters via Investor::scopeFavorites equivalent in the controller. --}} {{ __('admin.favorites.chip') }}
@endif {{-- ── Meeting-status chip strip ── Quick filter for the four meeting states defined in the controller. Driven by `?meeting_status=...`. Reuses the same .gchip styling as the group strip above so the page feels visually coherent. Counts are intentionally omitted here — the four states partition the investor set so the totals would clutter without adding value. --}} @php $msPreserve = collect(request()->query())->except(['meeting_status', 'page'])->all(); $msChipUrl = fn (?string $key) => $key === null ? url()->current() . (count($msPreserve) ? '?' . http_build_query($msPreserve) : '') : url()->current() . '?' . http_build_query(array_merge($msPreserve, ['meeting_status' => $key])); // Same palette as the column badge — chips inherit only the // border/dot accent so they don't shout. Active state fills it. $msPalette = [ 'booked' => '#10b981', 'never_booked' => '#64748b', ]; @endphp
{{ __('admin.investors_admin.meeting_status_filter_label') }}: {{ __('admin.investors_admin.meeting_statuses.all') }} @foreach ($meetingStatusOptions as $msKey) @php $msc = $msPalette[$msKey] ?? '#6366f1'; @endphp {{ __('admin.investors_admin.meeting_statuses.' . $msKey) }} @endforeach
{{-- ── Quick date-range chip strip ── Applies an inclusive [from,to] filter on `created_at`. Mirrors the Meetings index chip pattern; rolling windows per the ops spec (last_30 = today − 29 days through today, not the previous calendar month). --}} @php $dpPreserve = collect(request()->query())->except(['date_preset', 'page'])->all(); $dpChipUrl = fn (?string $key) => $key === null ? url()->current() . (count($dpPreserve) ? '?' . http_build_query($dpPreserve) : '') : url()->current() . '?' . http_build_query(array_merge($dpPreserve, ['date_preset' => $key])); @endphp
{{ __('admin.investors_admin.date_presets.label') }}: {{ __('admin.investors_admin.date_presets.all_time') }} @foreach ($datePresets as $dp) {{ __('admin.investors_admin.date_presets.' . $dp) }} @endforeach
@push('styles') @endpush {{-- ── Toolbar: Filters (left, flexible) + Columns (right) ──────── --}}
{{-- Rows-per-page selector. Number-only (no label) and ordered AFTER the Columns button per UX spec. Submits a tiny GET form that preserves every other query param so applying it doesn't reset filters or the current group chip selection. --}}
@foreach (request()->except(['per_page', 'page']) as $k => $v) @if (is_array($v)) @foreach ($v as $vv) @endforeach @else @endif @endforeach
@if ($investors->isEmpty()) @else
@foreach ($columnCatalog as $key => $col) &dir= via the existing x-admin.sortable component. --}} @else {{ $col['label'] }} @endif @endforeach @foreach ($investors as $investor) @php // Delegate to the model's locale-aware // `display_name` accessor — it resolves // through full_name_ → composed // first+last → email → "#id" without // touching the legacy `name` column. // (Phase 2 of the 2026-06-09 refactor.) $displayName = $investor->display_name ?: $dash; $currentActionId = (int) ($investor->action_option_id ?? 0); $currentActionName = $currentActionId && isset($actionOptions[$currentActionId]) ? $actionOptions[$currentActionId] : ''; @endphp @foreach ($columnCatalog as $key => $col) @endforeach @endforeach
@if (isset($sortableColumns[$key])) {{-- Click-to-sort header — drives ?sort=
{{ __('admin.fields.actions') }}
{{ $displayName }} {!! ($col['value'])($investor) !!}
@if (! empty($actionOptions)) @endif
{{ $investors->links() }}
@endif
{{-- Rejection confirmation modal (shared with the investor show page). --}} @include('admin.partials.rejection-modal') @endsection @push('scripts') @endpush