feat: profile_photo en contratos activos, notificaciones bilingues en reportes y foto en panel usuarios

- API contratos activos (cliente y proveedor): agrega campo profile_photo (técnico asignado o supplier, null si no tiene)
- Notificaciones push bilingues en comentarios de reporte: "Nueva actividad en tu reporte" / "New activity on your report"
- Moderador en web panel notifica a cliente y proveedor al comentar, con prefijo Moderador/Moderator
- Panel usuarios: columna de foto de perfil entre ID y Nombre

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 17:17:14 -06:00
parent 38da1b56ce
commit 0ed98c96ee
40 changed files with 1390 additions and 651 deletions

View File

@@ -21,11 +21,13 @@
gap: 1rem;
position: relative;
}
.chat-info .btn-verdict {
.chat-info .btn-actions {
position: absolute;
right: 1rem;
top: 50%;
transform: translateY(-50%);
display: flex;
gap: 0.4rem;
}
.chat-info span { color: #495057; }
.chat-info strong { color: #212529; }
@@ -135,10 +137,16 @@
@if($report->veredict)
<span>Veredicto: <strong>{{ $report->veredict }}</strong></span>
@endif
<a class="btn btn-secondary btn-xs btn-verdict" title="Veredict"
href="{{ url('reports/veredict/' . $report->id) }}">
<i class="fa fa-gavel"></i>
</a>
<div class="btn-actions">
<button type="button" class="btn btn-info btn-xs" title="Ver detalles"
data-toggle="modal" data-target="#modalDetalles">
<i class="fa fa-info-circle"></i>
</button>
<a class="btn btn-secondary btn-xs" title="Veredicto"
href="{{ url('reports/veredict/' . $report->id) }}">
<i class="fa fa-gavel"></i>
</a>
</div>
</div>
{{-- Leyenda --}}
@@ -196,4 +204,76 @@
</div>
</div>
{{-- Modal fuera del chat-wrapper para evitar stacking context del flex --}}
<div class="modal fade" id="modalDetalles" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Detalles del Contrato #{{ $contract->id }}</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<h6 class="font-weight-bold mb-2">Información del contrato</h6>
<table class="table table-sm table-borderless mb-4">
<tr><th style="width:35%">Cliente</th><td>{{ $contract->user->name ?? '—' }}</td></tr>
<tr><th>Proveedor</th><td>{{ $contract->suppliers->company_name ?? '—' }}</td></tr>
<tr><th>Categoría</th><td>{{ $contract->categories->name ?? '—' }}</td></tr>
<tr><th>Dirección</th><td>{{ $contract->address }}</td></tr>
<tr><th>Cita</th><td>{{ $contract->appointment }}</td></tr>
<tr><th>Monto</th><td>${{ $contract->amount }}</td></tr>
<tr><th>Estado</th><td>{{ $contract->status->name ?? $contract->status_id }}</td></tr>
<tr><th>Transaction ID</th><td><code>{{ $contract->transaction_id ?? '—' }}</code></td></tr>
</table>
@if($nohome)
<hr>
<h6 class="font-weight-bold mb-2">Evidencia del proveedor (No Home)</h6>
<table class="table table-sm table-borderless mb-3">
<tr>
<th style="width:35%">Descripción</th>
<td>{{ $nohome->house_description ?? '—' }}</td>
</tr>
@if($nohome->location)
<tr>
<th>Coordenadas GPS</th>
<td>
{{ $nohome->location->getLatitude() }}, {{ $nohome->location->getLongitude() }}
<a href="https://www.google.com/maps?q={{ $nohome->location->getLatitude() }},{{ $nohome->location->getLongitude() }}"
target="_blank" class="btn btn-xs btn-outline-secondary ml-2">
<i class="fa fa-map-marker"></i> Ver en mapa
</a>
</td>
</tr>
@endif
</table>
@if($nohome->house_photo)
<img src="{{ $nohome->house_photo }}" alt="Foto del domicilio"
class="img-fluid rounded" style="max-height:400px;">
@endif
@else
<hr>
<p class="text-muted mb-0"><em>El proveedor no registró evidencia de visita.</em></p>
@endif
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cerrar</button>
</div>
</div>
</div>
</div>
@endsection
@section('js')
<script>
// main-content tiene overflow-y:auto que crea stacking context
// Mover el modal a body para que Bootstrap pueda posicionarlo correctamente
$(document).ready(function () {
$('#modalDetalles').appendTo('body');
});
</script>
@endsection