Files
autocrm/resources/views/filament/tenant/pages/reports.blade.php
T
Vasyka 8d82af2f54 Faza 3.5+3.6+4+5: Marketing, Reports, Provisioning, PWA
═══ Faza 3.5: Marketing ═══
Schema: msg_templates, marketing_channels, calls
Modele cu logică:
- MessageTemplate::render($context) — substituie {key} tokens
- MarketingChannel: roi/conversion_rate/cost_per_lead computed attrs
- Call: duration_formatted helper

Resources Filament (group Marketing):
- MessageTemplateResource: 5 canale (telegram/whatsapp/viber/sms/email)
- MarketingChannelResource: budget vs revenue cu ROI live calculat
- CallResource: in/out/missed cu filtre azi/missed

═══ Faza 3.6: Analytics ═══
Custom Filament Page Reports cu 6 rapoarte tab-uite:
- Finanțe: încasări/cheltuieli/profit/datorii + breakdown pe metodă/categorie
- Încărcare: fișe deschise/închise + breakdown pe status
- Mecanici: ore lucrate, manopere, venit per mecanic
- Manopere top: cele mai frecvente cu nr/ore/venit
- Piese: top vândute + low-stock
- Clienți: noi în perioadă + lead-uri pe sursă
Selector perioadă: azi / săptămâna / luna / luna trecută / anul

═══ Faza 4: Central provisioning ═══
- CoolifyClient service (Coolify v4 REST API wrapper)
- CompanyProvisioner: creează Company + admin user + roles + adaugă
  subdomeniul în Coolify FQDN + trigger redeploy automat
- CreateCompany page override → folosește provisioner, returnează
  notificare cu credentialele admin
- Form CompanyResource extins cu admin_name/email/password (vizibil doar create)
- Action 'Suspendă' / 'Activează' pe table cu confirmation

Env vars necesare în Coolify pentru provisioning auto:
  COOLIFY_API_URL=http://65.21.20.141:8000
  COOLIFY_API_TOKEN=<token>
  COOLIFY_APP_UUID=g13hlrpd5g44zxl5af3ktio2

═══ Faza 5: PWA + branding ═══
- Route /manifest.json dinamic per tenant (nume, theme color, icons)
- Route /sw.js — service worker minimal (cache shell + static)
- TenantPanelProvider renderHook HEAD_END — link manifest + theme-color
  + apple-mobile-web-app meta
- TenantPanelProvider renderHook BODY_END — registrare service worker

Seed extins:
- 5 template-uri mesaje (programare/auto-gata/reminder/ITP/felicitare)
- 5 canale marketing (Google Ads/FB/IG/Telegram/Recomandări)
- 2 apeluri demo

Total Filament tenant routes: 81.
2026-05-07 04:55:33 +00:00

225 lines
13 KiB
PHP

