@extends('layouts.app') @section('content')
@php $formatIsk = fn($amount) => number_format((float) $amount, 0, ',', '.') . ' ISK'; $latestMonthTotal = $billing['month_groups'][0]['month_total'] ?? 0; $averageMonthTotal = count($billing['month_groups']) > 0 ? (int) round($billing['grand_total'] / count($billing['month_groups'])) : 0; $activeFilterCount = 0; $monthsTracked = $stats->count(); $latestUniqueLocks = (int) ($stats->last()['count'] ?? 0); $totalNewLocksInRange = (int) $stats->sum('new_locks'); $totalNewPropertiesInRange = (int) $stats->sum('new_properties'); $netGrowthRate = $latestUniqueLocks > 0 && $totalNewLocksInRange > 0 ? round(($totalNewLocksInRange / max($latestUniqueLocks, 1)) * 100, 1) : 0; $billableMonths = count($billing['month_groups']); $propertyGrowthRows = $stats ->flatMap(fn($row) => collect($row['properties'] ?? [])->map(fn($property) => [ 'month' => $row['month'], 'name' => $property['name'], 'locks' => $property['locks'], ])) ->values(); $propertyCoverageRate = $monthsTracked > 0 ? round(($propertyGrowthRows->count() / max($monthsTracked, 1)), 1) : 0; if (!empty($filters['from_month'])) $activeFilterCount++; if (!empty($filters['to_month'])) $activeFilterCount++; if (!empty($filters['property_id'])) $activeFilterCount++; if (!empty($filters['property_name'])) $activeFilterCount++; if (!empty($filters['only_billable'])) $activeFilterCount++; if (count($selectedApis) !== count($apis)) $activeFilterCount++; @endphp @if(session('status')) @endif @if($errors->has('dkplus')) @endif
Analytics Superadmin

Active Locks by Month

Track lock growth, property onboarding, and monthly billing performance with API-level visibility.

Grand total billed

{{ $formatIsk($billing['grand_total']) }}

Latest month total

{{ $formatIsk($latestMonthTotal) }}

Average monthly total

{{ $formatIsk($averageMonthTotal) }}

Active filter groups

{{ $activeFilterCount }}

Latest unique locks

{{ $latestUniqueLocks }}

New locks in selected range

{{ $totalNewLocksInRange }}

New properties in selected range

{{ $totalNewPropertiesInRange }}

Months tracked

{{ $monthsTracked }}
Growth Health

How fast the selected period expanded.

{{ $netGrowthRate }}%

Net lock velocity

Based on new locks in range vs. latest unique lock base.

Property Activity

Average additions per tracked month.

{{ $propertyCoverageRate }}

Properties / month

Shows monthly onboarding consistency in the current filters.

Billing Signal

Snapshot of active invoice periods.

{{ $billableMonths }}

Billable months
Grand: {{ $formatIsk($billing['grand_total']) }} Average: {{ $formatIsk($averageMonthTotal) }}

Use this card to quickly verify billing continuity across the selected range.

Filters

Refine monthly lock and billing data by time, property, and API source.

Reset filters
@foreach($apis as $api)
@endforeach
@if(!empty($filters['from_month'])) From: {{ $filters['from_month'] }} @endif @if(!empty($filters['to_month'])) To: {{ $filters['to_month'] }} @endif @if(!empty($filters['property_id'])) Property ID: {{ $filters['property_id'] }} @endif @if(!empty($filters['property_name'])) Property name contains: {{ $filters['property_name'] }} @endif @if(!empty($filters['only_billable'])) Billable > 0 only @endif @if(count($selectedApis) !== count($apis)) APIs: {{ implode(', ', $selectedApis) }} @endif @if($activeFilterCount === 0) No active filters. Showing all data. @endif
Monthly Lock Growth

Overview of unique locks and property additions per month.

@forelse($stats as $row) @empty @endforelse
Month Unique Locks New Locks New Properties
{{ $row['month'] }} {{ $row['count'] }} {{ $row['new_locks'] }} {{ $row['new_properties'] }}
No rows for selected filters.
Monthly Property Growth

Property additions shown as dedicated rows per month for better visibility.

@forelse($propertyGrowthRows as $propertyRow) @empty @endforelse
Month Property Added Locks Added
{{ $propertyRow['month'] }} {{ strip_tags((string) $propertyRow['name']) }} {{ $propertyRow['locks'] }}
No new properties in the selected range.
Monthly Billing (Cumulative)

Expand a month to review property-level charge details and API subtotals.

@forelse($billing['month_groups'] as $monthGroup) @php $collapseId = 'billing_month_' . str_replace('-', '_', $monthGroup['month']); $headingId = $collapseId . '_heading'; @endphp
Month Total: {{ $formatIsk($monthGroup['month_total']) }}
@foreach($monthGroup['api_groups'] as $apiGroup) @foreach($apiGroup['rows'] as $row) @php $property = $properties->first(fn($candidate) => (string) $candidate->PropertyID === (string) $row['property_id']); $propertyEmail = $propertyEmailMap[(string) $row['property_id']] ?? null; $invoiceRecord = $invoiceMap[(string) $row['property_id'] . '|' . $row['month']] ?? null; @endphp @endforeach @endforeach
Month Property API/Source dkPlus customer Total assigned locks Pricing rule Unit price Subtotal Calculation details Invoice
{{ $row['month'] }} {{ $row['property_name'] }} {{ $row['api'] }}
@csrf
{{ $row['billable_unique_lock_count'] }} {{ $row['pricing_rule_label'] }} {{ $row['pricing_badge'] }} {{ $formatIsk($row['unit_price']) }} {{ $formatIsk($row['subtotal']) }}
View formula
Formula: {{ $row['calculation_formula'] }}
Input quantity: {{ $row['input_quantity'] }} {{ $row['input_quantity_label'] }}
Subtotal: {{ $formatIsk($row['subtotal']) }}
@if($invoiceRecord?->dkplus_invoice_id) Created: {{ $invoiceRecord->dkplus_invoice_id }} @else Not created @endif @if($invoiceRecord?->status) {{ strtoupper($invoiceRecord->status) }} @endif
@csrf
@if($invoiceRecord?->dkplus_invoice_id)
@csrf
@endif @if(!empty($invoiceRecord?->pdf_url)) Open invoice @endif
API subtotal: {{ $apiGroup['api'] }} {{ $formatIsk($apiGroup['api_total']) }}
Month grand total {{ $formatIsk($monthGroup['month_total']) }}
@empty

No billing data for the selected API filters.

@endforelse
@endsection @push('css') @endpush @push('js') @endpush