@push('styles') @endpush @php // Los counts ahora se basan en TODOS los proyectos del user // (no solo los del filtro activo) — calculados en el controller. $totalProjects = $allProjectsCount; $activeCount = $statusCounts['active'] ?? 0; $pendingCount = ($statusCounts['required'] ?? 0) + ($statusCounts['on_development'] ?? 0); $failedCount = $statusCounts['failed'] ?? 0; $pausedCount = $statusCounts['paused'] ?? 0; $archivedCount = $statusCounts['archived'] ?? 0; // Lista de proyectos con problema (failed) — para el banner top y CTA Atención. $failedProjects = $projects->where('pipeline_status', \App\Models\TenantProject::STATUS_FAILED); // Total mensual: monetario sigue basándose en el filtro visible (lo que // el cliente ve abajo). Si todos están en trial, mostramos el monto // futuro (cuando salgan de trial) para que sepa cuánto va a pagar. $monthlyActive = $projects->where('pipeline_status', 'active')->sum('amount_usd'); $monthlyPotential = $projects->whereNotIn('pipeline_status', ['paused', 'failed', 'archived']) ->sum('amount_usd'); $earliestTrial = $projects->whereNotNull('trial_ends_at') ->filter(fn($p) => $p->trial_ends_at && $p->trial_ends_at->isFuture()) ->sortBy('trial_ends_at') ->first(); @endphp
@php // Welcome screen persistente. Aparece para: // · Users fresh (< 24hs) con proyecto en provisión → modo "in-progress" // · Users fresh sin ningún proyecto → modo "no-projects" (3 pasos) // · Cualquier user con ?welcome=1 manual // Se oculta automáticamente cuando el primer proyecto está active // o con ?hide-welcome=1 (dismiss explícito). $isFreshUser = $user->created_at && $user->created_at->isAfter(now()->subDay()); $hasInProgress = $projects->whereIn('pipeline_status', ['required', 'on_development'])->isNotEmpty(); $hasNoProjects = $allProjectsCount === 0; $showWelcome = (request('welcome') || ($isFreshUser && ($hasInProgress || $hasNoProjects))) && !request('hide-welcome'); $onboardingMode = $hasNoProjects ? 'no-projects' : 'in-progress'; $firstProject = $projects->first(); @endphp @if($showWelcome && $onboardingMode === 'no-projects')

¡Bienvenido a BewPro, {{ $user->first_name ?: explode('@', $user->email)[0] }}!

Acá vas a gestionar tus sitios web, suscripciones y soporte. Te dejamos los próximos pasos para arrancar.

1
Completá tu perfil

Datos personales y de facturación para que tu sitio se entregue a tiempo.

Completar perfil →
2
Explorá el catálogo

+250 modelos por industria. Elegí el que mejor representa tu negocio.

Ver productos →
3
¿Dudas? Estamos acá

Revisá el Centro de Ayuda o abrí un ticket si necesitás asistencia personalizada.

Centro de Ayuda →
@elseif($showWelcome) @php $pct = 15; $etaText = 'Preparando...'; if ($firstProject) { list($pct, $etaText) = $firstProject->provisioning_eta; } @endphp

¡Tu sitio se está preparando, {{ $user->first_name ?: explode('@', $user->email)[0] }}!

El aprovisionamiento técnico completo toma entre 20 y 30 minutos.

