@extends('layouts.dashboard') @section('content') @php /** * Vendor advertising — this restaurant's campaigns, and how they are actually * performing. Impressions and clicks are read from the ad event log, so the * click-through rate is measured rather than estimated (CampaignController::index()). * * @var array $campaigns the current pager window of campaign rows * @var array $pager paginate() metadata * @var array $placements AdCampaign::placements * @var array $prices [placement => monthly price] * @var array $rangeLinks ['options' => [days => label], 'selected' => string] * @var int $rangeDays the selected window, in days * @var bool $showMoney false for a manager: ad spend stays with the owner * @var int $impressions @var int $clicks * @var float|null $ctr click-through rate, or null when nothing has been served * @var float $spend lifetime paid ad spend * @var int $liveCount campaigns currently running * @var array $perCampaign campaign_id => ['impressions' => int, 'clicks' => int] * @var array $deliveryChart @var array $deliveryMeta impressions + clicks per day * @var array $placementChart @var array $placementMeta impressions by placement * @var array $occupancyChart @var array $occupancyMeta booked periods per placement * @var bool $canPayByGateway this install can take a placement's money at all */ use App\Models\AdCampaign; /** Status pill colour map. */ $statusPill = [ 'pending' => 'qm-pill-amber', 'approved' => 'qm-pill-blue', 'active' => 'qm-pill-green', 'paused' => 'qm-pill-grey', 'rejected' => 'qm-pill-red', 'expired' => 'qm-pill-grey', ]; $showMoney = $showMoney ?? true; $perCampaign = $perCampaign ?? []; $canPayByGateway = (bool) ($canPayByGateway ?? false); @endphp @php /* .qm-d2: the dashboard density standard (app.css §35) */ @endphp
{{ t_raw('vendor.campaigns.subtitle') }}
| {{ t_raw('vendor.campaigns.col_headline') }} | {{ t_raw('common.period') }} | {{ t_raw('common.price') }} | {{ t_raw('common.status') }} | {{ t_raw('admin.campaigns.col_payment') }} | {{ t_raw('common.actions') }} |
|---|---|---|---|---|---|
|
{!! media_thumb($c['image'] ?? '', 'assets/client/covers/shop-1.png', 44, 'qm-tbl-thumb') !!}
{{ (string) $c['headline'] }}
{{ AdCampaign::placementLabel((string) $c['placement']) }}
@if ($target !== ''){{ $target }}@endif
|
@if (!empty($c['starts_at'])) {!! fmt_day($c['starts_at']) !!} – {!! fmt_day($c['ends_at'] ?? null) !!} @else {{ t_raw('admin.campaigns.months_suffix', [':n' => $months]) }} @endif | {{ money($c['price']) }} | @php /* Reach used to sit here. It belongs to a placement that is RUNNING, and a running placement is now on the Active placements card beside the occupancy chart — where its impressions, clicks and rate are shown together. This list is the work queue: nothing in it has been served yet, so every row read a dash. */ @endphp
{{ status_label('campaign', $status) }}
@if (!empty($c['admin_note']))
{{ $c['admin_note'] }}
@endif
|
@php /* ONCE IT IS PAID, THIS IS NOT THE PLACE. A settled placement's billing lives in the payment record under My Account, with its invoice; saying "paid" again on the operational list only repeats it. While it is UNPAID this cell still matters — it is where the owner learns the placement is waiting for them, and where they pay. */ @endphp @if ($pay !== 'paid') @php /* The state and the thing you do about it, on one line. They were stacked only because the column was narrow; dropping reach gave it the width, and "Unpaid" reads as the label on its own button. */ @endphp {{ status_label('payment', $pay) }} @else {!! dash() !!} @endif @php /* Unpaid is the moment the placement is bought — it is created that way and nothing else has to happen first. The resume button lives here rather than behind another screen, because this cell is where the owner learns it is waiting for them. 'approved' is still accepted for rows created while payment came second. ONE button, not one per gateway. This cell used to offer "Pay with PayPal" beside "Pay by card", which asked the owner to commit to a provider from a table row — before the checkout page had shown them the amount or the tax. Which gateway is now asked at checkout, next to the figure it applies to, so this only has to say what is owed. */ @endphp @if (in_array($status, ['pending', 'approved'], true) && $pay !== 'paid' && $canPayByGateway) @endif | @php /* The invoice for a placement, exactly as Billing offers one for a plan — same icon, same wording, same row-actions partial. It appears only once a number has been issued, which happens when the money is captured; a placement that was never paid for has nothing to invoice. */ @endphp @partial('row-actions', ['actions' => [ [ 'label' => t_raw('common.view'), 'icon' => 'fa-eye', 'href' => '/vendor/campaigns/' . (int) $c['id'], 'primary' => true, ], (int) ($c['invoice_no'] ?? 0) > 0 ? [ 'label' => t_raw('vendor.billing.view_invoice'), 'icon' => 'fa-file-invoice', 'href' => '/vendor/campaigns/invoice/' . (int) $c['id'], 'primary' => true, ] : null, $status === 'pending' ? [ 'label' => t_raw('common.cancel'), 'icon' => 'fa-ban', 'danger' => true, 'form' => [ 'action' => '/vendor/campaigns/cancel', 'fields' => ['id' => (int) $c['id']], 'confirm' => t_raw('vendor.campaigns.confirm_cancel'), ], ] : null, ]]) |
{{ t_raw('vendor.campaigns.queue_clear_text') }}
{{ t_raw('vendor.campaigns.empty_cta') }} @else{{ t_raw('vendor.campaigns.empty_text') }}
{{ t_raw('vendor.campaigns.empty_cta') }} @endif{{ t_raw('vendor.campaigns.history_moved') }}