refactor(livewire): organizar componentes y vistas por dominio en subnamespaces

- app/Livewire: 34 componentes agrupados en Issues/, Projects/, Phases/,
  Companies/, Users/, Admin/, Inspections/, Layers/, Media/, Common/
  (Client/, Reports/, Forms/, Actions/ ya estaban). Namespaces actualizados.
- resources/views/livewire: vistas sueltas movidas a subcarpetas espejo
  (companies/, users/, phases/, roles/, inspections/, media/, common/);
  render() actualizado.
- Referencias actualizadas sin romper nada: rutas (FQN, nombres de ruta intactos),
  tags <livewire:...>/@livewire() a alias con punto, y use de los tests.
- No tocado: Volt de Breeze (auth/profile/navigation), y el portal cliente
  (user-nav/client-projects) que ya tenía referencias inconsistentes.

Verificado: 69 rutas OK, vistas compilan, suite 69 passing (solo 2 pre-existentes
sqlite). autoload regenerado con --ignore-platform-reqs (PHP 8.2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 16:54:09 +02:00
parent 9c164bb7ef
commit 7d390872c3
68 changed files with 191 additions and 107 deletions
@@ -21,7 +21,7 @@
{{ __('New Company') }}
</a>
</div>
<livewire:company-table />
<livewire:companies.company-table />
</div>
</div>
@@ -60,5 +60,5 @@
{{-- ================================================================
ISSUES TABLE (Rappasoft)
================================================================ --}}
<livewire:issue-table :project-id="$project->id" :key="'issue-table-'.$project->id" />
<livewire:issues.issue-table :project-id="$project->id" :key="'issue-table-'.$project->id" />
</div>
@@ -60,12 +60,12 @@ new class extends Component
<!-- Language Switcher -->
<div class="hidden sm:flex sm:items-center sm:ms-6">
@livewire('language-switcher')
@livewire('common.language-switcher')
</div>
<!-- Notification Bell -->
<div class="hidden sm:flex sm:items-center sm:ms-2">
@livewire('notification-bell')
@livewire('common.notification-bell')
</div>
<!-- Settings Dropdown -->
@@ -127,7 +127,7 @@ new class extends Component
<div class="mt-3 space-y-1">
<div class="mb-2 text-center text-sm">
@livewire('language-switcher')
@livewire('common.language-switcher')
</div>
<x-responsive-nav-link :href="route('profile')" wire:navigate>
{{ __('Profile') }}
@@ -12,7 +12,7 @@
</div>
{{-- Tabla Rappasoft de fases --}}
<livewire:phase-table :project-id="$project->id" :key="'phase-table-'.$project->id" />
<livewire:phases.phase-table :project-id="$project->id" :key="'phase-table-'.$project->id" />
{{-- ================================================================
MODAL crear / editar fase
@@ -25,5 +25,5 @@
@endcan
{{-- Tabla Rappasoft de empresas asignadas --}}
<livewire:project-companies-table :project-id="$project->id" :key="'project-companies-table-'.$project->id" />
<livewire:projects.project-companies-table :project-id="$project->id" :key="'project-companies-table-'.$project->id" />
</div>
@@ -21,13 +21,13 @@
@include('livewire.projects.partials.project-data-form')
</div>
<div x-show="tab==='phases'" x-cloak>
<livewire:phase-list :project="$project" :key="'phases-'.$project->id" />
<livewire:phases.phase-list :project="$project" :key="'phases-'.$project->id" />
</div>
<div x-show="tab==='users'" x-cloak>
<livewire:project-users :project="$project" :key="'users-'.$project->id" />
<livewire:projects.project-users :project="$project" :key="'users-'.$project->id" />
</div>
<div x-show="tab==='companies'" x-cloak>
<livewire:project-companies :project="$project" :key="'companies-'.$project->id" />
<livewire:projects.project-companies :project="$project" :key="'companies-'.$project->id" />
</div>
</div>
@else
@@ -30,7 +30,7 @@
📎 {{ __("Files of element") }}
</summary>
<div class="p-2">
@livewire('media-manager', [
@livewire('media.media-manager', [
'mediableType' => 'App\\Models\\Feature',
'mediableId' => $selectedFeature->id,
], key('media-feature-' . $selectedFeature->id))
@@ -181,7 +181,7 @@
<x-heroicon-o-paper-clip class="w-4 h-4 inline" /> {{ __('Files of element') }}
</summary>
<div class="p-2">
@livewire('media-manager', [
@livewire('media.media-manager', [
'mediableType' => 'App\\Models\\Feature',
'mediableId' => $selectedFeature->id,
], key('media-feature-' . $selectedFeature->id))
@@ -351,7 +351,7 @@
@endif
@elseif($activeTab === 'issues')
<!-- Issues tab: render embedded IssueManager component -->
@livewire('issue-manager', ['project' => $project], key('issues-tab-' . $project->id))
@livewire('issues.issue-manager', ['project' => $project], key('issues-tab-' . $project->id))
@endif
</div>
@@ -25,5 +25,5 @@
@endcan
{{-- Tabla Rappasoft de usuarios asignados --}}
<livewire:project-users-table :project-id="$project->id" :key="'project-users-table-'.$project->id" />
<livewire:projects.project-users-table :project-id="$project->id" :key="'project-users-table-'.$project->id" />
</div>
@@ -320,6 +320,90 @@
</div>
</div>
{{-- Validez y estado --}}
<div class="card bg-base-100 shadow">
<div class="card-body p-6">
<h3 class="font-semibold text-base flex items-center gap-2 mb-4">
<x-heroicon-o-calendar-days class="w-5 h-5 text-primary" />
Validez de acceso
</h3>
<div class="space-y-3 text-sm">
<div class="flex justify-between items-center py-2 border-b border-base-200">
<span class="text-gray-500">Estado</span>
<span class="badge {{ $statusBadge[0] }}">{{ $statusBadge[1] }}</span>
</div>
<div class="flex justify-between items-center py-2 border-b border-base-200">
<span class="text-gray-500">Válido desde</span>
<span class="font-medium">
{{ $user->valid_from ? $user->valid_from->format('d/m/Y') : '— (sin límite)' }}
</span>
</div>
<div class="flex justify-between items-center py-2 border-b border-base-200">
<span class="text-gray-500">Válido hasta</span>
<span class="font-medium {{ isset($isExpired) && $isExpired ? 'text-error font-bold' : '' }}">
{{ $user->valid_until ? $user->valid_until->format('d/m/Y') : '— (sin límite)' }}
</span>
</div>
<div class="flex justify-between items-center py-2">
<span class="text-gray-500">Email verificado</span>
@if($user->email_verified_at)
<span class="flex items-center gap-1 text-success text-xs font-medium">
<x-heroicon-o-check-circle class="w-4 h-4" />
{{ $user->email_verified_at->format('d/m/Y') }}
</span>
@else
<span class="text-warning text-xs flex items-center gap-1">
<x-heroicon-o-clock class="w-4 h-4" />
Pendiente
</span>
@endif
</div>
</div>
</div>
</div>
{{-- Empresa --}}
@if($user->company)
<div class="card bg-base-100 shadow md:col-span-2">
<div class="card-body p-6">
<h3 class="font-semibold text-base flex items-center gap-2 mb-3">
<x-heroicon-o-building-office-2 class="w-5 h-5 text-primary" />
Empresa
</h3>
<div class="flex items-center gap-4">
@if($user->company->logo_path && \Illuminate\Support\Facades\Storage::disk('public')->exists($user->company->logo_path))
<img src="{{ \Illuminate\Support\Facades\Storage::disk('public')->url($user->company->logo_path) }}"
class="w-14 h-14 object-contain border border-base-300 rounded-lg" alt="" />
@else
<div class="w-14 h-14 bg-base-200 rounded-lg flex items-center justify-center">
<x-heroicon-o-building-office class="w-7 h-7 opacity-30" />
</div>
@endif
<div>
<p class="font-semibold">{{ $user->company->name }}</p>
@if($user->company->apodo)
<p class="text-sm text-gray-500">{{ $user->company->apodo }}</p>
@endif
@if($user->company->email)
<p class="text-xs text-gray-400">{{ $user->company->email }}</p>
@endif
</div>
@php
$typeBadge = match($user->company->type) {
'owner' => ['badge-success', 'Promotor'],
'constructor' => ['badge-primary', 'Constructor'],
'subcontractor' => ['badge-secondary','Subcontratista'],
'consultant' => ['badge-info', 'Consultor'],
'supplier' => ['badge-warning', 'Proveedor'],
default => ['badge-ghost', 'Otro'],
};
@endphp
<span class="badge {{ $typeBadge[0] }} ml-auto">{{ $typeBadge[1] }}</span>
</div>
</div>
</div>
@endif
{{-- Permisos directos del usuario --}}
<div class="card bg-base-100 shadow md:col-span-2">
<div class="card-body p-6">