{{-- Columna Izquierda: Aprovisionamiento técnico --}}
Progreso de la Instalación
{{ $etaText }} {{ $pct }}%
  • @if($pct >= 20) Base de datos configurada y credenciales de acceso listas @else Configurando base de datos segura... @endif
  • @if($pct >= 40) Certificado de seguridad SSL (HTTPS) registrado @elseif($pct >= 20) Registrando certificado de seguridad SSL... @else Instalando certificado de seguridad SSL @endif
  • @if($pct >= 70) Plantilla de diseño deluxe y código clonado @elseif($pct >= 40) Copiando archivos del modelo deluxe... @else Copiando archivos del modelo deluxe @endif
  • @if($pct >= 90) Optimizaciones de velocidad y SEO aplicadas @elseif($pct >= 70) Aplicando optimizaciones de velocidad de carga... @else Aplicando optimizaciones de velocidad de carga @endif
  • @if($pct >= 100) Panel de administración en línea y listo para usar @elseif($pct >= 90) Creando credenciales finales del administrador... @else Creando credenciales finales del administrador @endif
{{-- Columna Derecha: Onboarding "Mientras Esperas..." --}}
Mientras tanto... ¡Prepárate!
Tareas preparatorias
  • Prepara el logo de tu marca (PNG transparente).
  • Escribe textos cortos de tus 3 servicios.
  • Copia los links de tus redes sociales.
{{-- Thumbnail de video simulado --}}
Video: Primeros Pasos en 2m Ver tutorial rápido
Completar mi Perfil Centro de Ayuda @php $cleanPhone = preg_replace('/[^0-9]/', '', config('site.contact.phone', '')); @endphp @if($cleanPhone) Chat con Asesor @endif
{{-- Modal de Video Onboarding --}} @endif {{-- Atención requerida — proyectos con provisión fallada. Aparece arriba de cualquier otro banner. Cada item linkea al detalle del proyecto + soporte. Si el sync trajo info de error en prospect_email/name lo mostramos como contexto. --}} @if($failedCount > 0)
Atención requerida — {{ $failedCount }} {{ $failedCount === 1 ? 'proyecto necesita' : 'proyectos necesitan' }} intervención

La provisión no terminó correctamente. Revisá el detalle y abrí un ticket de soporte para que lo destrabemos.

@foreach($failedProjects as $failedProject)
{{ $failedProject->project_name }} @if($failedProject->product_name) {{ $failedProject->product_label }} @endif @if($failedProject->prospect_name || $failedProject->prospect_email) {{ $failedProject->prospect_name ?: $failedProject->prospect_email }} @endif · alta {{ $failedProject->created_at?->format('d/m/Y') }} @if($failedProject->created_at) ({{ $failedProject->created_at->diffForHumans() }}) @endif
@endforeach
@endif {{-- Trial expiring banner (Bloque B). Aparece si algún proyecto tiene trial_ends_at en los próximos 7 días. CTA al Stripe Billing Portal si el customer tiene billing usable; si no, deriva a soporte. --}} @if($trialEndingSoon->isNotEmpty())
Tu trial termina pronto

Para mantener tu sitio activo, configurá un método de pago antes de que termine el período de prueba.

