Desarrollo del modulo de alumno y control escolar para subir archivos de expediente

This commit is contained in:
2026-03-26 17:36:09 -06:00
parent 1bcebeb8a1
commit 3061a64418
137 changed files with 7234 additions and 57 deletions
+33
View File
@@ -155,6 +155,9 @@
</div>
</div>
</div>
@endsection
@@ -182,5 +185,35 @@
});
</script>
<script>
function verDocumento(url, docId) {
document.getElementById('visorFrame').src = url;
Livewire.dispatch('set-documento', { id: docId });
// 🔥 guardar instancia global
visorModalInstance = new bootstrap.Modal(
document.getElementById('visorModal')
);
visorModalInstance.show();
}
document.addEventListener('livewire:init', () => {
Livewire.on('cerrar-modal', () => {
if (visorModalInstance) {
visorModalInstance.hide();
}
// 🔥 limpiar iframe
document.getElementById('visorFrame').src = "";
});
});
</script>
@endsection
@@ -193,9 +193,93 @@
</div>
</div>
<div class="row pt-5">
<div>
<h5>Expediente del alumno</h5>
@php
$tipos = ['curp', 'acta', 'comprobante','certificadoPrepa','certificadoLicenciatura'];
$documentos = collect($prospecto?->documentos)->keyBy('tipo');
@endphp
@foreach($tipos as $tipo)
@php
$doc = $documentos[$tipo] ?? null;
@endphp
<div class="mb-3">
<strong>{{ strtoupper($tipo) }}</strong> :
@if($doc)
@if($doc->status == 1)
<span class="badge bg-success"> Validado</span>
@elseif($doc->status == 2)
<span class="badge bg-danger"> Rechazado</span>
@else
<span class="badge bg-warning">Pendiente de revision</span>
<button
onclick="verDocumento('{{ url('documento/ver/'.$doc->id) }}', {{ $doc->id }})"
class="btn btn-sm btn-primary mt-2">
<i class="fa-duotone fa-solid fa-eye"></i> Ver documento
</button>
@endif
@else
<span class="text-danger"> Faltante</span>
@endif
</div>
@endforeach
</div>
</div>
@else
@endif
</div>
<div class="modal fade"
id="visorModal"
tabindex="-1"
wire:ignore.self>
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Vista previa</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<iframe id="visorFrame" width="100%" height="500px" style="border:none;" wire:ignore></iframe>
<div class="m-5 text-center">
<button wire:click="rechazarDocumento" class="btn btn-danger">
<i class="fa-solid fa-trash-can"></i> Rechazar
</button>
<button wire:click="aprobarDocumento" class="btn btn-success">
<i class="fa-duotone fa-regular fa-circle-check"></i> Aprobar
</button>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,127 @@
<div wire:ignore.self
class="modal fade"
id="exampleModal"
tabindex="-1"
role="dialog"
data-backdrop="static"
data-keyboard="false">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Carga de documentos</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
@php
$pendientes = collect(['curp','acta','comprobante'])->filter(function($tipo) {
$doc = $documentosPorTipo[$tipo] ?? null;
return !$doc || $doc->status == 2;
});
@endphp
@if($pendientes->isEmpty())
<div class="alert alert-success">
Todos los documentos están validados
</div>
@endif
<form wire:submit.prevent="store" enctype="multipart/form-data">
@php
$doc = $documentosPorTipo['curp'] ?? null;
@endphp
@if(!$doc || $doc->status == 2)
<div class="mb-3">
<label class="small mb-1">CURP:</label>
@if($doc && $doc->status == 2)
<span class="badge bg-danger"> Rechazado</span>
@endif
<div class="custom-file-upload" wire:ignore>
<input type="file" id="curp" hidden
onchange="updateFileName(this, 'curp-name')"
wire:model="curp">
<label for="curp" class="btn btn-outline-primary btn-sm">
📄 Seleccionar archivo
</label>
<span id="curp-name" class="ml-2 text-muted">
Ningún archivo seleccionado
</span>
</div>
</div>
@endif
{{-- acta --}}
@php
$doc = $documentosPorTipo['acta'] ?? null;
@endphp
@if(!$doc || $doc->status == 2)
<div class="mb-3">
<label class="small mb-1">Acta:</label>
@if($doc && $doc->status == 2)
<span class="badge bg-danger"> Rechazado</span>
@endif
<div class="custom-file-upload" wire:ignore>
<input type="file" id="acta" hidden
onchange="updateFileName(this, 'acta-name')"
wire:model="acta">
<label for="acta" class="btn btn-outline-primary btn-sm">
📄 Seleccionar archivo
</label>
<span id="acta-name" class="ml-2 text-muted">
Ningún archivo seleccionado
</span>
</div>
</div>
@endif
{{-- acta --}}
@php
$doc = $documentosPorTipo['comprobante'] ?? null;
@endphp
@if(!$doc || $doc->status == 2)
<div class="mb-3">
<label class="small mb-1">Comprobante de domicilio:</label>
@if($doc && $doc->status == 2)
<span class="badge bg-danger"> Rechazado</span>
@endif
<div class="custom-file-upload" wire:ignore>
<input type="file" id="comprobante" hidden
onchange="updateFileName(this, 'comprobante-name')"
wire:model="comprobante">
<label for="comprobante" class="btn btn-outline-primary btn-sm">
📄 Seleccionar archivo
</label>
<span id="comprobante-name" class="ml-2 text-muted">
Ningún archivo seleccionado
</span>
</div>
</div>
@endif
<button class="btn btn-primary" type="submit">Subir</button>
</form>
</div>
</div>
</div>
</div>
+138 -14
View File
@@ -4,29 +4,153 @@
@section('content')
<style>
.custom-file-upload {
display: flex;
align-items: center;
gap: 10px;
}
.custom-file-upload span {
font-size: 13px;
}
</style>
<div class="container-fluid">
<div class="card shadow mb-4">
<div class="card-header py-3">
<div class="card-header py-3">
<div class="d-sm-flex align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Mi perfil</h6>
</div>
<div class="card-body">
@livewire('profile.update-profile-information-form')
<hr>
@livewire('profile.update-password-form')
<hr>
@livewire('profile.logout-other-browser-sessions-form')
@hasrole('Alumno')
<button onclick="Livewire.dispatch('documentos')"
class="btn btn-sm btn-success shadow-sm">
<i class="fas fa-file fa-sm text-white-50"></i> Expediente
</button>
@endrole
</div>
</div>
<div class="card-body">
@hasrole('Alumno')
<div>
<h5>Expediente</h5>
@php
$tipos = ['curp', 'acta', 'comprobante'];
$alumno = Auth::user()->alumnos()->first();
@endphp
@foreach($tipos as $tipo)
@php
$doc = $alumno?->documentos->firstWhere('tipo', $tipo);
@endphp
<div class="mb-3">
<strong>{{ strtoupper($tipo) }}</strong> :
@if($doc)
@if($doc->status == 1)
<span class="badge bg-success"> Validado</span>
@elseif($doc->status == 2)
<span class="badge bg-danger"> Rechazado</span>
@else
<span class="badge bg-warning">Pendiente de revision</span>
@endif
<br>
<button
onclick="verDocumento('{{ url('documento/ver/'.$doc->id) }}')"
class="btn btn-sm btn-primary mt-2">
<i class="fa-duotone fa-solid fa-eye"></i> Ver documento
</button>
@else
<span class="text-danger"> Faltante</span>
@endif
</div>
@endforeach
</div>
<livewire:documentos/>
@endrole
@livewire('profile.update-profile-information-form')
<hr>
@livewire('profile.update-password-form')
<hr>
@livewire('profile.logout-other-browser-sessions-form')
</div>
</div>
</div>
<!-- 🔥 MODAL VISOR -->
<div class="modal fade" id="visorModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Vista previa</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<iframe id="visorFrame" width="100%" height="600px" style="border:none;"></iframe>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
document.addEventListener('livewire:init', () => {
Livewire.on('open-documentos-modal', () => {
$('#exampleModal').modal('show');
});
});
// 🔥 abrir documento en modal
function verDocumento(url) {
document.getElementById('visorFrame').src = url + '?t=' + new Date().getTime();
var modal = new bootstrap.Modal(document.getElementById('visorModal'));
modal.show();
}
// 🔥 limpiar iframe al cerrar
document.getElementById('visorModal').addEventListener('hidden.bs.modal', function () {
document.getElementById('visorFrame').src = '';
});
</script>
<script>
function updateFileName(input, targetId) {
const fileName = input.files[0]?.name || 'Ningún archivo seleccionado';
document.getElementById(targetId).textContent = fileName;
}
</script>
@endsection