@extends('admin.layout', ['menuKey' => 'analytics']) @section('title', 'Analytics') @php use App\Support\Charts; $range = $d['range']; $pct = fn ($a, $b) => $b > 0 ? number_format($a / $b * 100, 1).'%' : '–'; $rowsTable = fn (string $h, array $rows, string $n = 'Count') => ['head' => [$h, $n], 'rows' => array_map(fn ($r) => [$r['label'], number_format($r['n'])], $rows)]; @endphp @section('content')
Overview

Analytics

The whole site, counted on our own server: people (not bots, not admins), where they came from, what they looked for, and what it earned. Updated every 10 minutes. Google Analytics still runs alongside for anyone who wants its reports.

Site health
@include('charts.range', ['range' => $range, 'csv' => request()->fullUrlWithQuery(['format' => 'csv'])]) @include('charts.kpis', ['tiles' => $d['kpis']])
@include('dash._trend', ['series' => $d['series'], 'range' => $range, 'title' => 'Over time'])

Visitors

Where visits came from

@include('charts.bars', ['rows' => $d['sources']]) @include('dash._table', ['table' => $rowsTable('Source', $d['sources'], 'Page views')])

Top referring sites

@include('charts.bars', ['rows' => $d['referrers'], 'empty' => 'No other sites have sent visitors in this period.'])

Where visitors are

Page views by state; countries and cities beside it.

@include('charts.usmap', ['values' => $d['states'], 'noun' => 'page views'])

Countries

@include('charts.bars', ['rows' => $d['countries']])

Cities

@include('charts.bars', ['rows' => $d['cities'], 'empty' => 'No cities yet (needs the MaxMind database).'])
@include('dash._table', ['table' => ['head' => ['State', 'Page views'], 'rows' => collect($d['states'])->sortDesc()->map(fn ($n, $s) => [Charts::stateName($s), number_format($n)])->values()->all()]])

Time of day

Page views by hour, US Eastern

@include('charts.columns', ['values' => $d['hours'], 'noun' => 'page views']) @include('dash._table', ['table' => ['head' => ['Hour', 'Page views'], 'rows' => array_map(fn ($k, $v) => [Charts::hour($k), number_format($v)], array_keys($d['hours']), $d['hours'])]])

Devices and browsers

@include('charts.bars', ['rows' => $d['devices']])

Browsers

@include('charts.bars', ['rows' => $d['browsers']])

Most viewed pages

@include('charts.bars', ['rows' => $d['pages']])

Jobs and search

From search to reply

All posts together

@php $top = $d['funnel'][0][1] ?: 1; @endphp
@foreach ($d['funnel'] as $i => [$label, $n])
{{ $label }}{{ number_format($n) }}@if ($i === 1){{ $pct($n, $d['funnel'][0][1]) }} of those seen@endif
@endforeach

What people search for

{{ number_format($d['searchCount']) }} searches by words or place @if ($d['probes'] > 0)· {{ number_format($d['probes']) }} {{ $d['probes'] === 1 ? 'attack test' : 'attack tests' }} left out@endif

@include('charts.bars', ['rows' => $d['searches'], 'empty' => 'No searches by words yet.'])

Places searched

@include('charts.bars', ['rows' => $d['searchPlaces'], 'empty' => 'No place searches yet.'])

Searches that found nothing

Job titles or places people want that the board doesn't have yet.

@include('charts.bars', ['rows' => $d['noResults'], 'empty' => 'Every search found something.'])

Posting

New posts, by how they were made and paid for

@include('charts.bars', ['rows' => $d['posting']['source'], 'empty' => 'No new posts in this period.'])

Paid with

@include('charts.bars', ['rows' => $d['posting']['paid'], 'empty' => 'No new posts in this period.'])

Posters held back by a limit

{{ number_format($d['posting']['limitTotal']) }} times in this period

@include('charts.bars', ['rows' => $d['posting']['limits'], 'empty' => 'Nobody hit a limit.'])

By email domain

@include('charts.bars', ['rows' => $d['posting']['limitWho'], 'empty' => 'Nobody hit a limit.'])

Job packs

Sold and used

@include('charts.kpis', ['tiles' => [ ['label' => 'Packs sold', 'value' => number_format($d['packs']['sold']), 'sub' => number_format($d['packs']['postsSold']).' posts'], ['label' => 'Pack revenue', 'value' => '$'.number_format($d['packs']['revenue'] / 100, 2), 'sub' => 'after refunds, before tax'], ['label' => 'Pack posts used', 'value' => number_format($d['packs']['used']), 'sub' => number_format($d['packs']['returned']).' given back'], ['label' => 'Given free', 'value' => number_format($d['packs']['postsGiven']), 'sub' => 'posts, by admins'], ]])

Packs sold, by size

@include('charts.bars', ['rows' => $d['packs']['bySize'], 'empty' => 'No packs sold in this period.'])

Posts still to use

{{ number_format($d['packs']['outstanding']) }} unused · {{ number_format($d['packs']['expiring']) }} expire within 30 days

@include('charts.bars', ['rows' => $d['packs']['top'], 'empty' => 'No unused pack posts.'])

Replies to candidates (No Ghosting)