@foreach($trialEndingSoon as $trialProject)
{{ $trialProject->project_name }} @if($trialProject->trial_days_left === 0) termina hoy @elseif($trialProject->trial_days_left === 1) termina mañana @else termina en {{ $trialProject->trial_days_left }} días @endif ({{ $trialProject->trial_ends_at->format('d/m/Y') }})
@if($trialProject->hasUsableBilling())
@csrf
@else Contactar soporte @endif
@endforeach
@endif @if(request('verified'))
Tu email fue verificado correctamente.
@endif {{-- Banner soft de captura de datos: aparece si hay proyectos activos sin prospect real / asesor / tier. Click → linkea al primer incompleto. Sólo se muestra cuando hay incompletos; no compite con failed/trial. --}} @if(($incompleteCount ?? 0) > 0)
{{ $incompleteCount }} {{ $incompleteCount === 1 ? 'proyecto necesita' : 'proyectos necesitan' }} datos de cliente / asesor / tier. Completalos para tener tracking comercial al día.
@php // Primer proyecto incompleto para deep-link al editor. $firstIncomplete = $projects->first(function ($p) use ($user) { if (!in_array($p->pipeline_status, [\App\Models\TenantProject::STATUS_ACTIVE, \App\Models\TenantProject::STATUS_ON_DEVELOPMENT], true)) return false; return empty($p->prospect_email) || $p->prospect_email === $user->email || empty($p->closer_user_id) || empty($p->tier); }); @endphp @if($firstIncomplete) Empezar por {{ \Illuminate\Support\Str::limit($firstIncomplete->project_name, 30) }} @endif
@endif @if(session('error'))
{{ session('error') }}
@endif @if(session('success'))
{{ session('success') }}
@endif {{-- Stats Bar --}}
{{ $totalProjects }}
Total Proyectos
{{ $activeCount }}
Activos
@if($inDelivery->count() > 0)
{{ $inDelivery->count() }} en entrega
@endif @if($onHold->count() > 0)
{{ $onHold->count() }} en pausa
@endif @if($showcase->count() > 0)
{{ $showcase->count() }} showcase
@endif
@if($pendingCount > 0) @endif @if($failedCount > 0) @endif @if($pausedCount > 0) @endif @if($archivedCount > 0) @endif
USD {{ number_format($monthlyActive, 2) }}
Total mensual @if($monthlyActive == 0 && $monthlyPotential > 0 && $earliestTrial) USD {{ number_format($monthlyPotential, 2) }} desde {{ $earliestTrial->trial_ends_at->format('d/m/Y') }} @endif
{{-- ════════ EN PROCESO DE ENTREGA ════════ Proyectos pipeline_status=active pero con handoff comercial pendiente (dominio custom, módulos extra, ajustes finales). Sección destacada para que CD no los pierda de vista entre los activos "ya entregados". --}} @php // Configuración de las 3 secciones destacadas. Reusamos el mismo // markup variando color/copy según el estado. $deliverySections = [ ['key' => 'in_delivery', 'collection' => $inDelivery, 'title' => 'En entrega · Requieren tu atención', 'description' => 'Sitios online con detalles del handoff pendientes (dominio custom, módulos extra, ajustes con el cliente).', 'icon' => 'fa-truck-fast', 'tone' => 'info', 'border' => 'border-info', 'gradient' => 'rgba(13,202,240,0.06), rgba(13,202,240,0.02)', 'badgeBg' => 'bg-info', 'btnBg' => 'btn-info', 'btnLight' => 'btn-light-info', 'open' => true], ['key' => 'on_hold', 'collection' => $onHold, 'title' => 'En pausa', 'description' => 'Sitios activos en standby — no se está trabajando ahora.', 'icon' => 'fa-pause-circle', 'tone' => 'warning', 'border' => 'border-warning', 'gradient' => 'rgba(255,193,7,0.07), rgba(255,193,7,0.02)', 'badgeBg' => 'bg-warning text-dark', 'btnBg' => 'btn-warning text-dark', 'btnLight' => 'btn-light-warning', 'open' => false], ['key' => 'showcase', 'collection' => $showcase, 'title' => 'Showcase / Anzuelos', 'description' => 'Sitios activos sin cliente comprometido — vivos como demo o captación de leads.', 'icon' => 'fa-bullseye', 'tone' => 'showcase', 'border' => 'cd-border-showcase border-2', 'gradient' => 'rgba(124,77,255,0.06), rgba(124,77,255,0.02)', 'badgeBg' => 'cd-bg-showcase', 'btnBg' => 'cd-bg-showcase border-0', 'btnLight' => 'cd-bg-light-showcase border-0', 'open' => false], ]; @endphp @foreach($deliverySections as $section) @continue($section['collection']->isEmpty()) @php $isShowcase = $section['key'] === 'showcase'; $isInDelivery = $section['key'] === 'in_delivery'; @endphp
{{ $section['title'] }}

{{ $section['description'] }}

{{ $section['collection']->count() }} {{ $section['collection']->count() === 1 ? 'proyecto' : 'proyectos' }}
@foreach($section['collection'] as $deliveryProject)
@php $deliveryAge = $deliveryProject->updated_at ?? $deliveryProject->created_at; @endphp
{{ $deliveryProject->project_name }} @if($deliveryAge) {{ $deliveryAge->diffForHumans(null, true) }} @endif
@if($deliveryProject->product_name) {{ $deliveryProject->product_label }} @endif @if($deliveryProject->created_at) · alta {{ $deliveryProject->created_at->format('d/m/Y') }} @endif
@if($deliveryProject->domain) {{ $deliveryProject->domain }} @endif @if($deliveryProject->delivery_notes)
{{ \Illuminate\Support\Str::limit($deliveryProject->delivery_notes, 80) }}
@elseif($isInDelivery)
Sin notas — agregá el motivo
@else
Sin notas
@endif
@if($deliveryProject->admin_url) Admin @endif
@endforeach
@endforeach {{-- Card principal --}}
{{-- Sub-header con greeting compacto. El subtítulo "Gestioná..." se elimina en mobile (ya está en el page-header de arriba). --}}

