ifnex/04_Laravel/app/Filament/Widgets/ExchangeRateWidget.php
Kazem Alghasi 91984dbde3 feat(ui): redesign admin dashboard with layered grid and custom branding
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.
2026-10-03 11:08:50 +03:30

100 lines
4.2 KiB
PHP

<?php
namespace App\Filament\Widgets;
use App\Models\ExchangeRateHistory;
use App\Services\ExchangeRateService;
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
/**
* Layer 2 (right) — live exchange-rate summary, spanning 4/12 of the body grid.
*/
class ExchangeRateWidget extends BaseWidget
{
protected static ?int $sort = 3;
protected static bool $isLazy = false;
protected int|string|array $columnSpan = 12;
/**
* Stat cards inside the rate widget: 1 column on the narrow lg+ column,
* 2 on mobile so they don't all stack vertically on tiny screens.
*/
protected int|string|array $columns = 1;
protected function getStats(): array
{
$service = app(ExchangeRateService::class);
$aedInfo = $service->getLatestRateInfo('AED', 'IRR');
$usdInfo = $service->getLatestRateInfo('USD', 'IRR');
$aedChangePercent = $aedInfo['change_percent'];
$usdChangePercent = $usdInfo['change_percent'];
$aedChart = $service->getRateChart('AED', 'IRR', 7);
$usdChart = $service->getRateChart('USD', 'IRR', 7);
return [
Stat::make('نرخ درهم (AED)', ifnex_format_number($aedInfo['current_rate'] ?? 0))
->description(
$aedChangePercent !== null
? sprintf('%s %s — %s',
$aedChangePercent > 0 ? '↑' : ($aedChangePercent < 0 ? '↓' : '—'),
ifnex_persian_digits(number_format(abs((float) $aedChangePercent), 2)) . '%',
($aedInfo['changed_by'] ?? 'سیستم'))
: 'آخرین تغییر: ' . ($aedInfo['last_updated']?->format('Y/m/d H:i') ?? '—')
)
->descriptionIcon(
$aedChangePercent > 0 ? 'heroicon-m-arrow-trending-up' :
($aedChangePercent < 0 ? 'heroicon-m-arrow-trending-down' : 'heroicon-m-minus')
)
->icon('heroicon-o-currency-dollar')
->color(
$aedChangePercent > 0 ? 'danger' :
($aedChangePercent < 0 ? 'success' : 'gray')
)
->chart(!empty($aedChart) ? $aedChart : [0]),
Stat::make('نرخ دلار (USD)', ifnex_format_number($usdInfo['current_rate'] ?? 0))
->description(
$usdChangePercent !== null
? sprintf('%s %s — %s',
$usdChangePercent > 0 ? '↑' : ($usdChangePercent < 0 ? '↓' : '—'),
ifnex_persian_digits(number_format(abs((float) $usdChangePercent), 2)) . '%',
($usdInfo['changed_by'] ?? 'سیستم'))
: 'آخرین تغییر: ' . ($usdInfo['last_updated']?->format('Y/m/d H:i') ?? '—')
)
->descriptionIcon(
$usdChangePercent > 0 ? 'heroicon-m-arrow-trending-up' :
($usdChangePercent < 0 ? 'heroicon-m-arrow-trending-down' : 'heroicon-m-minus')
)
->icon('heroicon-o-banknotes')
->color(
$usdChangePercent > 0 ? 'danger' :
($usdChangePercent < 0 ? 'success' : 'gray')
)
->chart(!empty($usdChart) ? $usdChart : [0]),
Stat::make('تغییرات امروز',
ifnex_persian_digits(number_format(
ExchangeRateHistory::whereDate('created_at', today())->count()
))
)
->description('تغییرات نرخ ثبت‌شده در امروز')
->descriptionIcon('heroicon-m-arrow-path')
->icon('heroicon-o-arrow-path')
->color('info'),
Stat::make('در انتظار تأیید',
ifnex_persian_digits(number_format($service->getPendingCount()))
)
->description('نرخ‌های پیشنهادی API — منتظر تأیید مدیر')
->descriptionIcon('heroicon-m-clock')
->icon('heroicon-o-clock')
->color('warning'),
];
}
}