@if($accountsData && $accountsData->count() > 0)
{{ __('Show in decimals') }}
@endif
@if($this->fromDate && $this->toDate && $accountsData && $accountsData->count() > 0) {{ __('PDF') }} {{ __('Loading...') }} @else {{ __('PDF') }} @endif
@if($accountsData && $accountsData->count() > 0)

{{ __('Account Balance') }}

{{ __('Balance overview of your accounts') }}

@foreach($accountsData as $account) @endforeach
{{ __('Account Name') }} {{ __('Start Balance') }} {{ __('End Balance') }} {{ __('Difference') }}
{{ $account['name'] }}
{{ $account['start_balance_formatted'] }} {{ $account['end_balance_formatted'] }} {{ $account['difference_formatted'] }}@if($account['difference'] > 0) +@endif
{{ __('TOTALS') }}
{{ $this->formatAmount($accountsData->sum('start_balance')) }} {{ $this->formatAmount($accountsData->sum('end_balance')) }} @php $totalDifference = $accountsData->sum('difference'); @endphp {{ $this->formatAmount($totalDifference) }}@if($totalDifference > 0) +@endif
@if($transactionTypesData && $transactionTypesData->count() > 0)

{{ __('Transacion types') }}

{{ __('Overview of the different transaction purposes') }}

@foreach($transactionTypesData as $transactionType) @endforeach
{{ __('Transaction Type') }} {{ __('Credit') }} {{ __('Debit') }} {{ __('Total') }}
{{ $transactionType['type_name'] }}
{{ $transactionType['incoming_formatted'] }} {{ $transactionType['outgoing_formatted'] }} {{ $transactionType['net_formatted'] }}
@endif @if($accountBalancesTimelineData && count($accountBalancesTimelineData) > 4)

{{ __('Account Balance Over Time') }}

{{ __('Track your account balance over time') }}