Hola {{ $user->first_name ?: explode('@', $user->email)[0] }} 👋 Gestioná tus sitios, suscripciones y reembolsos desde acá.

{{-- Mobile: solo íconos para ahorrar ancho. Desktop: texto completo. --}} Soporte Mis pagos
{{-- Status filters (Bloque D). Solo se muestran si el cliente tiene >= 2 proyectos — pattern del Reseller. --}} @if($allProjectsCount >= 2) @php $filters = [ '' => ['label' => 'Todos', 'count' => $allProjectsCount, 'icon' => 'fa-list', 'tone' => 'default', 'delivery' => null], 'active' => ['label' => 'Activos', 'count' => $statusCounts['active'] ?? 0, 'icon' => 'fa-check-circle', 'tone' => 'default', 'delivery' => null], 'on_development' => ['label' => 'En desarrollo', 'count' => $statusCounts['on_development'] ?? 0, 'icon' => 'fa-cog', 'tone' => 'default', 'delivery' => null], 'required' => ['label' => 'Preparando', 'count' => $statusCounts['required'] ?? 0, 'icon' => 'fa-clock', 'tone' => 'default', 'delivery' => null], 'failed' => ['label' => 'Con problema', 'count' => $statusCounts['failed'] ?? 0, 'icon' => 'fa-triangle-exclamation','tone' => 'danger', 'delivery' => null], 'in_delivery' => ['label' => 'En entrega', 'count' => $inDelivery->count(), 'icon' => 'fa-truck-fast', 'tone' => 'info', 'delivery' => 'in_delivery'], 'on_hold' => ['label' => 'En pausa', 'count' => $onHold->count(), 'icon' => 'fa-pause-circle', 'tone' => 'warning', 'delivery' => 'on_hold'], 'showcase' => ['label' => 'Showcase', 'count' => $showcase->count(), 'icon' => 'fa-bullseye', 'tone' => 'showcase', 'delivery' => 'showcase'], 'paused' => ['label' => 'Suspendidos', 'count' => $statusCounts['paused'] ?? 0, 'icon' => 'fa-pause-circle', 'tone' => 'default', 'delivery' => null], 'archived' => ['label' => 'Archivados', 'count' => $statusCounts['archived'] ?? 0, 'icon' => 'fa-archive', 'tone' => 'default', 'delivery' => null], ]; @endphp
@foreach($filters as $key => $f) @continue($key !== '' && $f['count'] === 0) @php $isActive = $f['delivery'] ? ($deliveryFilter ?? '') === $f['delivery'] : (($statusFilter ?? '') === $key && !$deliveryFilter); // Mapeo de tone → clases del chip (light bg con accent color). $toneClasses = [ 'default' => ['btn' => 'btn-light', 'badge' => 'bg-light-primary text-primary'], 'danger' => ['btn' => 'btn-light-danger', 'badge' => 'bg-danger text-white'], 'info' => ['btn' => 'btn-light-info', 'badge' => 'bg-info text-white'], 'warning' => ['btn' => 'btn-light-warning', 'badge' => 'bg-warning text-dark'], 'showcase' => ['btn' => 'cd-bg-light-showcase border-0', 'badge' => 'cd-bg-showcase text-white'], ]; $tone = $toneClasses[$f['tone']] ?? $toneClasses['default']; $btnClass = $isActive ? 'btn-primary' : $tone['btn']; $badgeClass = $isActive ? 'bg-white text-primary' : $tone['badge']; $url = $f['delivery'] ? route('client.projects.index', ['delivery' => $f['delivery']]) : ($key === '' ? route('client.projects.index') : route('client.projects.index', ['status' => $key])); @endphp {{ $f['label'] }} {{ $f['count'] }} @endforeach
@endif @if($projects->isEmpty() && $statusFilter)
Sin proyectos con ese filtro

Ningún proyecto coincide con el estado seleccionado.

Ver todos
@elseif($projects->isEmpty())

Todavía no tenés proyectos activos

Cuando comiences un proyecto, va a aparecer acá automáticamente.

