feat: Pipeline board full-bleed + hover actions + Programare→Calendar
Addresses the gaps surfaced after the first redesign — the board was still boxed in Filament chrome (not truly full-page), hover floating-actions and "+ Adaugă" CTAs were missing, and the P0 "Programează" from deal card had no calendar wiring. Full-page: - getMaxContentWidth() = Width::Full - getHeading()/getSubheading() return empty so Filament's title bar disappears, leaving the kanban edge-to-edge - CSS uses :has(.pb-shell) to strip Filament's page padding + heading block at the layout level - Board height = calc(100vh - 64px); columns scroll independently Hover floating-actions on every card (column-aware): - Cols 1-2 (Cerere / Calculație): 📅 quickSchedule - Col 3 (Programat): ▶ start work (creates WO) - Col 4 (În lucru): ✓ mark Gata - Col 5 (Gata): 💰 mark Achitat - All cards with phone: 📞 tel: + 💬 wa.me - All cards: ↗ open in resource edit - Shown only on .pb-deal:hover, positioned absolute top-right "+ Adaugă" CTA at column bottom: - Cols 1-3 → /app/leads/create - Cols 4-5 → /app/work-orders/create Programare → Calendar (P0 AAA): - quickSchedule($key) on PipelineBoard creates a real Appointment row for tomorrow 10:00 linked to (client_id, vehicle_id, master_id, deal_id), sets deal.stage='scheduled' + scheduled_at, then shows a toast - Panel bottom action bar gains "📅 Programează" CTA for lead/deal cards - "📅 Calendar" jump CTA for WO cards - calendarUrl() returns the canonical filament.tenant.pages.calendar-board route Empty column state now reads "Gol — trage un card aici" instead of just "Gol" so the drop affordance is explicit. Stat strip + filter bar sticky at top; board fills the remaining viewport. Tests: +1 (quickSchedule creates Appointment + moves deal). Suite 181/181. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,21 @@ class PipelineBoard extends Page
|
||||
|
||||
protected string $view = 'filament.tenant.pages.pipeline-board';
|
||||
|
||||
public function getMaxContentWidth(): \Filament\Support\Enums\Width
|
||||
{
|
||||
return \Filament\Support\Enums\Width::Full;
|
||||
}
|
||||
|
||||
public function getHeading(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getSubheading(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public string $activeFilter = 'all'; // all | mine | urgent | today
|
||||
public ?string $openCardKey = null; // "lead:5" / "deal:8" / "wo:12"
|
||||
|
||||
@@ -269,6 +284,56 @@ class PipelineBoard extends Page
|
||||
$this->openCardKey = $key;
|
||||
}
|
||||
|
||||
/** Quick-schedule from a card: bumps the source to "Programat", creates an Appointment for tomorrow 10:00, returns calendar URL. */
|
||||
public function quickSchedule(string $key): void
|
||||
{
|
||||
[$kind, $id] = explode(':', $key, 2) + [null, null];
|
||||
$id = (int) $id;
|
||||
if (! $kind || ! $id) return;
|
||||
|
||||
$clientId = null; $vehicleId = null; $dealId = null; $title = null; $masterId = null;
|
||||
|
||||
if ($kind === 'lead') {
|
||||
$lead = Lead::find($id);
|
||||
if (! $lead) return;
|
||||
$deal = $lead->convert(['stage' => 'scheduled', 'scheduled_at' => now()->addDay()->setHour(10)->setMinute(0)]);
|
||||
$clientId = $deal->client_id; $vehicleId = $deal->vehicle_id; $dealId = $deal->id;
|
||||
$title = $deal->name;
|
||||
} elseif ($kind === 'deal') {
|
||||
$deal = Deal::find($id);
|
||||
if (! $deal) return;
|
||||
$deal->update(['stage' => 'scheduled', 'scheduled_at' => now()->addDay()->setHour(10)->setMinute(0)]);
|
||||
$clientId = $deal->client_id; $vehicleId = $deal->vehicle_id; $dealId = $deal->id;
|
||||
$title = $deal->name;
|
||||
$masterId = $deal->assigned_to;
|
||||
} elseif ($kind === 'wo') {
|
||||
$wo = WorkOrder::find($id);
|
||||
if (! $wo) return;
|
||||
$clientId = $wo->client_id; $vehicleId = $wo->vehicle_id;
|
||||
$title = $wo->number;
|
||||
$masterId = $wo->master_id;
|
||||
}
|
||||
|
||||
\App\Models\Tenant\Appointment::create([
|
||||
'client_id' => $clientId,
|
||||
'vehicle_id' => $vehicleId,
|
||||
'master_id' => $masterId,
|
||||
'deal_id' => $dealId,
|
||||
'date' => today()->addDay(),
|
||||
'time_start' => '10:00',
|
||||
'time_end' => '11:00',
|
||||
'title' => $title ?: 'Programare',
|
||||
'status' => 'scheduled',
|
||||
]);
|
||||
$this->notify("Programare creată · mâine 10:00");
|
||||
$this->openCardKey = null;
|
||||
}
|
||||
|
||||
public function calendarUrl(): string
|
||||
{
|
||||
return route('filament.tenant.pages.calendar-board');
|
||||
}
|
||||
|
||||
public function closeCard(): void
|
||||
{
|
||||
$this->openCardKey = null;
|
||||
|
||||
Reference in New Issue
Block a user