{{ __('Filters narrow the selection: a profile must match every active filter.') }}
{{-- Location --}}{{ __('Includes profiles that have never logged in. Banks have no login and are left out.') }}
@endif{{ __('System entries such as migrations and rounding corrections do not count as activity.') }}
@endif{{ __('Only include profiles that have transacted with the selected profile(s).') }}
{{ __('Statistics are calculated over the profiles matching your selection.') }}
{{ $this->sectionScope() }}
{{ __('Internal:') }} {{ $this->formatAmount($stats['economic']['internal_volume']) }} · {{ __('External:') }} {{ $this->formatAmount($stats['economic']['external_volume']) }}
{{ __('Internal transactions happen between two profiles inside the selection; external ones have one side outside it.') }}
| {{ __('Transaction type') }} | {{ __('Count') }} | {{ __('Volume') }} |
|---|---|---|
| {{ $type['type_name'] }} | {{ number_format($type['count'], 0, ',', '.') }} | {{ $this->formatAmount($type['volume']) }} |
{{ __('No data available for this chart.') }}
@endif @endif{{ $this->sectionScope() }}
| {{ $this->isPartnerScoped() ? __('Partner profile type') : __('Profile type') }} | @foreach ($columnLabels as $key => $label){{ $label }} | @endforeach|||
|---|---|---|---|---|
| {{ __($typeName) }} | {{ number_format($row['total'], 0, ',', '.') }} | {{ number_format($row['active'], 0, ',', '.') }} | {{ number_format($row['inactive'], 0, ',', '.') }} | {{ $row['recent_logins'] === null ? '-' : number_format($row['recent_logins'], 0, ',', '.') }} |
{{ $chart['label'] }}: {{ __('No data available for this chart.') }}
@endif @endforeach{{ __('No profile in this selection registered during this period. The totals above cover the whole selection, whenever each profile signed up.') }}
@endif{{ $this->sectionScope() }}
{{ $metric['label'] }}: {{ __('No data available for this chart.') }}
@endif @endforeach{{ __('No data available for this chart.') }}
@endif{{ $this->sectionScope(false) }}
@if ($filterByContacts && count($selectedContactProfiles) > 0) {{ __('Current balance of the :count user and organization trading partners of the selected profile(s), in hours. Banks are excluded.', ['count' => $stats['balance_distribution']['profiles_counted']]) }} @else {{ __('Current balance of the :count user and organization profiles in the selection, in hours. Banks are excluded.', ['count' => $stats['balance_distribution']['profiles_counted']]) }} @endif
@php // The chart reads one key per series off each band row, so // fold the per-type counts into the bands here. $balanceSeries = $stats['balance_distribution']['series'] ?? []; $balanceRows = collect($stats['balance_distribution']['bands']) ->map(function ($band, $i) use ($balanceSeries) { foreach ($balanceSeries as $type => $counts) { $band[$type] = $counts[$i] ?? 0; } return $band; }) ->values(); @endphp {{-- One chart per profile type, each with its own y-axis. Organizations are far fewer than users, so on a shared axis scaled to hundreds of users a bar of 1 organization is sub-pixel and reads as missing data. --}} @php $balanceCharts = [ [ 'field' => 'User', 'label' => __('Users'), 'canvas' => 'statsBalanceChart', 'attr' => 'data-stats-balance-chart', ], [ 'field' => 'Organization', 'label' => __('Organizations'), 'canvas' => 'statsBalanceOrgChart', 'attr' => 'data-stats-balance-org-chart', ], ]; $balanceColors = $this->balanceSeriesColors(); @endphp {{-- Stacked, not side by side: at half width the 27 bands are cramped and the angled labels crowd. Full width gives each chart roughly twice the area. --}}{{ $chart['label'] }}: {{ __('No profiles of this type in the selection.') }}
@endif @endforeach{{ $this->sectionScope() }}
{{ __(':profiles excluded from this table.', ['profiles' => implode(', ', $stats['distribution']['excluded_profiles'])]) }}
@endif @if ($stats['distribution']['top_profiles']->isEmpty()){{ __('No activity in this selection.') }}
@else| {{ $this->profilesLabel(false) }} | {{ __('Type') }} | @php $sortable = [ ['key' => 'paid_count', 'label' => __('Paid'), 'hint' => __('Payments sent by this profile.')], ['key' => 'received_count', 'label' => __('Received'), 'hint' => __('Payments received by this profile.')], ['key' => 'paid_volume', 'label' => __('Paid volume'), 'hint' => __('Time this profile paid out.')], ['key' => 'received_volume', 'label' => __('Received volume'), 'hint' => __('Time this profile received.')], ['key' => 'partners', 'label' => __('Trading partners'), 'hint' => __('Distinct profiles this one traded with. Can be higher than the partner count in the network graph, which leaves out removed profiles.')], ['key' => 'count', 'label' => __('Transactions'), 'hint' => __('Payments sent and received together. This is what the rows are ranked by.')], ]; @endphp @foreach ($sortable as $column)@endforeach | |||||
|---|---|---|---|---|---|---|---|
| {{ $anonymizeProfiles ? $profile['anonymous_name'] : $profile['name'] }} | {{ __($profile['type']) }} | {{ number_format($profile['paid_count'], 0, ',', '.') }} | {{ number_format($profile['received_count'], 0, ',', '.') }} | {{ $this->formatAmount($profile['paid_volume']) }} | {{ $this->formatAmount($profile['received_volume']) }} | {{ number_format($profile['partners'] ?? 0, 0, ',', '.') }} | {{ number_format($profile['count'], 0, ',', '.') }} |
{{ $this->sectionScope() }}
{{ __(':profiles excluded from this table.', ['profiles' => implode(', ', $stats['distribution']['excluded_profiles'])]) }}
@endif| {{ __('Location') }} | @php $locationSortable = [ ['key' => 'paid_count', 'label' => __('Paid'), 'hint' => __('Payments sent by profiles based here.')], ['key' => 'received_count', 'label' => __('Received'), 'hint' => __('Payments received by profiles based here.')], ['key' => 'paid_volume', 'label' => __('Paid volume'), 'hint' => __('Time paid out by profiles based here.')], ['key' => 'received_volume', 'label' => __('Received volume'), 'hint' => __('Time received by profiles based here.')], ['key' => 'count', 'label' => __('Transactions'), 'hint' => __('Payments sent and received together. This is what the rows are ranked by.')], ]; @endphp @foreach ($locationSortable as $column)@endforeach | ||||
|---|---|---|---|---|---|
| {{ $row['label'] ?: __('Location not specified') }} | {{ number_format($row['paid_count'], 0, ',', '.') }} | {{ number_format($row['received_count'], 0, ',', '.') }} | {{ $this->formatAmount($row['paid_volume']) }} | {{ $this->formatAmount($row['received_volume']) }} | {{ number_format($row['count'], 0, ',', '.') }} |
{{ $this->sectionScope() }}
{{ $note }}
@endforeach{{ __('No profiles were marked as inactive in this selection.') }}
@endif{{ $this->sectionScope($networkPeriod === 'inherit') }}
@endif @if ($networkTimelapse && ($lapse = $this->networkTimelapseMonths())) {{-- The period dropdown is replaced during a time-lapse, so this names what the animation covers instead. --}}{{ __('Time-lapse: :from to :till', [ 'from' => $lapse[0]['label'], 'till' => $this->timelapseEndDate()?->translatedFormat('j M Y'), ]) }}
@elseif ($networkPeriod !== 'inherit'){{ __('Period for this graph:') }} {{ $this->networkPeriodLabel() }}
@endif@if (($ns['max_degree'] ?? null) !== null && $ns['max_degree'] < \App\Services\StatsService::DEGREE_CEILING) {{ __('Showing :shown of :total profiles with :min to :max trading partners.', [ 'shown' => number_format($ns['shown_nodes'], 0, ',', '.'), 'total' => number_format($ns['total_nodes'], 0, ',', '.'), 'min' => $ns['min_degree'], 'max' => $ns['max_degree'], ]) }} {{ __('The busiest profile has :count trading partners.', ['count' => $ns['highest_degree'] ?? 0]) }} @else {{ __('Showing :shown of :total profiles with at least :min trading partners.', [ 'shown' => number_format($ns['shown_nodes'], 0, ',', '.'), 'total' => number_format($ns['total_nodes'], 0, ',', '.'), 'min' => $ns['min_degree'], ]) }} @endif
{{-- Search field and period selector sit OUTSIDE the empty/non-empty guard: with the period selector inside the else branch, the very control needed to refill an empty graph would disappear. --}}{{ __('Time-lapse ends on this date.') }}
{{ __('Applies to this graph only.') }}
@endif@if ($ns['total_nodes'] > 0) {{ __('Too little mutual trading in this selection to draw a network. Lower the minimum number of trading partners.') }} @elseif ($filterByLastTransaction && $networkPeriod !== 'inherit') {{-- This combination contradicts itself: profiles with no transaction since X cannot, by definition, have traded within a period inside X. --}} {{ __('This graph period falls inside the inactivity filter, so these profiles have no transactions here by definition. Choose a longer graph period, or switch off "no transaction since".') }} @else {{ __('The profiles in this selection did not trade with each other, so there is no network to draw. Widen the filters or the period.') }} @endif
@else{{ \App\Http\Livewire\Admin\Stats::networkViewCaption($networkView) }}
{{-- Filled by the circulation classifier, which runs in the browser on the visible set - the server cannot know the counts. NOT hidden when empty, and given a fixed height: during a time-lapse this line changes on every frame, and letting it collapse resized the graph container underneath it frame by frame. It always has something to say - at minimum how much is drawn. --}} {{-- Below the subtext, in the warning colour: the graph is drawn but one view could not be computed. Reserves its line for the same reason. --}}
{{ $legend['view_label'] ?? '' }}: {{ $legend['view_hint'] }}
@endif{{ __('Hover over a dot to see its name and its trading partners.') }} {{ __('Click a dot to show only that profile and the ones it trades with.') }} {{ __('Drag a dot to spread the graph out; its trading partners move along unless you switch that off.') }} {{ __('Use the search box to find a profile in this graph.') }}