{{-- 16:9, like every chart on the statistics page. The sized wrapper is what Chart.js measures. --}}
@endif @if($balanceDistribution && ($balanceDistribution['withheld'] ?? false)) {{-- Too few trading partners: with only a handful of profiles per band, a reader who knows who this profile deals with could read an individual's balance off the chart. --}}

{{ __('Balance distribution of your trading partners') }}

{{ __('Not shown: you have :count trading partners in this period, and at least :minimum are needed to keep individual balances private.', [ 'count' => $balanceDistribution['profiles_counted'], 'minimum' => $balanceDistribution['minimum'], ]) }}

@elseif($balanceDistribution) @php $bdRows = collect($balanceDistribution['bands']) ->map(function ($band, $i) use ($balanceDistribution) { foreach ($balanceDistribution['series'] as $type => $counts) { $band[$type] = $counts[$i] ?? 0; } return $band; }) ->values(); @endphp

{{ __('Balance distribution of your trading partners') }}

{{ __('How the balances of your :count trading partners are spread, in hours.', ['count' => $balanceDistribution['profiles_counted']]) }}

{{-- One chart per profile type, each with its own y-axis, as on the platform statistics page. Organizations are far fewer than users, so on a shared axis scaled to hundreds of users a bar of one organization is sub-pixel and reads as missing data. --}} @php $bdCharts = [ [ 'field' => 'User', 'label' => __('Users'), 'canvas' => 'reportBalanceDistributionChart', 'attr' => 'data-report-balance-distribution', ], [ 'field' => 'Organization', 'label' => __('Organizations'), 'canvas' => 'reportBalanceDistributionOrgChart', 'attr' => 'data-report-balance-distribution-org', ], ]; @endphp {{-- Stacked so each chart gets the full width; at half width the banded axis is too cramped to read. --}}
@foreach ($bdCharts as $chart) @php $bdTotal = $bdRows->sum(fn($row) => $row[$chart['field']] ?? 0); // Trim each axis to its own series. The bands are // computed across both types together, so a band // holding only an organization leaves a run of empty // bars on the user chart — and vice versa. Keep one // empty band past the last occupied one, as the // platform statistics page does. $bdSeriesRows = $bdRows->values(); $bdLast = $bdSeriesRows->count() - 1; while ( $bdLast > 0 && ($bdSeriesRows[$bdLast][$chart['field']] ?? 0) === 0 && ($bdSeriesRows[$bdLast - 1][$chart['field']] ?? 0) === 0 ) { $bdLast--; } // Same at the leading end: a chart whose first bar // sits in band five should not open with four empty // ones. $bdFirst = 0; while ( $bdFirst < $bdLast && ($bdSeriesRows[$bdFirst][$chart['field']] ?? 0) === 0 && ($bdSeriesRows[$bdFirst + 1][$chart['field']] ?? 0) === 0 ) { $bdFirst++; } $bdSeriesRows = $bdSeriesRows->slice($bdFirst, $bdLast - $bdFirst + 1)->values(); @endphp @if ($bdTotal > 0)

{{ $chart['label'] }}

@else {{-- No bordered box: an empty frame reads as a chart that failed to draw. --}}

{{ $chart['label'] }}: {{ __('No profiles of this type in the selection.') }}

@endif @endforeach
@endif @if($isOrganization && $returnRatioTimelineData && count($returnRatioTimelineData) > 4)
{{ __('Loading chart...') }}

{{ __('Reciprocity Rate Timeline') }}

{{ __('Reciprocity rate: the share of your Hours that were also returned by the same people you helped during this period.') }}

{{ __('A high rate means you are part of a more closed exchange network with frequent returning exchange partners, while a low rate suggests you are part of a more open network with many different exchange partners.') }}

{{-- 16:9, like every chart on the statistics page. The sized wrapper is what Chart.js measures. --}}
@endif

{{ __('Transaction Relation Statistics') }}

{{ __('Which types of profiles have you transacted with?') }}

@if($statisticsData['profiles_by_type'] && $statisticsData['profiles_by_type']->count() > 0) @foreach($statisticsData['profiles_by_type'] as $modelType => $count) @endforeach @endif @if($isOrganization) {{-- Withheld below the partner threshold: with only a few partners the median is arithmetically the mean and reads as a finding when it is not. --}} @if(!is_null($statisticsData['median_return_ratio'] ?? null)) @endif @endif
{{ __('Period Statistics') }} {{ __('Value') }}
{{ __('Number of Transactions') }}
{{ number_format($statisticsData['transaction_count']) }}
{{ __('Unique ' . class_basename($modelType) . 's') }}
{{ number_format($count) }}
{{ __('Total unique profiles') }}
{{ number_format($statisticsData['unique_profiles']) }}
{{ __('Average reciprocity rate') }}
{{ number_format($statisticsData['average_return_ratio'], 1) }}%
{{ __('Median reciprocity rate') }}
{{ number_format($statisticsData['median_return_ratio'], 1) }}%
@else
{{ __('Please select a period to generate the account balance report') }}
@endif {{-- Trading network. Banks only: it shows who trades with whom among the bank's own clients. Scoping happens server-side in networkScope(). --}} @if ($this->showsNetwork()) @php $network = $this->networkData(); @endphp

{{ __('Trading network') }}

{{ __('Trade between your own clients.') }} {{ __('Period for this graph:') }} {{ $this->networkPeriodLabel() }}

{{ $showNetwork ? __('Hide network') : __('Show network') }} {{-- Shown only while the request is in flight: the graph pulls in ~570 kB of lazily loaded JavaScript plus its own aggregation, so the wait needs acknowledging rather than looking like nothing happened. --}} {{ __('Loading... this may take a while.') }}
@if ($showNetwork && $network) @php $ns = $network['stats']; $ceiling = \App\Services\StatsService::DEGREE_CEILING; $networkPeriods = [ 'inherit' => __('Same period as the report'), 'since_one_year_ago' => __('Since one year ago'), 'previous_year' => __('Previous year'), 'previous_quarter' => __('Previous quarter'), 'previous_month' => __('Previous month'), 'all_time' => __('All time'), ]; $typeOptions = [ 'User' => __('Users'), 'Organization' => __('Organizations'), 'Bank' => __('Banks'), ]; $layouts = [ 'force' => __('Force-directed'), 'circle' => __('Circle'), 'concentric' => __('Rings by connections'), 'tree' => __('Tree'), 'grid' => __('Grid'), ]; // From the component, so the report's buttons cannot drift // from the admin stats page. $views = \App\Http\Livewire\Admin\Stats::networkViewOptions(); @endphp
{{ __('Trading partners') }}
{{ $networkMinDegree }}
{{ $networkMaxDegree >= $ceiling ? '>' . $ceiling : $networkMaxDegree }}
{{ __('Hide the largest hub') }}
{{ __('Show') }} @foreach ($typeOptions as $type => $label) @endforeach
{{-- No wire:model: purely browser behaviour. --}}
{{ __('Anonymise profile names') }}
{{-- Same controls as the admin stats page: these change which profiles the query resolves, so they go through Livewire and rebuild the graph. --}} @if ($networkView === 'circulation') @php $stoppedCeiling = \App\Http\Livewire\Admin\Stats::STOPPED_AFTER_CEILING; @endphp
{{ __('Define \'stopped\' at') }} @if ($networkStoppedAfterMonths >= $stoppedCeiling) >{{ $stoppedCeiling }} {{ __('months') }} @else {{ $networkStoppedAfterMonths }} {{ __('months') }} @endif
@endif
{{ __('Incl. network outside current bank') }}
{{ __('Arrangement') }} @foreach ($layouts as $key => $label) @endforeach
{{ __('View') }} @foreach ($views as $key => $label) @endforeach

{{ __('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'], ]) }}

{{-- Search and period stay outside the empty/non-empty guard, so an empty graph does not hide the controls that fix it. --}}
@if ($ns['shown_nodes'] >= 2)
@endif
{{-- Vector export of the graph alone. No wire:click: a Livewire round trip would rebuild the graph and discard the very view the user is asking to export. --}}
@if ($ns['shown_nodes'] < 2)

@if ($ns['total_nodes'] > 0) {{ __('Too little mutual trading in this selection to draw a network. Lower the minimum number of trading partners.') }} @else {{ __('Your clients did not trade with each other in this period. Choose a longer period, or include the network outside the current bank.') }} @endif

@else
{{-- Info text left, spread button right, on one row. The paragraphs are hidden until they have something to say, so the button keeps its place through min-h and items-start rather than their height. --}}
{{-- The question this view answers, above the runtime notes and sharing their left margin. --}}

{{ \App\Http\Livewire\Admin\Stats::networkViewCaption($networkView) }}

{{-- Filled by the circulation classifier in the browser; see the stats view. Reserves its line rather than collapsing: the same JS drives both views, and a line that disappears resizes the graph below it. --}}

{{-- Below the subtext, in the warning colour: the graph is drawn but one view could not be computed. --}}

{{-- wire:ignore keeps the canvas out of Livewire's morph, so the zoom, pan and any focused node survive a re-render (exporting a PDF goes through the server and would otherwise reset the view). The wrapper's data-* attributes above are still updated, so changing a setting rebuilds the graph as normal. --}} {{-- See the stats view: the placeholder lives inside the host, so Cytoscape removes it on mount. --}}
{{ __('Drawing the network...') }}
@php $legend = $this->networkLegend($network); @endphp
{{ __('Legend') }}
{{-- What the current view encodes. Volume and flow differ in both dot size and line meaning, which the graph alone does not reveal. --}} @if (!empty($legend['view_hint']))

{{ $legend['view_label'] ?? '' }}: {{ $legend['view_hint'] }}

@endif
{{ __('Colour') }}: {{ $legend['color_meaning'] }}
@if ($networkView === 'cluster') @endif @if (!empty($legend['colors']))
@foreach ($legend['colors'] as $item) {{ $item['label'] }} @endforeach
@endif
@if (!empty($legend['styles']))
{{ __('Node style') }}
@foreach ($legend['styles'] as $style) {{ $style['label'] }} @endforeach
@endif
{{ __('Dot size') }}: {{ $legend['size_meaning'] }}
{{ __('small to large') }}
{{ __('Line thickness') }}: {{ $legend['width_meaning'] }}
{{ __('few to many') }}
@if (!empty($legend['directions']))
{{ __('Transaction type') }}
@foreach ($legend['directions'] as $dir) {{-- Dashed for bookkeeping edges, matching the graph. --}} @if (!empty($dir['dashed'])) @else @endif {{ $dir['label'] }} @endforeach
@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.') }}

@endif @endif
@endif