<x-filament-panels::page>
@php
$data = $this->data();
$tabs = $this->tabs();
$periods = $this->periods();
@endphp
<div class="space-y-6">
{{-- Period selector --}}
<div class="flex flex-wrap gap-2 items-center">
<span class="text-sm font-medium">Perioadă:</span>
@foreach ($periods as $key => $label)
<button
type="button"
wire:click="setPeriod('{{ $key }}')"
@class([
'px-3 py-1.5 rounded-md text-sm border',
'bg-primary-600 text-white border-primary-600' => $period === $key,
'bg-white border-gray-200 hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-700' => $period !== $key,
])
>{{ $label }}</button>
@endforeach
</div>
{{-- Tabs --}}
<div class="flex flex-wrap gap-2 border-b border-gray-200 dark:border-gray-700">
@foreach ($tabs as $key => $label)
<button
type="button"
wire:click="setTab('{{ $key }}')"
@class([
'px-4 py-2 text-sm font-medium -mb-px border-b-2',
'border-primary-600 text-primary-600' => $tab === $key,
'border-transparent text-gray-500 hover:text-gray-700' => $tab !== $key,
])
>{{ $label }}</button>
@endforeach
</div>
{{-- Content per tab --}}
@if ($tab === 'finance')
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
@foreach ([
['Încasări', $data['income'], 'success'],
['Cheltuieli', $data['expenses'], 'danger'],
['Profit', $data['profit'], $data['profit'] >= 0 ? 'success' : 'danger'],
['Datorii clienți', $data['debt'], 'warning'],
] as [$label, $value, $color])
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-xs text-gray-500">{{ $label }}</div>
<div class="text-2xl font-bold text-{{ $color }}-600 mt-1">
{{ number_format((float)$value, 2, '.', ' ') }} MDL
</div>
</div>
@endforeach
</div>
<div class="grid md:grid-cols-2 gap-4">
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<h3 class="font-semibold mb-3">Încasări pe metodă</h3>
<table class="w-full text-sm">
<thead><tr class="border-b"><th class="text-left py-2">Metodă</th><th class="text-right">Tranz.</th><th class="text-right">Total</th></tr></thead>
<tbody>
@forelse ($data['by_method'] as $row)
<tr class="border-b last:border-0">
<td class="py-2">{{ \App\Models\Tenant\Payment::METHODS[$row->method] ?? $row->method }}</td>
<td class="text-right">{{ $row->cnt }}</td>
<td class="text-right font-semibold">{{ number_format((float)$row->total, 2, '.', ' ') }}</td>
</tr>
@empty
<tr><td colspan="3" class="py-4 text-gray-400 text-center">Nicio plată în perioada selectată.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<h3 class="font-semibold mb-3">Cheltuieli pe categorie</h3>
<table class="w-full text-sm">
<thead><tr class="border-b"><th class="text-left py-2">Categorie</th><th class="text-right">Nr.</th><th class="text-right">Total</th></tr></thead>
<tbody>
@forelse ($data['by_category'] as $row)
<tr class="border-b last:border-0">
<td class="py-2">{{ \App\Models\Tenant\Expense::CATEGORIES[$row->category] ?? $row->category }}</td>
<td class="text-right">{{ $row->cnt }}</td>
<td class="text-right font-semibold text-red-600">{{ number_format((float)$row->total, 2, '.', ' ') }}</td>
</tr>
@empty
<tr><td colspan="3" class="py-4 text-gray-400 text-center">Nicio cheltuială.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="text-sm text-gray-500">Marjă profit: <b>{{ $data['margin_pct'] }}%</b></div>
@elseif ($tab === 'workload')
<div class="grid md:grid-cols-2 gap-4">
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-xs text-gray-500">Fișe deschise</div>
<div class="text-3xl font-bold">{{ $data['opened'] }}</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-xs text-gray-500">Fișe închise</div>
<div class="text-3xl font-bold text-success-600">{{ $data['closed'] }}</div>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<h3 class="font-semibold mb-3">Pe status</h3>
<table class="w-full text-sm">
<tbody>
@foreach ($data['by_status'] as $row)
<tr class="border-b last:border-0">
<td class="py-2">{{ \App\Models\Tenant\WorkOrder::STATUSES[$row->status] ?? $row->status }}</td>
<td class="text-right font-bold">{{ $row->cnt }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@elseif ($tab === 'masters')
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700 overflow-x-auto">
<table class="w-full text-sm">
<thead><tr class="border-b">
<th class="text-left py-2">Mecanic</th>
<th class="text-left">Specializare</th>
<th class="text-right">Manopere</th>
<th class="text-right">Ore</th>
<th class="text-right">Venit</th>
</tr></thead>
<tbody>
@forelse ($data['rows'] as $r)
<tr class="border-b last:border-0">
<td class="py-2 font-medium">{{ $r['name'] }}</td>
<td class="text-gray-500">{{ $r['specialization'] ?? '—' }}</td>
<td class="text-right">{{ $r['works'] }}</td>
<td class="text-right">{{ number_format($r['hours'], 1) }}</td>
<td class="text-right font-semibold text-success-600">{{ number_format($r['revenue'], 2, '.', ' ') }}</td>
</tr>
@empty
<tr><td colspan="5" class="py-4 text-gray-400 text-center">Niciun mecanic activ.</td></tr>
@endforelse
</tbody>
</table>
</div>
@elseif ($tab === 'works')
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700 overflow-x-auto">
<table class="w-full text-sm">
<thead><tr class="border-b">
<th class="text-left py-2">Manoperă</th>
<th class="text-right">Nr.</th>
<th class="text-right">Ore total</th>
<th class="text-right">Venit</th>
</tr></thead>
<tbody>
@forelse ($data['rows'] as $r)
<tr class="border-b last:border-0">
<td class="py-2">{{ $r->name }}</td>
<td class="text-right font-semibold">{{ $r->cnt }}</td>
<td class="text-right">{{ number_format((float)$r->hours, 1) }}</td>
<td class="text-right text-success-600">{{ number_format((float)$r->revenue, 2, '.', ' ') }}</td>
</tr>
@empty
<tr><td colspan="4" class="py-4 text-gray-400 text-center">Nicio manoperă efectuată.</td></tr>
@endforelse
</tbody>
</table>
</div>
@elseif ($tab === 'parts')
<div class="grid md:grid-cols-2 gap-4">
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700 overflow-x-auto">
<h3 class="font-semibold mb-3">Top piese vândute</h3>
<table class="w-full text-sm">
<thead><tr class="border-b"><th class="text-left py-2">Piesă</th><th class="text-right">Cant.</th><th class="text-right">Venit</th><th class="text-right">Marjă</th></tr></thead>
<tbody>
@forelse ($data['sold'] as $r)
<tr class="border-b last:border-0">
<td class="py-2">{{ $r->name }} <span class="text-gray-400">{{ $r->brand }}</span></td>
<td class="text-right">{{ number_format((float)$r->qty, 2) }}</td>
<td class="text-right">{{ number_format((float)$r->revenue, 2, '.', ' ') }}</td>
<td class="text-right text-success-600">{{ number_format((float)$r->margin, 2, '.', ' ') }}</td>
</tr>
@empty
<tr><td colspan="4" class="py-4 text-gray-400 text-center">Nicio piesă montată.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<h3 class="font-semibold mb-3">⚠️ Stoc minim atins</h3>
<table class="w-full text-sm">
<thead><tr class="border-b"><th class="text-left py-2">Piesă</th><th class="text-right">Stoc</th><th class="text-right">Min.</th></tr></thead>
<tbody>
@forelse ($data['low'] as $p)
<tr class="border-b last:border-0">
<td class="py-2">{{ $p->name }}</td>
<td class="text-right font-bold {{ $p->qty <= 0 ? 'text-red-600' : 'text-yellow-600' }}">{{ $p->qty }}</td>
<td class="text-right">{{ $p->min_qty }}</td>
</tr>
@empty
<tr><td colspan="3" class="py-4 text-gray-400 text-center">Toate piesele sunt în stoc.</td></tr>
@endforelse
</tbody>
</table>
</div>
</div>
@elseif ($tab === 'clients')
<div class="grid md:grid-cols-2 gap-4">
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<div class="text-xs text-gray-500">Clienți noi în perioada</div>
<div class="text-3xl font-bold">{{ $data['new_count'] }}</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
<h3 class="font-semibold mb-3">Lead-uri pe sursă</h3>
<table class="w-full text-sm">
@foreach ($data['by_source'] as $row)
<tr class="border-b last:border-0">
<td class="py-1">{{ \App\Models\Tenant\Lead::SOURCES[$row->source] ?? ($row->source ?? '—') }}</td>
<td class="text-right font-bold">{{ $row->cnt }}</td>
</tr>
@endforeach
</table>
</div>
</div>
@endif
</div>
</x-filament-panels::page>