Comenzar un nuevo proyecto
@else
@foreach($projects as $project) @php [$badgeLabel, $badgeClass] = $project->status_badge; @endphp @php $rowClass = match ($project->delivery_status) { \App\Models\TenantProject::DELIVERY_IN_DELIVERY => 'cd-row-in-delivery', \App\Models\TenantProject::DELIVERY_ON_HOLD => 'cd-row-on-hold', \App\Models\TenantProject::DELIVERY_SHOWCASE => 'cd-row-showcase', default => '', }; [$deliveryLabel, $deliveryBadgeClass, $deliveryIcon] = $project->delivery_badge; @endphp @endforeach
Proyecto Cliente final Estado Sitio Plan Activación Acciones
{{ $project->project_name }} @if($project->product_name) {{ $project->product_label }} @endif @if(!$project->delivery_status || $project->delivery_status === \App\Models\TenantProject::DELIVERY_COMPLETED) {{-- no badge para completed (es el estado default) --}} @else {{ $deliveryLabel }} @endif @if($project->trial_ends_at && $project->trial_ends_at->isFuture()) Trial hasta {{ $project->trial_ends_at->format('d/m/Y') }} @endif
@php // Mostrar prospect info solo cuando aporta — si prospect_name = project_name // y prospect_email = email del user logueado, no aporta info nueva. $showName = $project->prospect_name && trim($project->prospect_name) !== trim($project->project_name); $showEmail = $project->prospect_email && $project->prospect_email !== $user->email; @endphp @if($showName || $showEmail)
@if($showName) {{ $project->prospect_name }} @endif @if($showEmail) {{ $project->prospect_email }} @endif
@else @endif
{{ $badgeLabel }} @if($project->isProvisioning()) @php [$pct, $etaText] = $project->provisioning_eta; @endphp
{{ $etaText }}
@endif
@if($project->domain && $project->isReachable()) {{ $project->domain }} @if(!$project->isLive()) en desarrollo @endif {{-- Botón "Abrir admin" — atajo al panel de admin del sitio del cliente. Solo si sitio responde y pipeline_status=active (no on_development). --}} @if($project->isLive()) @endif @elseif($project->domain) {{ $project->domain }} @elseif($project->pipeline_status === \App\Models\TenantProject::STATUS_FAILED) Provisionado falló @elseif($project->isProvisioning()) Provisionando… @else Sin dominio asignado @endif @if($project->amount_usd > 0) USD {{ number_format($project->amount_usd, 2) }} /mes @else Canje @endif @php // Antigüedad: fecha de alta + tiempo desde alta. Mostramos created_at // siempre (es el dato estable) y last_synced_at como freshness check // si difiere significativamente. $alta = $project->created_at; $sync = $project->last_synced_at; $isStale = $sync && $sync->lt(now()->subHours(2)); @endphp @if($alta) {{ $alta->format('d/m/Y') }} {{ $alta->diffForHumans() }} @if($project->isProvisioning() && $alta->lt(now()->subHour())) {{ $alta->diffForHumans(now(), \Carbon\CarbonInterface::DIFF_ABSOLUTE) }} en {{ $project->status_badge[0] }} @elseif($sync && $isStale) sync {{ $sync->diffForHumans() }} @endif @else @endif
{{-- ACCIÓN PRIMARIA: entrar al admin del sitio. En mobile pasa a full-width para ser tappable. --}} @if($project->isReachable() && $project->admin_url) Entrar al admin @elseif($project->isProvisioning()) Provisionando... @endif {{-- Acciones secundarias: detalle + delivery + billing portal --}} {{-- Marcar entrega: solo si el proyecto es active/on_dev (no tiene sentido sobre failed/archived). --}} @if(in_array($project->pipeline_status, [\App\Models\TenantProject::STATUS_ACTIVE, \App\Models\TenantProject::STATUS_ON_DEVELOPMENT], true)) @endif @if($project->hasUsableBilling())
@csrf
@endif {{-- Borrar: solo visible si la policy delete() autoriza (failed/archived/paused/required + owner o reseller). Active queda explicitamente excluido. --}} @can('delete', $project) @endcan
@endif
{{-- ════════ Modal: marcar / editar estado de entrega ════════ Reutilizado por la tabla principal y por las cards de "En entrega". Se popula vía data-* en el botón trigger. --}} {{-- ════════ Modal: borrar proyecto desde la tabla ════════ Reusado por cualquier botón trash en filas (failed/archived/paused/required). Populated via data-* en el botón trigger. --}} @push('scripts') @endpush