feat(dashboard): clickable stat cards + 2 new WO counters + parts row link
Every card on the tenant main dashboard now links to a relevant page:
Operations widget (was 5 cards, now 7):
- Clienți → /app/clients
- Mașini → /app/vehicles
- Cereri noi → /app/leads pre-filtered by status=new
- Deal-uri active → /app/deals
- Programări azi → /app/calendar-board
- NEW: Fișe în lucru (in_work + awaiting_parts) → /app/work-orders filter
- NEW: Fișe gata predare (ready) → /app/work-orders filter
Finance widget (all 4 cards clickable now):
- Încasări → /app/payments
- Cheltuieli → /app/expenses
- Profit → /app/reports (with "Vezi raportul financiar" hint)
- Datorii → /app/work-orders pre-filtered by pay_status=unpaid
LowStockTable rows now navigate to /app/parts/{id}/edit on click and
have an explicit edit action for touch users.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,19 +33,25 @@ class FinanceOverview extends BaseWidget
|
|||||||
Stat::make(__('Încasări (luna)'), number_format($income, 2, '.', ' ') . ' MDL')
|
Stat::make(__('Încasări (luna)'), number_format($income, 2, '.', ' ') . ' MDL')
|
||||||
->icon('heroicon-o-arrow-trending-up')
|
->icon('heroicon-o-arrow-trending-up')
|
||||||
->color('success')
|
->color('success')
|
||||||
->description(now()->translatedFormat('F Y')),
|
->description(now()->translatedFormat('F Y'))
|
||||||
|
->url(url('/app/payments')),
|
||||||
|
|
||||||
Stat::make(__('Cheltuieli (luna)'), number_format($expenses, 2, '.', ' ') . ' MDL')
|
Stat::make(__('Cheltuieli (luna)'), number_format($expenses, 2, '.', ' ') . ' MDL')
|
||||||
->icon('heroicon-o-arrow-trending-down')
|
->icon('heroicon-o-arrow-trending-down')
|
||||||
->color('danger'),
|
->color('danger')
|
||||||
|
->url(url('/app/expenses')),
|
||||||
|
|
||||||
Stat::make(__('Profit (luna)'), number_format($profit, 2, '.', ' ') . ' MDL')
|
Stat::make(__('Profit (luna)'), number_format($profit, 2, '.', ' ') . ' MDL')
|
||||||
->icon('heroicon-o-banknotes')
|
->icon('heroicon-o-banknotes')
|
||||||
->color($profit >= 0 ? 'success' : 'danger'),
|
->color($profit >= 0 ? 'success' : 'danger')
|
||||||
|
->description(__('Vezi raportul financiar'))
|
||||||
|
->url(url('/app/reports')),
|
||||||
|
|
||||||
Stat::make(__('Datorii clienți'), number_format($debt, 2, '.', ' ') . ' MDL')
|
Stat::make(__('Datorii clienți'), number_format($debt, 2, '.', ' ') . ' MDL')
|
||||||
->icon('heroicon-o-exclamation-circle')
|
->icon('heroicon-o-exclamation-circle')
|
||||||
->color($debt > 0 ? 'warning' : 'success'),
|
->color($debt > 0 ? 'warning' : 'success')
|
||||||
|
->description(__('Fișe cu plată în așteptare'))
|
||||||
|
->url(url('/app/work-orders') . '?tableFilters[pay_status][value]=unpaid'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Filament\Tenant\Widgets;
|
namespace App\Filament\Tenant\Widgets;
|
||||||
|
|
||||||
use App\Models\Tenant\Part;
|
use App\Models\Tenant\Part;
|
||||||
|
use Filament\Actions\Action;
|
||||||
use Filament\Tables;
|
use Filament\Tables;
|
||||||
use Filament\Tables\Table;
|
use Filament\Tables\Table;
|
||||||
use Filament\Widgets\TableWidget as BaseWidget;
|
use Filament\Widgets\TableWidget as BaseWidget;
|
||||||
@@ -38,6 +39,13 @@ class LowStockTable extends BaseWidget
|
|||||||
Tables\Columns\TextColumn::make('min_qty')->label(__('Min.'))->alignRight(),
|
Tables\Columns\TextColumn::make('min_qty')->label(__('Min.'))->alignRight(),
|
||||||
Tables\Columns\TextColumn::make('preferredSupplier.name')->label(__('Furnizor'))->placeholder('—'),
|
Tables\Columns\TextColumn::make('preferredSupplier.name')->label(__('Furnizor'))->placeholder('—'),
|
||||||
])
|
])
|
||||||
|
->recordUrl(fn (Part $r) => url('/app/parts/' . $r->id . '/edit'))
|
||||||
|
->actions([
|
||||||
|
Action::make('edit')
|
||||||
|
->label(__('Editează'))
|
||||||
|
->icon('heroicon-m-pencil-square')
|
||||||
|
->url(fn (Part $r) => url('/app/parts/' . $r->id . '/edit')),
|
||||||
|
])
|
||||||
->paginated(false)
|
->paginated(false)
|
||||||
->defaultSort('qty');
|
->defaultSort('qty');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Models\Tenant\Client;
|
|||||||
use App\Models\Tenant\Deal;
|
use App\Models\Tenant\Deal;
|
||||||
use App\Models\Tenant\Lead;
|
use App\Models\Tenant\Lead;
|
||||||
use App\Models\Tenant\Vehicle;
|
use App\Models\Tenant\Vehicle;
|
||||||
|
use App\Models\Tenant\WorkOrder;
|
||||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||||
|
|
||||||
@@ -19,32 +20,51 @@ class StatsOverview extends BaseWidget
|
|||||||
$newLeads = Lead::where('status', 'new')->count();
|
$newLeads = Lead::where('status', 'new')->count();
|
||||||
$openDeals = Deal::whereNotIn('stage', ['done', 'lost'])->count();
|
$openDeals = Deal::whereNotIn('stage', ['done', 'lost'])->count();
|
||||||
$todayAppointments = Appointment::whereDate('date', today())->count();
|
$todayAppointments = Appointment::whereDate('date', today())->count();
|
||||||
|
$activeWos = WorkOrder::whereIn('status', ['in_work', 'awaiting_parts'])->count();
|
||||||
|
$readyWos = WorkOrder::where('status', 'ready')->count();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Stat::make(__('Clienți'), Client::count())
|
Stat::make(__('Clienți'), Client::count())
|
||||||
->description(__('Total în baza de date'))
|
->description(__('Total în baza de date'))
|
||||||
->icon('heroicon-o-users')
|
->icon('heroicon-o-users')
|
||||||
->color('primary'),
|
->color('primary')
|
||||||
|
->url(url('/app/clients')),
|
||||||
|
|
||||||
Stat::make(__('Mașini'), Vehicle::count())
|
Stat::make(__('Mașini'), Vehicle::count())
|
||||||
->description(__('Total înregistrate'))
|
->description(__('Total înregistrate'))
|
||||||
->icon('heroicon-o-truck')
|
->icon('heroicon-o-truck')
|
||||||
->color('info'),
|
->color('info')
|
||||||
|
->url(url('/app/vehicles')),
|
||||||
|
|
||||||
Stat::make(__('Cereri noi'), $newLeads)
|
Stat::make(__('Cereri noi'), $newLeads)
|
||||||
->description(__('De procesat'))
|
->description(__('De procesat'))
|
||||||
->icon('heroicon-o-inbox-arrow-down')
|
->icon('heroicon-o-inbox-arrow-down')
|
||||||
->color($newLeads > 0 ? 'warning' : 'success'),
|
->color($newLeads > 0 ? 'warning' : 'success')
|
||||||
|
->url(url('/app/leads') . '?tableFilters[status][value]=new'),
|
||||||
|
|
||||||
Stat::make(__('Deal-uri active'), $openDeals)
|
Stat::make(__('Deal-uri active'), $openDeals)
|
||||||
->description(__('În pipeline'))
|
->description(__('În pipeline'))
|
||||||
->icon('heroicon-o-funnel')
|
->icon('heroicon-o-funnel')
|
||||||
->color('primary'),
|
->color('primary')
|
||||||
|
->url(url('/app/deals')),
|
||||||
|
|
||||||
Stat::make(__('Programări azi'), $todayAppointments)
|
Stat::make(__('Programări azi'), $todayAppointments)
|
||||||
->description(today()->format('d.m.Y'))
|
->description(today()->format('d.m.Y'))
|
||||||
->icon('heroicon-o-calendar')
|
->icon('heroicon-o-calendar')
|
||||||
->color('success'),
|
->color('success')
|
||||||
|
->url(url('/app/calendar-board')),
|
||||||
|
|
||||||
|
Stat::make(__('Fișe în lucru'), $activeWos)
|
||||||
|
->description(__('În serviciu sau așteaptă piese'))
|
||||||
|
->icon('heroicon-o-wrench-screwdriver')
|
||||||
|
->color($activeWos > 0 ? 'primary' : 'success')
|
||||||
|
->url(url('/app/work-orders') . '?tableFilters[status][value]=in_work'),
|
||||||
|
|
||||||
|
Stat::make(__('Fișe gata predare'), $readyWos)
|
||||||
|
->description(__('Așteaptă client'))
|
||||||
|
->icon('heroicon-o-check-circle')
|
||||||
|
->color($readyWos > 0 ? 'success' : 'gray')
|
||||||
|
->url(url('/app/work-orders') . '?tableFilters[status][value]=ready'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -276,6 +276,7 @@
|
|||||||
"Avans achitat": "Advance paid",
|
"Avans achitat": "Advance paid",
|
||||||
"Avg zile": "Avg days",
|
"Avg zile": "Avg days",
|
||||||
"Azi": "Today",
|
"Azi": "Today",
|
||||||
|
"Așteaptă client": "Awaiting customer",
|
||||||
"Așteaptă piese": "Awaiting parts",
|
"Așteaptă piese": "Awaiting parts",
|
||||||
"Așteaptă piesele": "Awaiting parts",
|
"Așteaptă piesele": "Awaiting parts",
|
||||||
"Aștept aprobare client": "Awaiting client approval",
|
"Aștept aprobare client": "Awaiting client approval",
|
||||||
@@ -854,8 +855,11 @@
|
|||||||
"Fișa nu are client asociat.": "This work order has no client attached.",
|
"Fișa nu are client asociat.": "This work order has no client attached.",
|
||||||
"Fișe": "Orders",
|
"Fișe": "Orders",
|
||||||
"Fișe active": "Active orders",
|
"Fișe active": "Active orders",
|
||||||
|
"Fișe cu plată în așteptare": "Work orders with pending payment",
|
||||||
"Fișe deschise": "Open orders",
|
"Fișe deschise": "Open orders",
|
||||||
|
"Fișe gata predare": "Ready for pickup",
|
||||||
"Fișe lucru": "Work orders",
|
"Fișe lucru": "Work orders",
|
||||||
|
"Fișe în lucru": "Active work orders",
|
||||||
"Fișe închise": "Closed orders",
|
"Fișe închise": "Closed orders",
|
||||||
"Fișier": "File",
|
"Fișier": "File",
|
||||||
"Fișier Excel (.xlsx) sau CSV *": "Excel file (.xlsx) or CSV *",
|
"Fișier Excel (.xlsx) sau CSV *": "Excel file (.xlsx) or CSV *",
|
||||||
@@ -2192,6 +2196,7 @@
|
|||||||
"Vezi profit & pierdere (P&L)": "View P&L",
|
"Vezi profit & pierdere (P&L)": "View P&L",
|
||||||
"Vezi propriul salariu": "View own salary",
|
"Vezi propriul salariu": "View own salary",
|
||||||
"Vezi rapoarte & analitică": "View reports & analytics",
|
"Vezi rapoarte & analitică": "View reports & analytics",
|
||||||
|
"Vezi raportul financiar": "Open financial report",
|
||||||
"Vezi salariile tuturor": "View all salaries",
|
"Vezi salariile tuturor": "View all salaries",
|
||||||
"Vezi stoc (piese)": "View stock (parts)",
|
"Vezi stoc (piese)": "View stock (parts)",
|
||||||
"Vezi toate fișele": "View all orders",
|
"Vezi toate fișele": "View all orders",
|
||||||
@@ -2525,6 +2530,7 @@
|
|||||||
"În lucru": "In progress",
|
"În lucru": "In progress",
|
||||||
"În pipeline": "In pipeline",
|
"În pipeline": "In pipeline",
|
||||||
"În procesare": "Processing",
|
"În procesare": "Processing",
|
||||||
|
"În serviciu sau așteaptă piese": "In service or waiting for parts",
|
||||||
"În trial": "In trial",
|
"În trial": "In trial",
|
||||||
"Înainte": "Back",
|
"Înainte": "Back",
|
||||||
"Înapoi": "Back",
|
"Înapoi": "Back",
|
||||||
|
|||||||
@@ -276,6 +276,7 @@
|
|||||||
"Avans achitat": "Аванс оплачен",
|
"Avans achitat": "Аванс оплачен",
|
||||||
"Avg zile": "Ср. дней",
|
"Avg zile": "Ср. дней",
|
||||||
"Azi": "Сегодня",
|
"Azi": "Сегодня",
|
||||||
|
"Așteaptă client": "Ожидают клиента",
|
||||||
"Așteaptă piese": "Ждёт запчасти",
|
"Așteaptă piese": "Ждёт запчасти",
|
||||||
"Așteaptă piesele": "Ждёт запчасти",
|
"Așteaptă piesele": "Ждёт запчасти",
|
||||||
"Aștept aprobare client": "Ожидание одобрения клиента",
|
"Aștept aprobare client": "Ожидание одобрения клиента",
|
||||||
@@ -854,8 +855,11 @@
|
|||||||
"Fișa nu are client asociat.": "К заказ-наряду не привязан клиент.",
|
"Fișa nu are client asociat.": "К заказ-наряду не привязан клиент.",
|
||||||
"Fișe": "Наряды",
|
"Fișe": "Наряды",
|
||||||
"Fișe active": "Активные наряды",
|
"Fișe active": "Активные наряды",
|
||||||
|
"Fișe cu plată în așteptare": "Заказы с ожидающей оплатой",
|
||||||
"Fișe deschise": "Открытые наряды",
|
"Fișe deschise": "Открытые наряды",
|
||||||
|
"Fișe gata predare": "Готовы к выдаче",
|
||||||
"Fișe lucru": "Рабочие листы",
|
"Fișe lucru": "Рабочие листы",
|
||||||
|
"Fișe în lucru": "Заказы в работе",
|
||||||
"Fișe închise": "Закрытые наряды",
|
"Fișe închise": "Закрытые наряды",
|
||||||
"Fișier": "Файл",
|
"Fișier": "Файл",
|
||||||
"Fișier Excel (.xlsx) sau CSV *": "Файл Excel (.xlsx) или CSV *",
|
"Fișier Excel (.xlsx) sau CSV *": "Файл Excel (.xlsx) или CSV *",
|
||||||
@@ -2192,6 +2196,7 @@
|
|||||||
"Vezi profit & pierdere (P&L)": "Просмотр P&L",
|
"Vezi profit & pierdere (P&L)": "Просмотр P&L",
|
||||||
"Vezi propriul salariu": "Просмотр своей зарплаты",
|
"Vezi propriul salariu": "Просмотр своей зарплаты",
|
||||||
"Vezi rapoarte & analitică": "Просмотр отчётов и аналитики",
|
"Vezi rapoarte & analitică": "Просмотр отчётов и аналитики",
|
||||||
|
"Vezi raportul financiar": "Открыть финансовый отчёт",
|
||||||
"Vezi salariile tuturor": "Просмотр зарплат всех",
|
"Vezi salariile tuturor": "Просмотр зарплат всех",
|
||||||
"Vezi stoc (piese)": "Просмотр склада (запчасти)",
|
"Vezi stoc (piese)": "Просмотр склада (запчасти)",
|
||||||
"Vezi toate fișele": "Просмотр всех нарядов",
|
"Vezi toate fișele": "Просмотр всех нарядов",
|
||||||
@@ -2525,6 +2530,7 @@
|
|||||||
"În lucru": "В работе",
|
"În lucru": "В работе",
|
||||||
"În pipeline": "В воронке",
|
"În pipeline": "В воронке",
|
||||||
"În procesare": "В обработке",
|
"În procesare": "В обработке",
|
||||||
|
"În serviciu sau așteaptă piese": "В работе или ожидание запчастей",
|
||||||
"În trial": "В триале",
|
"În trial": "В триале",
|
||||||
"Înainte": "Назад",
|
"Înainte": "Назад",
|
||||||
"Înapoi": "Назад",
|
"Înapoi": "Назад",
|
||||||
|
|||||||
Reference in New Issue
Block a user