Are candidates answered?

Conversations started in this period

@include('charts.kpis', ['tiles' => [ ['label' => 'Answered', 'value' => $d['replies']['rate'] === null ? '–' : $d['replies']['rate'].'%', 'sub' => 'of '.number_format($d['replies']['conversations']).' conversations'], ['label' => 'Usual reply', 'value' => $d['replies']['median'] ? $d['replies']['median'].' bd' : '–', 'sub' => 'business days (median)'], ['label' => 'Waiting now', 'value' => number_format($d['replies']['waiting']), 'sub' => 'past '.\App\Services\Orgs\ReplyStats::PENDING_DAYS.' business days'], ['label' => 'Reminders', 'value' => number_format($d['replies']['nudges']), 'sub' => number_format($d['replies']['closedNotices']).' "job closed" notes to candidates'], ]])

No Ghosting badge: {{ $d['replies']['lost'] }} verified {{ $d['replies']['lost'] === 1 ? 'organization has' : 'organizations have' }} lost it, {{ $d['replies']['warned'] }} warned.

Candidates waiting, by employer

Waiting longer than {{ \App\Services\Orgs\ReplyStats::PENDING_DAYS }} business days

@include('charts.bars', ['rows' => $d['replies']['waitingBy'], 'empty' => 'Nobody is waiting. Every candidate has an answer.'])

Filters people use on the job board

@include('charts.bars', ['rows' => $d['filters'], 'empty' => 'No filters used in this period.'])

Blog shares

Shares by where

{{ number_format($d['shares']['total']) }} in this period

@include('charts.bars', ['rows' => $d['shares']['by'], 'empty' => 'No articles shared in this period.'])

Most shared articles

@include('charts.bars', ['rows' => $d['shares']['top'], 'empty' => 'No articles shared in this period.'])

Bulk uploads

CSV files

{{ number_format($d['bulk']['uploads']) }} uploads from {{ number_format($d['bulk']['orgs']) }} {{ $d['bulk']['orgs'] === 1 ? 'organization' : 'organizations' }}

@include('charts.kpis', ['tiles' => [ ['label' => 'Rows uploaded', 'value' => number_format($d['bulk']['rows']), 'sub' => number_format($d['bulk']['valid']).' passed the checks'], ['label' => 'Jobs posted', 'value' => number_format($d['bulk']['posted']), 'sub' => number_format($d['bulk']['live']).' went live at once'], ['label' => 'Not posted', 'value' => number_format($d['bulk']['notPosted']), 'sub' => 'refused when publishing'], ['label' => 'Not finished', 'value' => number_format($d['bulk']['abandoned']), 'sub' => 'uploads never posted'], ]])

Uploads by outcome

@include('charts.bars', ['rows' => $d['bulk']['status'], 'empty' => 'No bulk uploads in this period.'])

Most common CSV problems

Rows with a problem, by column

@include('charts.bars', ['rows' => $d['bulk']['fields'], 'empty' => 'No problems found in uploaded rows.'])

Sponsored placements

Totals

@include('charts.kpis', ['tiles' => [ ['label' => 'Views', 'value' => Charts::compact($d['sponsor']['views'])], ['label' => 'Clicks', 'value' => Charts::compact($d['sponsor']['clicks']), 'sub' => $pct($d['sponsor']['clicks'], $d['sponsor']['views']).' click rate'], ['label' => 'Spend', 'value' => Charts::money($d['sponsor']['spend'])], ]])

By placement

@include('charts.bars', ['rows' => array_map(fn ($r) => $r + ['note' => number_format($r['clicks']).' clicks · '.$pct($r['clicks'], $r['n'])], $d['sponsor']['placements'])])

Top campaigns

By spend, then views

@include('charts.bars', ['rows' => array_map(fn ($r) => $r + ['note' => number_format($r['clicks']).' clicks · '.($r['house'] ? 'house' : Charts::money($r['spend']))], $d['sponsor']['top']), 'empty' => 'No sponsored views in this period.'])

Email and speed

Email

@include('charts.kpis', ['tiles' => [ ['label' => 'Job alert emails', 'value' => Charts::compact($d['email']['alerts']), 'sub' => 'jobs sent to candidates'], ['label' => 'Bounces', 'value' => number_format($d['email']['bounces']), 'delta' => Charts::delta($d['email']['bounces'], $d['email']['prevBounces'], 'down', $range->previous()->label)], ['label' => 'Spam complaints', 'value' => number_format($d['email']['complaints'])], ]])

Page speed

Server time to build a page (last 7 days at most)

@include('charts.kpis', ['tiles' => [ ['label' => 'Typical', 'value' => $d['ms']['p50'] === null ? '–' : $d['ms']['p50'].' ms'], ['label' => '95% of pages under', 'value' => $d['ms']['p95'] === null ? '–' : $d['ms']['p95'].' ms'], ]])

Slowest page types (average ms)

@include('charts.bars', ['rows' => $d['ms']['slow'], 'empty' => 'Not enough pages yet.'])
@if ($d['since'])

Counting started {{ $d['since'] }}. Revenue, posts, replies and sponsored figures come from their own records and go back further.

@endif @endsection