Implement a comprehensive overhaul of the Filament admin dashboard, introducing a structured 12-column responsive grid layout and enhanced visual identity. Key changes include: - Redesigned Dashboard layout with a 4-layer progressive disclosure architecture (KPIs, Exchange Rates, Data Tables, and Trends). - Introduced custom IFNEX design tokens via CSS, including branded stat cards, status badges, and improved sidebar typography. - Enhanced FinanceOverviewWidget with real-time trend analysis and sparkline-ready data structures. - Refactored RecentShipments and RecentTransactions widgets to use custom Blade templates for a more polished, consistent UI. - Integrated brand assets including the company logo and custom authentication pages. - Added global number formatting helpers and integrated them into financial widgets. - Updated AdminPanelProvider to support custom login pages and brand-specific header/footer hooks.
127 lines
4.4 KiB
PHP
127 lines
4.4 KiB
PHP
<?php
|
||
|
||
namespace App\Filament\Widgets;
|
||
|
||
use App\Models\WalletTransaction;
|
||
use Filament\Widgets\ChartWidget;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Morilog\Jalali\Jalalian;
|
||
|
||
/**
|
||
* Layer 2 (left) — 7-day revenue trend chart, spanning 8/12 of the body grid.
|
||
*/
|
||
class TransactionChartWidget extends ChartWidget
|
||
{
|
||
protected static ?int $sort = 8;
|
||
|
||
protected static ?string $pollingInterval = '60s';
|
||
|
||
protected static ?string $heading = 'روند تراکنشهای ۷ روز اخیر';
|
||
|
||
protected static ?string $maxHeight = '320px';
|
||
|
||
protected int|string|array $columnSpan = 12;
|
||
|
||
protected function getData(): array
|
||
{
|
||
$data = WalletTransaction::select(
|
||
DB::raw('DATE(created_at) as date'),
|
||
DB::raw('SUM(CASE WHEN type = "deposit" AND status = "completed" THEN amount ELSE 0 END) as deposits'),
|
||
DB::raw('SUM(CASE WHEN type IN ("withdrawal", "order_payment") AND status = "completed" THEN amount ELSE 0 END) as withdrawals')
|
||
)
|
||
->where('created_at', '>=', now()->subDays(7))
|
||
->groupBy('date')
|
||
->orderBy('date')
|
||
->get();
|
||
|
||
$labels = $data->pluck('date')->map(function ($date) {
|
||
return Jalalian::fromCarbon(\Carbon\Carbon::parse($date))->format('l d');
|
||
})->toArray();
|
||
$deposits = $data->pluck('deposits')->map(fn ($v) => (float) $v)->toArray();
|
||
$withdrawals = $data->pluck('withdrawals')->map(fn ($v) => (float) $v)->toArray();
|
||
|
||
return [
|
||
'datasets' => [
|
||
[
|
||
'label' => 'واریزیها',
|
||
'data' => $deposits,
|
||
'borderColor' => '#10b981',
|
||
'backgroundColor' => 'rgba(16, 185, 129, 0.15)',
|
||
'fill' => true,
|
||
'tension' => 0.4,
|
||
'pointRadius' => 3,
|
||
'pointHoverRadius' => 5,
|
||
'pointBackgroundColor' => '#10b981',
|
||
],
|
||
[
|
||
'label' => 'برداشتها',
|
||
'data' => $withdrawals,
|
||
'borderColor' => '#f59e0b',
|
||
'backgroundColor' => 'rgba(245, 158, 11, 0.15)',
|
||
'fill' => true,
|
||
'tension' => 0.4,
|
||
'pointRadius' => 3,
|
||
'pointHoverRadius' => 5,
|
||
'pointBackgroundColor' => '#f59e0b',
|
||
],
|
||
],
|
||
'labels' => $labels,
|
||
];
|
||
}
|
||
|
||
protected function getType(): string
|
||
{
|
||
return 'line';
|
||
}
|
||
|
||
protected function getOptions(): array
|
||
{
|
||
return [
|
||
'plugins' => [
|
||
'legend' => [
|
||
'position' => 'top',
|
||
'align' => 'end',
|
||
'labels' => [
|
||
'font' => [
|
||
'family' => 'Vazirmatn',
|
||
'size' => 12,
|
||
],
|
||
'usePointStyle' => true,
|
||
'boxWidth' => 8,
|
||
],
|
||
],
|
||
'tooltip' => [
|
||
'callbacks' => [
|
||
'label' => 'function(context) {
|
||
var v = context.parsed.y;
|
||
if (v === null || v === undefined) return "";
|
||
return context.dataset.label + ": " + v.toLocaleString("fa-IR") + " ریال";
|
||
}',
|
||
],
|
||
],
|
||
],
|
||
'scales' => [
|
||
'y' => [
|
||
'beginAtZero' => true,
|
||
'ticks' => [
|
||
'callback' => 'function(value) { return value.toLocaleString("fa-IR"); }',
|
||
'font' => ['family' => 'Vazirmatn', 'size' => 11],
|
||
],
|
||
'grid' => [
|
||
'color' => 'rgba(0, 0, 0, 0.05)',
|
||
],
|
||
],
|
||
'x' => [
|
||
'ticks' => [
|
||
'maxRotation' => 0,
|
||
'font' => ['family' => 'Vazirmatn', 'size' => 11],
|
||
],
|
||
'grid' => [
|
||
'display' => false,
|
||
],
|
||
],
|
||
],
|
||
'maintainAspectRatio' => false,
|
||
];
|
||
}
|
||
} |