Auto commit: 2026-04-06T07:35:25.615Z
This commit is contained in:
parent
429417d752
commit
6d57abd008
@ -9,7 +9,7 @@ use Filament\Widgets\ChartWidget;
|
||||
|
||||
class FunnelStageChart extends ChartWidget
|
||||
{
|
||||
protected static ?string $heading = 'Funnel ejecutivo';
|
||||
protected static ?string $heading = 'Funnel';
|
||||
|
||||
protected int | string | array $columnSpan = 'full';
|
||||
|
||||
@ -32,7 +32,7 @@ class FunnelStageChart extends ChartWidget
|
||||
'borderRadius' => 12,
|
||||
'borderSkipped' => false,
|
||||
]],
|
||||
'labels' => ['Taxi', 'Views', 'Clicks', 'Bookings'],
|
||||
'labels' => ['Taxi', 'Espera útil', 'Detalle', 'Reserva'],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -21,28 +21,20 @@ class JourneyStoryboard extends Widget
|
||||
$views = Event::where('event_type', 'recommendation_viewed')->count();
|
||||
$clicks = Event::where('event_type', 'recommendation_clicked')->count();
|
||||
$bookingsCount = Booking::count();
|
||||
$latestBooking = Booking::with(['offer', 'ride', 'recommendation'])->latest()->first();
|
||||
$commission = (float) Booking::sum('commission_amount');
|
||||
$gmv = (float) Booking::sum('amount');
|
||||
$rideToBooking = $rides > 0 ? round(($bookingsCount / $rides) * 100, 1) : 0;
|
||||
$clickThrough = $views > 0 ? round(($clicks / $views) * 100, 1) : 0;
|
||||
$topZone = Ride::query()
|
||||
->selectRaw("COALESCE(context_zone, 'General') as zone, COUNT(*) as aggregate")
|
||||
->groupBy('zone')
|
||||
->orderByDesc('aggregate')
|
||||
->value('zone') ?: 'General';
|
||||
|
||||
return [
|
||||
'rides' => $rides,
|
||||
'views' => $views,
|
||||
'clicks' => $clicks,
|
||||
'bookingsCount' => $bookingsCount,
|
||||
'latestBooking' => $latestBooking,
|
||||
'commission' => $commission,
|
||||
'gmv' => $gmv,
|
||||
'rideToBooking' => $rideToBooking,
|
||||
'clickThrough' => $clickThrough,
|
||||
'topZone' => $topZone,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
45
app/Filament/Widgets/LatestBookingsTable.php
Normal file
45
app/Filament/Widgets/LatestBookingsTable.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Booking;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as BaseWidget;
|
||||
|
||||
class LatestBookingsTable extends BaseWidget
|
||||
{
|
||||
protected static ?string $heading = 'Últimas reservas';
|
||||
|
||||
protected static ?int $sort = 4;
|
||||
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(Booking::query()->with(['offer', 'ride'])->latest()->limit(5))
|
||||
->paginated(false)
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('offer.title')
|
||||
->label('Oferta')
|
||||
->description(fn (Booking $record): string => $record->customer_name ?: ($record->ride?->pickup_label ?: 'Reserva directa'))
|
||||
->weight('semibold'),
|
||||
Tables\Columns\TextColumn::make('amount')
|
||||
->label('Importe')
|
||||
->money('EUR'),
|
||||
Tables\Columns\TextColumn::make('status')
|
||||
->label('Estado')
|
||||
->badge()
|
||||
->color(fn (?string $state): string => match ($state) {
|
||||
'confirmed', 'completed' => 'success',
|
||||
'pending' => 'warning',
|
||||
'cancelled' => 'danger',
|
||||
default => 'gray',
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Creada')
|
||||
->since(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
46
app/Filament/Widgets/LatestRidesTable.php
Normal file
46
app/Filament/Widgets/LatestRidesTable.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Ride;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget as BaseWidget;
|
||||
|
||||
class LatestRidesTable extends BaseWidget
|
||||
{
|
||||
protected static ?string $heading = 'Últimos trayectos';
|
||||
|
||||
protected static ?int $sort = 3;
|
||||
|
||||
protected int | string | array $columnSpan = 1;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->query(Ride::query()->latest()->limit(5))
|
||||
->paginated(false)
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('pickup_label')
|
||||
->label('Trayecto')
|
||||
->description(fn (Ride $record): string => '→ '.$record->destination_label)
|
||||
->weight('semibold'),
|
||||
Tables\Columns\TextColumn::make('status')
|
||||
->label('Estado')
|
||||
->badge()
|
||||
->formatStateUsing(fn (?string $state): string => ucfirst($state ?: 'pendiente'))
|
||||
->color(fn (?string $state): string => match ($state) {
|
||||
'confirmed', 'completed' => 'success',
|
||||
'pending' => 'warning',
|
||||
'cancelled' => 'danger',
|
||||
default => 'gray',
|
||||
}),
|
||||
Tables\Columns\TextColumn::make('eta_minutes')
|
||||
->label('ETA')
|
||||
->formatStateUsing(fn ($state): string => $state ? $state.' min' : '—'),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->label('Creado')
|
||||
->since(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,8 @@ namespace App\Providers\Filament;
|
||||
|
||||
use App\Filament\Widgets\FunnelStageChart;
|
||||
use App\Filament\Widgets\JourneyStoryboard;
|
||||
use App\Filament\Widgets\LatestBookingsTable;
|
||||
use App\Filament\Widgets\LatestRidesTable;
|
||||
use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\AuthenticateSession;
|
||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||
@ -41,6 +43,8 @@ class AdminPanelProvider extends PanelProvider
|
||||
->widgets([
|
||||
JourneyStoryboard::class,
|
||||
FunnelStageChart::class,
|
||||
LatestRidesTable::class,
|
||||
LatestBookingsTable::class,
|
||||
])
|
||||
->middleware([
|
||||
EncryptCookies::class,
|
||||
|
||||
@ -8,33 +8,29 @@
|
||||
<article class="card form-card">
|
||||
<span class="eyebrow">Reserva confirmada</span>
|
||||
<h1 style="font-size:clamp(2.15rem,4vw,3.7rem);max-width:12ch;">Reserva cerrada.</h1>
|
||||
<p class="compact-lead">{{ $booking->customer_name }} ya tiene {{ $booking->offer->title }}.</p>
|
||||
<p class="compact-lead">{{ $booking->offer->title }} ya está en marcha.</p>
|
||||
|
||||
<div class="story-rail" aria-label="Progreso del funnel">
|
||||
<span class="story-chip is-done">1 · Taxi</span>
|
||||
<span class="story-chip is-done">2 · Contexto</span>
|
||||
<span class="story-chip is-done">2 · Confirmado</span>
|
||||
<span class="story-chip is-done">3 · Oferta</span>
|
||||
<span class="story-chip is-active">4 · Reserva</span>
|
||||
</div>
|
||||
|
||||
<div class="route-card">
|
||||
<small>Resultado</small>
|
||||
<small>Estado actual</small>
|
||||
<strong>{{ $booking->offer->title }}</strong>
|
||||
<span>{{ $booking->amount ? '€'.number_format((float) $booking->amount, 2) : 'Importe pendiente' }} · {{ ucfirst($booking->status) }} · {{ $booking->ride?->context_zone ?: ($booking->offer->location_label ?: 'General') }}</span>
|
||||
<span>{{ $booking->amount ? '€'.number_format((float) $booking->amount, 2) : 'Importe pendiente' }} · {{ ucfirst($booking->status) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="metric-strip metric-strip--3">
|
||||
<div class="metric-strip metric-strip--2">
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $booking->amount ? '€'.number_format((float) $booking->amount, 0) : '—' }}</strong>
|
||||
<span>importe</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $booking->commission_amount ? '€'.number_format((float) $booking->commission_amount, 0) : '—' }}</strong>
|
||||
<span>comisión</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $booking->recommendation?->position ? 'Top '.$booking->recommendation->position : 'Directa' }}</strong>
|
||||
<span>atribución</span>
|
||||
<strong>{{ ucfirst($booking->status) }}</strong>
|
||||
<span>estado</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -43,37 +39,36 @@
|
||||
@if($booking->ride)
|
||||
<a class="btn btn-secondary" href="{{ route('rides.confirmed', $booking->ride) }}">Volver al taxi confirmado</a>
|
||||
@endif
|
||||
<button type="button" class="icon-button" data-modal-open="booking-business" data-tooltip="Detalle negocio">Ver negocio</button>
|
||||
<button type="button" class="icon-button" data-modal-open="booking-business" data-tooltip="Más info">ℹ️</button>
|
||||
</div>
|
||||
|
||||
<details class="disclosure" style="margin-top:8px;">
|
||||
<summary>
|
||||
<span>ℹ️ Ver detalle de atribución</span>
|
||||
<span>ℹ️ Más info</span>
|
||||
<span>Abrir</span>
|
||||
</summary>
|
||||
<div class="disclosure-body">
|
||||
<ul class="accordion-list">
|
||||
<li>Booking UUID: <strong>{{ $booking->uuid }}</strong>.</li>
|
||||
<li>Estado: <strong>{{ ucfirst($booking->status) }}</strong>.</li>
|
||||
<li>Ride vinculado: <strong>{{ $booking->ride?->uuid ?? 'Reserva directa' }}</strong>.</li>
|
||||
<li>Origen: <strong>{{ $booking->ride?->source_channel ? ucfirst($booking->ride->source_channel) : 'Directo' }}</strong>.</li>
|
||||
<li>Zona: <strong>{{ $booking->ride?->context_zone ?: ($booking->offer->location_label ?: 'General') }}</strong>.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
|
||||
<aside class="card form-card">
|
||||
<span class="eyebrow">Business snapshot</span>
|
||||
<span class="eyebrow">Resumen</span>
|
||||
<div class="decision-primary">
|
||||
<h2>Valor visible</h2>
|
||||
<h2>Todo listo.</h2>
|
||||
<div class="screen-kpis">
|
||||
<div class="screen-kpi">
|
||||
<strong>{{ $booking->offer->location_label ?: 'Zona activa' }}</strong>
|
||||
<span>zona</span>
|
||||
</div>
|
||||
<div class="screen-kpi">
|
||||
<strong>{{ $booking->ride?->source_channel ? ucfirst($booking->ride->source_channel) : 'Directo' }}</strong>
|
||||
<span>canal</span>
|
||||
<strong>{{ $booking->party_size ?: 1 }}</strong>
|
||||
<span>personas</span>
|
||||
</div>
|
||||
<div class="screen-kpi">
|
||||
<strong>{{ $booking->customer_email ?: 'No informado' }}</strong>
|
||||
@ -84,14 +79,14 @@
|
||||
|
||||
<details class="disclosure disclosure--soft" style="margin-top:16px;">
|
||||
<summary>
|
||||
<span>ℹ️ Ver datos operativos</span>
|
||||
<span>Ver detalle</span>
|
||||
<span>Abrir</span>
|
||||
</summary>
|
||||
<div class="disclosure-body">
|
||||
<ul class="accordion-list">
|
||||
<li>{{ $booking->offer->title }} queda como conversión cerrada.</li>
|
||||
<li>La comisión demo ya es visible en dashboard.</li>
|
||||
<li>El usuario ve un cierre limpio; negocio ve trazabilidad cuando la pide.</li>
|
||||
<li>Si vienes desde taxi confirmado, la reserva mantiene la trazabilidad completa.</li>
|
||||
<li>La lectura económica queda fuera de la vista principal.</li>
|
||||
<li>Negocio la consulta solo cuando la necesita.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
@ -102,15 +97,15 @@
|
||||
<div class="app-modal-card">
|
||||
<div class="app-modal-head">
|
||||
<div>
|
||||
<p class="eyebrow">Detalle negocio</p>
|
||||
<h2 id="booking-business-title">Conversión atribuible</h2>
|
||||
<p class="eyebrow">Más info</p>
|
||||
<h2 id="booking-business-title">Detalle de atribución</h2>
|
||||
</div>
|
||||
<button type="button" class="app-modal-close" data-modal-close aria-label="Cerrar modal">Cerrar</button>
|
||||
</div>
|
||||
<div class="metric-strip metric-strip--3">
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $booking->amount ? '€'.number_format((float) $booking->amount, 2) : 'Pendiente' }}</strong>
|
||||
<span>GMV</span>
|
||||
<span>importe</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $booking->commission_amount ? '€'.number_format((float) $booking->commission_amount, 2) : 'Pendiente' }}</strong>
|
||||
@ -118,14 +113,14 @@
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $booking->recommendation?->position ? 'Top '.$booking->recommendation->position : 'Directa' }}</strong>
|
||||
<span>origen</span>
|
||||
<span>atribución</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="accordion-list">
|
||||
<li>Oferta: <strong>{{ $booking->offer->title }}</strong>.</li>
|
||||
<li>Zona: <strong>{{ $booking->ride?->context_zone ?: ($booking->offer->location_label ?: 'General') }}</strong>.</li>
|
||||
<li>Canal: <strong>{{ $booking->ride?->source_channel ? ucfirst($booking->ride->source_channel) : 'Directo' }}</strong>.</li>
|
||||
<li>La UI principal sigue limpia; la explicación queda bajo demanda.</li>
|
||||
<li>Ride vinculado: <strong>{{ $booking->ride?->uuid ?? 'Reserva directa' }}</strong>.</li>
|
||||
<li>Origen: <strong>{{ $booking->ride?->source_channel ? ucfirst($booking->ride->source_channel) : 'Directo' }}</strong>.</li>
|
||||
<li>La UI principal sigue limpia; el detalle de negocio aparece solo bajo demanda.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
@ -1,83 +1,79 @@
|
||||
<x-filament-widgets::widget>
|
||||
<x-filament::section>
|
||||
<div class="rounded-3xl border border-primary-100 bg-gradient-to-br from-white via-primary-50/40 to-amber-50/60 p-6 shadow-sm">
|
||||
<div class="rounded-3xl border border-white/80 bg-gradient-to-br from-white via-white to-primary-50/70 p-6 shadow-sm">
|
||||
<div class="flex flex-col gap-6 xl:flex-row xl:items-end xl:justify-between">
|
||||
<div class="space-y-3">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-primary-600">Executive snapshot</p>
|
||||
<div>
|
||||
<h2 class="text-3xl font-semibold tracking-tight text-gray-950">Booking atribuible, sin ruido.</h2>
|
||||
<p class="mt-2 max-w-2xl text-sm text-gray-600">Un KPI principal arriba. El funnel debajo. Lo demás, bajo demanda.</p>
|
||||
<div class="flex items-center gap-3">
|
||||
<div>
|
||||
<h2 class="text-3xl font-semibold tracking-tight text-gray-950">Producto limpio. KPI claro.</h2>
|
||||
<p class="mt-2 text-sm text-gray-600">Solo decisión arriba. Lectura profunda bajo demanda.</p>
|
||||
</div>
|
||||
<span class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-gray-200 bg-white text-sm text-gray-500" title="La comisión atribuible es el KPI principal del dashboard.">ℹ️</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-stretch">
|
||||
<div class="min-w-[220px] rounded-2xl border border-white/80 bg-white/90 p-5 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.22em] text-gray-500">KPI principal</p>
|
||||
<p class="mt-2 text-4xl font-semibold tracking-tight text-gray-950">€{{ number_format($commission, 0) }}</p>
|
||||
<p class="mt-2 text-sm text-gray-600">Comisión atribuible</p>
|
||||
<div class="min-w-[240px] rounded-3xl border border-gray-950/5 bg-gray-950 p-6 text-white shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.22em] text-white/60">KPI principal</p>
|
||||
<p class="mt-3 text-4xl font-semibold tracking-tight">€{{ number_format($commission, 0) }}</p>
|
||||
<div class="mt-3 flex items-center gap-2 text-sm text-white/70">
|
||||
<span>Comisión atribuible</span>
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center rounded-full border border-white/10 bg-white/5 text-[11px]" title="Suma de commission_amount en bookings generados por el funnel.">ℹ️</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ route('home') }}" class="inline-flex items-center justify-center rounded-2xl bg-primary-600 px-5 py-4 text-sm font-semibold text-white shadow-sm transition hover:bg-primary-500">
|
||||
Ver front demo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid gap-3 md:grid-cols-4">
|
||||
<article class="rounded-2xl border border-white/80 bg-white/85 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Taxi requests</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight text-gray-950">{{ number_format($rides) }}</p>
|
||||
<div class="mt-6 grid gap-3 md:grid-cols-2 xl:grid-cols-4">
|
||||
<article class="rounded-2xl border border-white/80 bg-white/90 p-4 shadow-sm">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Taxi</p>
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center rounded-full bg-gray-100 text-[11px] text-gray-500" title="Total de rides creados.">ℹ️</span>
|
||||
</div>
|
||||
<p class="mt-3 text-2xl font-semibold tracking-tight text-gray-950">{{ number_format($rides) }}</p>
|
||||
</article>
|
||||
<article class="rounded-2xl border border-white/80 bg-white/85 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">CTR view → click</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight text-gray-950">{{ $clickThrough }}%</p>
|
||||
<article class="rounded-2xl border border-white/80 bg-white/90 p-4 shadow-sm">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Bookings</p>
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center rounded-full bg-gray-100 text-[11px] text-gray-500" title="Reservas cerradas desde la demo.">ℹ️</span>
|
||||
</div>
|
||||
<p class="mt-3 text-2xl font-semibold tracking-tight text-gray-950">{{ number_format($bookingsCount) }}</p>
|
||||
</article>
|
||||
<article class="rounded-2xl border border-white/80 bg-white/85 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Bookings</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight text-gray-950">{{ number_format($bookingsCount) }}</p>
|
||||
<article class="rounded-2xl border border-white/80 bg-white/90 p-4 shadow-sm">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">CTR</p>
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center rounded-full bg-gray-100 text-[11px] text-gray-500" title="Relación view → click sobre las recomendaciones visibles.">ℹ️</span>
|
||||
</div>
|
||||
<p class="mt-3 text-2xl font-semibold tracking-tight text-gray-950">{{ $clickThrough }}%</p>
|
||||
</article>
|
||||
<article class="rounded-2xl border border-white/80 bg-white/85 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">GMV demo</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight text-gray-950">€{{ number_format($gmv, 0) }}</p>
|
||||
<article class="rounded-2xl border border-white/80 bg-white/90 p-4 shadow-sm">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">GMV</p>
|
||||
<span class="inline-flex h-6 w-6 items-center justify-center rounded-full bg-gray-100 text-[11px] text-gray-500" title="Volumen bruto de las reservas registradas.">ℹ️</span>
|
||||
</div>
|
||||
<p class="mt-3 text-2xl font-semibold tracking-tight text-gray-950">€{{ number_format($gmv, 0) }}</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 grid gap-3 xl:grid-cols-4">
|
||||
<article class="rounded-2xl border border-gray-200 bg-gray-950 p-4 text-white shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-white/60">1 · Taxi</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight">{{ number_format($rides) }}</p>
|
||||
<p class="mt-2 text-sm text-white/70">entradas al funnel</p>
|
||||
</article>
|
||||
<article class="rounded-2xl border border-amber-200 bg-amber-50/80 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-amber-800">2 · Contexto</p>
|
||||
<div class="mt-6 grid gap-3 md:grid-cols-3">
|
||||
<article class="rounded-2xl border border-gray-200 bg-white/80 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Espera útil</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight text-gray-950">{{ number_format($views) }}</p>
|
||||
<p class="mt-2 text-sm text-gray-600">views en espera útil</p>
|
||||
</article>
|
||||
<article class="rounded-2xl border border-teal-200 bg-teal-50/80 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-teal-800">3 · Oferta</p>
|
||||
<article class="rounded-2xl border border-gray-200 bg-white/80 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Detalle oferta</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight text-gray-950">{{ number_format($clicks) }}</p>
|
||||
<p class="mt-2 text-sm text-gray-600">clics a detalle</p>
|
||||
</article>
|
||||
<article class="rounded-2xl border border-emerald-200 bg-emerald-50/80 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-emerald-800">4 · Booking</p>
|
||||
<article class="rounded-2xl border border-gray-200 bg-white/80 p-4 shadow-sm">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Ride → booking</p>
|
||||
<p class="mt-2 text-2xl font-semibold tracking-tight text-gray-950">{{ $rideToBooking }}%</p>
|
||||
<p class="mt-2 text-sm text-gray-600">ride → booking</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<details class="mt-6 rounded-2xl border border-gray-200 bg-white/80 p-4">
|
||||
<summary class="cursor-pointer list-none text-sm font-semibold text-gray-900">ℹ️ Ver contexto ejecutivo</summary>
|
||||
<div class="mt-4 grid gap-3 md:grid-cols-3">
|
||||
<div class="rounded-2xl bg-gray-50 p-4">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Zona con más señal</p>
|
||||
<p class="mt-2 text-lg font-semibold text-gray-950">{{ $topZone }}</p>
|
||||
</div>
|
||||
<div class="rounded-2xl bg-gray-50 p-4 md:col-span-2">
|
||||
<p class="text-[11px] font-bold uppercase tracking-[0.18em] text-gray-500">Última reserva</p>
|
||||
<p class="mt-2 text-lg font-semibold text-gray-950">{{ $latestBooking?->offer?->title ?: 'Todavía no hay bookings' }}</p>
|
||||
<p class="mt-2 text-sm text-gray-600">La explicación profunda queda oculta por defecto para mantener el dashboard limpio.</p>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</x-filament::section>
|
||||
</x-filament-widgets::widget>
|
||||
|
||||
@ -11,19 +11,21 @@
|
||||
'activity' => 'Actividad',
|
||||
'service' => 'Servicio',
|
||||
];
|
||||
|
||||
$contextLine = \Illuminate\Support\Str::limit($offer->excerpt ?: 'Disponible para este trayecto ahora mismo.', 110);
|
||||
@endphp
|
||||
<section class="split">
|
||||
<article class="card offer-card">
|
||||
<div class="story-rail" aria-label="Progreso del funnel">
|
||||
<span class="story-chip is-done">1 · Taxi</span>
|
||||
<span class="story-chip is-done">2 · Contexto</span>
|
||||
<span class="story-chip is-done">2 · Confirmado</span>
|
||||
<span class="story-chip is-active">3 · Oferta</span>
|
||||
<span class="story-chip">4 · Reserva</span>
|
||||
</div>
|
||||
|
||||
<div class="phone-screen phone-screen--focused" style="padding:18px;">
|
||||
<div class="phone-topbar">
|
||||
<span>Propuesta contextual</span>
|
||||
<span>Detalle oferta</span>
|
||||
<span class="phone-dot-group" aria-hidden="true">
|
||||
<span class="phone-dot"></span>
|
||||
<span class="phone-dot"></span>
|
||||
@ -34,53 +36,47 @@
|
||||
<div class="offer-visual" aria-hidden="true"></div>
|
||||
|
||||
<div class="decision-primary">
|
||||
<span class="screen-badge">Detalle oferta</span>
|
||||
<span class="screen-badge">Oferta seleccionada</span>
|
||||
<h1 style="font-size:clamp(2.15rem,4vw,3.6rem);max-width:13ch;margin-top:14px;">{{ $offer->title }}</h1>
|
||||
<p class="screen-copy">{{ $contextLine }}</p>
|
||||
<div class="offer-meta">
|
||||
<span class="pill">{{ $categoryLabels[$offer->category] ?? ucfirst($offer->category) }}</span>
|
||||
@if($offer->location_label)<span class="pill">{{ $offer->location_label }}</span>@endif
|
||||
@if($offer->price_from)<span class="pill">Desde €{{ number_format((float) $offer->price_from, 0) }}</span>@endif
|
||||
@if($offer->price_from)<span class="pill">€{{ number_format((float) $offer->price_from, 0) }}</span>@endif
|
||||
@if($offer->duration_minutes)<span class="pill">{{ $offer->duration_minutes }} min</span>@endif
|
||||
@if($offer->available_now)<span class="pill">Disponible hoy</span>@endif
|
||||
<span class="pill">{{ $offer->available_now ? 'Disponible hoy' : 'Consultar disponibilidad' }}</span>
|
||||
</div>
|
||||
<div class="inline-actions">
|
||||
<div class="inline-actions inline-actions--stack-mobile">
|
||||
<a class="btn" href="#reserve">Reservar ahora</a>
|
||||
@if($recommendation)
|
||||
<button type="button" class="icon-button" data-modal-open="offer-signal" data-tooltip="Señal completa">Ver señal</button>
|
||||
<button type="button" class="btn btn-secondary" data-modal-open="offer-signal">ℹ️ Más info</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($offer->excerpt)
|
||||
<p class="screen-copy">{{ $offer->excerpt }}</p>
|
||||
@endif
|
||||
|
||||
@if($recommendation || $ride)
|
||||
<details class="disclosure disclosure--soft">
|
||||
<summary>
|
||||
<span>ℹ️ ¿Por qué aparece ahora?</span>
|
||||
<span>Abrir</span>
|
||||
</summary>
|
||||
<div class="disclosure-body">
|
||||
<ul class="accordion-list">
|
||||
@if($ride)
|
||||
<li>Llega después de un trayecto hacia <strong>{{ $ride->destination_label }}</strong>.</li>
|
||||
@endif
|
||||
@if($offer->location_label)
|
||||
<li>Está alineada con la zona <strong>{{ $offer->location_label }}</strong>.</li>
|
||||
@endif
|
||||
<li>{{ $recommendation?->reason ?: 'Se prioriza por proximidad, timing y facilidad de decisión.' }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
@endif
|
||||
<details class="disclosure disclosure--soft">
|
||||
<summary>
|
||||
<span>Más detalles</span>
|
||||
<span>Abrir</span>
|
||||
</summary>
|
||||
<div class="disclosure-body">
|
||||
<ul class="accordion-list">
|
||||
@if($offer->location_label)
|
||||
<li>Zona: <strong>{{ $offer->location_label }}</strong>.</li>
|
||||
@endif
|
||||
<li>Disponibilidad: <strong>{{ $offer->available_now ? 'Activa ahora' : 'Bajo confirmación' }}</strong>.</li>
|
||||
@if($offer->description)
|
||||
<li>{{ \Illuminate\Support\Str::limit($offer->description, 180) }}</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<aside class="card form-card" id="reserve">
|
||||
<span class="eyebrow">Reserva</span>
|
||||
<h2>Confirma y sigue.</h2>
|
||||
<p class="compact-lead">Solo pedimos lo necesario para cerrar la decisión.</p>
|
||||
<p class="compact-lead">Solo lo necesario para cerrar la decisión.</p>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="errors">
|
||||
@ -138,14 +134,14 @@
|
||||
|
||||
<details class="disclosure" style="margin-top:8px;">
|
||||
<summary>
|
||||
<span>ℹ️ Ver detalle de atribución</span>
|
||||
<span>ℹ️ Más info</span>
|
||||
<span>Abrir</span>
|
||||
</summary>
|
||||
<div class="disclosure-body">
|
||||
<ul class="accordion-list">
|
||||
<li>La reserva se vincula al trayecto si existe <code>ride_id</code>.</li>
|
||||
<li>Si vienes desde una recomendación, también queda guardada su posición.</li>
|
||||
<li>Eso permite enseñar conversión y comisión en admin sin ruido en la UI principal.</li>
|
||||
<li>La reserva se vincula al trayecto solo si llegas desde taxi confirmado.</li>
|
||||
<li>La atribución queda guardada sin ensuciar esta pantalla.</li>
|
||||
<li>La lectura de negocio vive en admin, no en el flujo de decisión.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
@ -157,8 +153,8 @@
|
||||
<div class="app-modal-card">
|
||||
<div class="app-modal-head">
|
||||
<div>
|
||||
<p class="eyebrow">Señal completa</p>
|
||||
<h2 id="offer-signal-title">Lectura contextual</h2>
|
||||
<p class="eyebrow">Más info</p>
|
||||
<h2 id="offer-signal-title">Por qué aparece esta oferta</h2>
|
||||
</div>
|
||||
<button type="button" class="app-modal-close" data-modal-close aria-label="Cerrar modal">Cerrar</button>
|
||||
</div>
|
||||
@ -168,12 +164,12 @@
|
||||
<span>posición</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $ride?->eta_minutes ?? '—' }}</strong>
|
||||
<span>ETA del taxi</span>
|
||||
<strong>{{ $offer->price_from ? '€'.number_format((float) $offer->price_from, 0) : '—' }}</strong>
|
||||
<span>precio visible</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $ride?->context_zone ?: ($offer->location_label ?: 'General') }}</strong>
|
||||
<span>zona activa</span>
|
||||
<strong>{{ $offer->duration_minutes ? $offer->duration_minutes.' min' : 'Flexible' }}</strong>
|
||||
<span>duración</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="accordion-list">
|
||||
@ -181,7 +177,9 @@
|
||||
@if($ride)
|
||||
<li>Trayecto vinculado: <strong>{{ $ride->pickup_label }}</strong> → <strong>{{ $ride->destination_label }}</strong>.</li>
|
||||
@endif
|
||||
<li>Oferta con ticket visible y reserva rápida.</li>
|
||||
@if($offer->location_label)
|
||||
<li>Zona relevante: <strong>{{ $offer->location_label }}</strong>.</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
@ -5,22 +5,22 @@
|
||||
|
||||
@section('content')
|
||||
@php
|
||||
$primaryRecommendation = $recommendations->first();
|
||||
$secondaryRecommendations = $recommendations->skip(1);
|
||||
$recommendationOptions = $recommendations->take(3);
|
||||
$primaryRecommendation = $recommendationOptions->first();
|
||||
$secondaryRecommendations = $recommendationOptions->skip(1);
|
||||
@endphp
|
||||
<section class="hero">
|
||||
<article class="card hero-copy hero-copy--compact">
|
||||
<div>
|
||||
<span class="eyebrow">Taxi confirmado</span>
|
||||
<h1>Tu taxi llega en {{ $ride->eta_minutes ?? 6 }} min.</h1>
|
||||
<p class="compact-lead">Decide una sola cosa más, si te interesa. El resto queda fuera.</p>
|
||||
<p class="compact-lead">Elige si quieres una parada útil antes de llegar.</p>
|
||||
</div>
|
||||
|
||||
<div class="meta-row">
|
||||
<span class="meta-pill">{{ $ride->pickup_label }}</span>
|
||||
<span class="meta-pill">{{ $ride->destination_label }}</span>
|
||||
<span class="meta-pill">{{ ucfirst($ride->source_channel) }}</span>
|
||||
<span class="meta-pill">{{ $ride->context_zone ?: 'Zona activa' }}</span>
|
||||
<div class="route-card">
|
||||
<small>Trayecto activo</small>
|
||||
<strong>{{ $ride->pickup_label }} → {{ $ride->destination_label }}</strong>
|
||||
<span>{{ ucfirst($ride->status) }} · {{ $ride->eta_minutes ?? 6 }} min</span>
|
||||
</div>
|
||||
|
||||
<div class="story-rail" aria-label="Progreso del funnel">
|
||||
@ -33,36 +33,33 @@
|
||||
@if ($primaryRecommendation)
|
||||
<div class="decision-card">
|
||||
<div class="decision-primary decision-primary--accent">
|
||||
<span class="screen-badge">Siguiente mejor opción</span>
|
||||
<span class="screen-badge">Recomendación principal</span>
|
||||
<h2 class="decision-title">{{ $primaryRecommendation->offer->title }}</h2>
|
||||
<p class="screen-copy">{{ $primaryRecommendation->offer->location_label ?: $ride->destination_label }} · Disponible ahora</p>
|
||||
<div class="offer-meta">
|
||||
@if($primaryRecommendation->offer->location_label)<span class="pill">{{ $primaryRecommendation->offer->location_label }}</span>@endif
|
||||
@if($primaryRecommendation->offer->price_from)<span class="pill">€{{ number_format((float) $primaryRecommendation->offer->price_from, 0) }}</span>@endif
|
||||
@if($primaryRecommendation->offer->duration_minutes)<span class="pill">{{ $primaryRecommendation->offer->duration_minutes }} min</span>@endif
|
||||
<span class="pill">Disponible ahora</span>
|
||||
<span class="pill">Top {{ $primaryRecommendation->position }}</span>
|
||||
</div>
|
||||
<div class="inline-actions">
|
||||
<div class="inline-actions inline-actions--stack-mobile">
|
||||
<a class="btn" href="{{ route('offers.show', $primaryRecommendation->offer->slug) }}?ride={{ $ride->id }}&recommendation={{ $primaryRecommendation->id }}">Ver y reservar</a>
|
||||
<button type="button" class="icon-button" data-modal-open="signal-primary" data-tooltip="Señal completa">Ver señal</button>
|
||||
<button type="button" class="btn btn-secondary" data-modal-open="signal-primary">ℹ️ Más info</button>
|
||||
<button type="button" class="icon-button" data-modal-open="impact-primary" data-tooltip="Ver impacto">Impacto</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details class="disclosure">
|
||||
<summary>
|
||||
<span>ℹ️ ¿Por qué esta opción?</span>
|
||||
<span>¿Por qué esta opción?</span>
|
||||
<span>Abrir</span>
|
||||
</summary>
|
||||
<div class="disclosure-body">
|
||||
<ul class="accordion-list">
|
||||
<li>Encaja con tu destino: <strong>{{ $ride->destination_label }}</strong>.</li>
|
||||
@if($primaryRecommendation->offer->location_label)
|
||||
<li>Está cerca de <strong>{{ $primaryRecommendation->offer->location_label }}</strong> y se puede decidir rápido.</li>
|
||||
@endif
|
||||
@if($primaryRecommendation->reason)
|
||||
<li>{{ $primaryRecommendation->reason }}</li>
|
||||
@else
|
||||
<li>La priorizamos por cercanía, disponibilidad y facilidad de cierre.</li>
|
||||
<li>Queda cerca de <strong>{{ $primaryRecommendation->offer->location_label }}</strong>.</li>
|
||||
@endif
|
||||
<li>{{ $primaryRecommendation->reason ?: 'La priorizamos por cercanía, disponibilidad y cierre rápido.' }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
@ -72,15 +69,15 @@
|
||||
<div class="app-modal-card">
|
||||
<div class="app-modal-head">
|
||||
<div>
|
||||
<p class="eyebrow">Señal completa</p>
|
||||
<h2 id="signal-primary-title">Por qué {{ $primaryRecommendation->offer->title }} sube arriba</h2>
|
||||
<p class="eyebrow">Más info</p>
|
||||
<h2 id="signal-primary-title">{{ $primaryRecommendation->offer->title }}</h2>
|
||||
</div>
|
||||
<button type="button" class="app-modal-close" data-modal-close aria-label="Cerrar modal">Cerrar</button>
|
||||
</div>
|
||||
<div class="metric-strip metric-strip--2">
|
||||
<div class="metric-cell">
|
||||
<strong>Top {{ $primaryRecommendation->position }}</strong>
|
||||
<span>posición actual</span>
|
||||
<span>prioridad</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $ride->eta_minutes ?? 6 }} min</strong>
|
||||
@ -88,13 +85,44 @@
|
||||
</div>
|
||||
</div>
|
||||
<ul class="accordion-list">
|
||||
<li>Trayecto activo: <strong>{{ $ride->pickup_label }}</strong> → <strong>{{ $ride->destination_label }}</strong>.</li>
|
||||
<li>Zona relevante: <strong>{{ $ride->context_zone ?: 'General' }}</strong>.</li>
|
||||
<li>Canal de origen: <strong>{{ ucfirst($ride->source_channel) }}</strong>.</li>
|
||||
<li>Trayecto: <strong>{{ $ride->pickup_label }}</strong> → <strong>{{ $ride->destination_label }}</strong>.</li>
|
||||
<li>Zona: <strong>{{ $ride->context_zone ?: 'General' }}</strong>.</li>
|
||||
<li>Canal: <strong>{{ ucfirst($ride->source_channel) }}</strong>.</li>
|
||||
<li>{{ $primaryRecommendation->reason ?: 'La propuesta destaca por contexto, proximidad y facilidad de reserva.' }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog class="app-modal" id="impact-primary" aria-labelledby="impact-primary-title">
|
||||
<div class="app-modal-card">
|
||||
<div class="app-modal-head">
|
||||
<div>
|
||||
<p class="eyebrow">Impacto</p>
|
||||
<h2 id="impact-primary-title">Qué mueve esta propuesta</h2>
|
||||
</div>
|
||||
<button type="button" class="app-modal-close" data-modal-close aria-label="Cerrar modal">Cerrar</button>
|
||||
</div>
|
||||
<div class="metric-strip metric-strip--3">
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $primaryRecommendation->offer->price_from ? '€'.number_format((float) $primaryRecommendation->offer->price_from, 0) : '—' }}</strong>
|
||||
<span>ticket visible</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ $primaryRecommendation->offer->duration_minutes ? $primaryRecommendation->offer->duration_minutes.' min' : 'Flexible' }}</strong>
|
||||
<span>duración</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<strong>{{ ucfirst($ride->source_channel) }}</strong>
|
||||
<span>origen del trayecto</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="accordion-list">
|
||||
<li>Se muestra cuando el trayecto ya está resuelto y la decisión es simple.</li>
|
||||
<li>La atribución y la lectura de negocio quedan fuera de la pantalla principal.</li>
|
||||
<li>Si el usuario reserva, el dashboard recoge el impacto sin añadir ruido aquí.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</dialog>
|
||||
@else
|
||||
<div class="notice">Todavía no hay una propuesta activa para este trayecto.</div>
|
||||
@endif
|
||||
@ -114,7 +142,7 @@
|
||||
<div class="route-card">
|
||||
<small>Trayecto activo</small>
|
||||
<strong>{{ $ride->pickup_label }} → {{ $ride->destination_label }}</strong>
|
||||
<span>ETA {{ $ride->eta_minutes ?? 6 }} min · {{ ucfirst($ride->source_channel) }} · {{ $ride->context_zone ?: 'Zona activa' }}</span>
|
||||
<span>ETA {{ $ride->eta_minutes ?? 6 }} min · {{ ucfirst($ride->status) }}</span>
|
||||
</div>
|
||||
|
||||
@if ($primaryRecommendation)
|
||||
@ -123,11 +151,11 @@
|
||||
<strong class="phone-action-title">{{ $primaryRecommendation->offer->title }}</strong>
|
||||
<span>Top {{ $primaryRecommendation->position }}</span>
|
||||
</div>
|
||||
<small>Disponible ahora</small>
|
||||
<small>{{ $primaryRecommendation->offer->location_label ?: 'Disponible ahora' }}</small>
|
||||
<div class="phone-action-meta">
|
||||
@if($primaryRecommendation->offer->location_label)<span>{{ $primaryRecommendation->offer->location_label }}</span>@endif
|
||||
@if($primaryRecommendation->offer->price_from)<span>€{{ number_format((float) $primaryRecommendation->offer->price_from, 0) }}</span>@endif
|
||||
@if($primaryRecommendation->offer->duration_minutes)<span>{{ $primaryRecommendation->offer->duration_minutes }} min</span>@endif
|
||||
<span>Disponible</span>
|
||||
</div>
|
||||
<a class="btn" href="{{ route('offers.show', $primaryRecommendation->offer->slug) }}?ride={{ $ride->id }}&recommendation={{ $primaryRecommendation->id }}">Ver y reservar</a>
|
||||
</article>
|
||||
@ -142,9 +170,9 @@
|
||||
<span>Top {{ $recommendation->position }}</span>
|
||||
</div>
|
||||
<div class="phone-action-meta">
|
||||
@if($recommendation->offer->location_label)<span>{{ $recommendation->offer->location_label }}</span>@endif
|
||||
@if($recommendation->offer->price_from)<span>€{{ number_format((float) $recommendation->offer->price_from, 0) }}</span>@endif
|
||||
@if($recommendation->offer->duration_minutes)<span>{{ $recommendation->offer->duration_minutes }} min</span>@endif
|
||||
<span>{{ $recommendation->offer->location_label ?: 'Disponible ahora' }}</span>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
@ -158,8 +186,8 @@
|
||||
<section class="section">
|
||||
<div class="section-head section-head--compact">
|
||||
<div>
|
||||
<span class="eyebrow">Más opciones</span>
|
||||
<h2>Solo si quieres comparar</h2>
|
||||
<span class="eyebrow">Otras opciones</span>
|
||||
<h2>Solo 2 más para comparar</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -169,17 +197,18 @@
|
||||
<div>
|
||||
<div class="offer-meta">
|
||||
<span class="pill">Top {{ $recommendation->position }}</span>
|
||||
@if($recommendation->offer->location_label)<span class="pill">{{ $recommendation->offer->location_label }}</span>@endif
|
||||
@if($recommendation->offer->price_from)<span class="pill">€{{ number_format((float) $recommendation->offer->price_from, 0) }}</span>@endif
|
||||
@if($recommendation->offer->duration_minutes)<span class="pill">{{ $recommendation->offer->duration_minutes }} min</span>@endif
|
||||
</div>
|
||||
<h3>{{ $recommendation->offer->title }}</h3>
|
||||
<p class="screen-copy">{{ $recommendation->offer->location_label ?: 'Disponible ahora' }}</p>
|
||||
</div>
|
||||
<div class="inline-actions inline-actions--stack-mobile">
|
||||
<a class="btn btn-secondary" href="{{ route('offers.show', $recommendation->offer->slug) }}?ride={{ $ride->id }}&recommendation={{ $recommendation->id }}">Ver detalle</a>
|
||||
<a class="btn btn-secondary" href="{{ route('offers.show', $recommendation->offer->slug) }}?ride={{ $ride->id }}&recommendation={{ $recommendation->id }}">Ver y reservar</a>
|
||||
</div>
|
||||
<details class="disclosure disclosure--soft">
|
||||
<summary>
|
||||
<span>ℹ️ Ver motivo</span>
|
||||
<span>ℹ️ Más info</span>
|
||||
<span>Abrir</span>
|
||||
</summary>
|
||||
<div class="disclosure-body">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user