Primer push

This commit is contained in:
2025-10-24 08:46:31 +02:00
parent ab72ebeb90
commit 519dba7445
586 changed files with 77601 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="GestionaDenunciasAN.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="Scripts/bootstrap.bundle.min.js"></script>
<script src="_framework/blazor.web.js"></script>
</body>
</html>

View File

@@ -0,0 +1,7 @@
@inherits LayoutComponentBase
<div class="main">
<div class="">
@Body
</div>
</div>

View File

@@ -0,0 +1,95 @@
@inherits LayoutComponentBase
@inject GestionaDenunciasAN.Models.UserState userState
<style>
/* Barra superior con gradiente de azul: izquierda azul clarito, derecha azul oscuro */
.top-row {
position: relative;
width: 100%;
height: 60px;
background: linear-gradient(to right, #5a9bd5, #1f497d);
color: #fff;
}
/* Título del portal posicionado a la izquierda */
.portal-title {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
font-weight: bold;
font-size: 1.2rem;
margin-left: 1rem;
}
/* Contenedor del enlace de usuario posicionado a la derecha */
.logout-container {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
margin-right: 1rem;
}
/* Enlace que luce como texto, sin decoraciones */
.logout-link {
text-decoration: none;
color: #fff;
display: flex;
align-items: center;
border: none;
background: transparent;
cursor: pointer;
font: inherit;
padding: 0;
}
/* Icono SVG más grande */
.logout-link img {
height: 1.5em;
width: auto;
margin-right: 0.5rem;
}
/* Al pasar el ratón, se pone en negrita y mantiene el color blanco */
.logout-link:hover,
.logout-link:focus,
.logout-link:active {
font-weight: bold;
color: #fff !important;
text-decoration: none;
}
</style>
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<!-- Barra superior completa con gradiente de azul -->
<div class="top-row">
<div class="portal-title">
Portal Gestion Denuncias
</div>
<div class="logout-container">
<a class="logout-link" href="/">
<img src="Content/icon/person-fill.svg" alt="User Icon" />
<span>@userState?.NombreUsu</span>
</a>
</div>
</div>
<article class="content">
@Body
</article>
</main>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>

View File

@@ -0,0 +1,96 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@@ -0,0 +1,99 @@
<!-- NavMenu.razor -->
<style>
.nav-menu-container {
height: 100vh;
overflow: hidden;
}
/* Parte superior con el logo */
.nav-top {
background-color: #5a9bd5 !important;
padding: 0.5rem;
height: 5em;
display: flex;
align-items: center;
justify-content: center;
}
.nav-top img {
height: 4em;
width: auto;
}
/* Sección scrollable para el contenido del menú */
.nav-scrollable {
background: linear-gradient(to bottom, #5a9bd5, #1f497d);
height: calc(100vh - 5em);
overflow-y: auto;
padding-top: 1rem;
}
/* Empujar el grupo inferior (Instrucciones) al fondo, con un margen inferior */
.bottom-group {
margin-top: auto;
margin-bottom: 1rem;
}
/* Ajuste de los enlaces para icono y texto en la misma línea */
.nav-link {
display: inline-flex;
align-items: center;
font-size: 1rem;
}
.nav-link .bi {
display: inline-block;
font-size: 1.25rem;
margin-right: 0.5rem;
line-height: 1;
vertical-align: middle;
}
</style>
<div class="nav-menu-container">
<!-- Parte superior: logo -->
<div class="nav-top">
<div class="container-fluid" style="display: flex; justify-content: center; align-items: center;">
<img src="Content/imagenes/logo-oaaf-negativo-transparente.svg" alt="logo" />
</div>
</div>
<!-- Contenido scrollable: menú de navegación -->
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column" style="display: flex; height: 100%;">
<!-- Grupo superior: Pendientes y Finalizados -->
<div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="GestionZip" Match="NavLinkMatch.All">
<span class="bi bi-list-task" aria-hidden="true"></span> Gestion ZIP
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Pendientes" Match="NavLinkMatch.All">
<span class="bi bi-list-task" aria-hidden="true"></span> Pendientes
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Gestiona" Match="NavLinkMatch.All">
<span class="bi bi-journal-check" aria-hidden="true"></span> Gestiona
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Rechazados" Match="NavLinkMatch.All">
<span class="bi bi-journal-x" aria-hidden="true"></span> Rechazados
</NavLink>
</div>
</div>
<!-- Grupo inferior: Instrucciones, empujado al fondo -->
<div class="bottom-group">
<div class="nav-item px-3">
<NavLink class="nav-link" href="Instrucciones">
<span class="bi bi-book-half" aria-hidden="true"></span> Instrucciones
</NavLink>
</div>
</div>
</nav>
</div>
</div>

View File

@@ -0,0 +1,105 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}

View File

@@ -0,0 +1,36 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}

View File

@@ -0,0 +1,408 @@
@page "/GestionZip"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Components.Forms
@using GestionaDenunciasAN.Models
@using System.Globalization
@inject IHostEnvironment HostEnvironment
@inject IJSRuntime JSRuntime
<PageTitle>Gestión de ZIP</PageTitle>
<h3>Gestión de ZIP</h3>
@if (string.IsNullOrEmpty(zipsPath))
{
<div class="alert alert-danger">
La ruta para guardar los ZIP no se encuentra.
</div>
}
else
{
<button class="btn btn-primary mb-3" @onclick="TriggerFileInput">Subir nuevo ZIP</button>
<InputFile id="hiddenFileInput" style="display:none;" OnChange="HandleFileSelected" multiple accept=".zip" />
@if (!string.IsNullOrEmpty(uploadMessage))
{
<div class="alert alert-info">@uploadMessage</div>
}
<h4>Archivos ZIP existentes:</h4>
@if (existingZips == null || !existingZips.Any())
{
<p>No hay archivos ZIP subidos.</p>
}
else
{
<table class="table table-striped">
<thead>
<tr>
<th>Nombre del ZIP</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
@foreach (var zipFileName in existingZips)
{
<tr>
<td>@zipFileName</td>
<td>
<button class="btn btn-danger btn-sm" @onclick="() => DeleteZip(zipFileName)">Eliminar</button>
</td>
</tr>
}
</tbody>
</table>
}
}
@code {
private string zipsPath;
private List<string> existingZips = new();
private string uploadMessage;
private const string FIXED_PATH = @"C:\ZipsDenuncias";
private const string DENUNCIAS_JSON = @"C:\ZipsDenuncias\denuncias.json";
private const string FICHEROS_JSON = @"C:\ZipsDenuncias\ficheros.json";
protected override void OnInitialized()
{
if (Directory.Exists(FIXED_PATH))
{
zipsPath = FIXED_PATH;
}
else
{
zipsPath = string.Empty;
uploadMessage = "La ruta para guardar los ZIP no se encuentra.";
}
if (!string.IsNullOrEmpty(zipsPath))
{
LoadExistingZips();
}
}
private async Task TriggerFileInput()
{
await JSRuntime.InvokeVoidAsync("triggerFileInput", "hiddenFileInput");
}
private async Task HandleFileSelected(InputFileChangeEventArgs e)
{
if (string.IsNullOrEmpty(zipsPath))
{
uploadMessage = "No se pudo subir porque la ruta no se encuentra.";
return;
}
try
{
int count = 0;
foreach (var file in e.GetMultipleFiles())
{
var fileName = file.Name;
var finalPath = Path.Combine(zipsPath, fileName);
using var stream = file.OpenReadStream(long.MaxValue);
using var fs = new FileStream(finalPath, FileMode.Create);
await stream.CopyToAsync(fs);
count++;
}
uploadMessage = $"{count} archivo(s) ZIP subido(s) correctamente.";
LoadExistingZips();
await ActualizarJsonConZipsAsync();
}
catch (Exception ex)
{
uploadMessage = $"Error al subir: {ex.Message}";
}
}
private void LoadExistingZips()
{
existingZips = Directory.GetFiles(zipsPath, "*.zip")
.Select(Path.GetFileName)
.ToList();
}
private void DeleteZip(string fileName)
{
var fullPath = Path.Combine(zipsPath, fileName);
if (File.Exists(fullPath))
{
File.Delete(fullPath);
}
LoadExistingZips();
}
// Procesa cada ZIP: extrae report.txt y ficheros adjuntos; actualiza o añade registros en los JSON.
private async Task ActualizarJsonConZipsAsync()
{
var registros = new List<DenunciasGestiona>();
var ficheros = new List<FicherosDenuncias>();
var zipFiles = Directory.GetFiles(zipsPath, "*.zip");
foreach (var zipFile in zipFiles)
{
var folderName = Path.GetFileNameWithoutExtension(zipFile);
var extractPath = Path.Combine(zipsPath, folderName);
if (!Directory.Exists(extractPath))
Directory.CreateDirectory(extractPath);
// Extraemos el ZIP (sobrescribe si es necesario)
System.IO.Compression.ZipFile.ExtractToDirectory(zipFile, extractPath, overwriteFiles: true);
// Procesamos el reporte
var reportPath = Path.Combine(extractPath, "report.txt");
if (File.Exists(reportPath))
{
var lines = await File.ReadAllLinesAsync(reportPath);
var denuncia = ParsearReportTxt(lines);
if (string.IsNullOrEmpty(denuncia.Expediente_Gestiona))
denuncia.Expediente_Gestiona = "Pendiente";
registros.Add(denuncia);
// Procesamos ficheros adjuntos
foreach (var subFolder in new[] { "files", "files_attached_from_recipients" })
{
var subPath = Path.Combine(extractPath, subFolder);
if (Directory.Exists(subPath))
{
foreach (var file in Directory.GetFiles(subPath))
{
var bytes = await File.ReadAllBytesAsync(file);
var fichero = new FicherosDenuncias
{
Id_Fichero = 0,
Id_Tipo = 1,
Descripcion = null,
Fecha = DateTime.Now,
Observaciones = "",
Id_Denuncia = denuncia.Id_Denuncia,
NombreFichero = Path.GetFileName(file),
Fichero = bytes
};
ficheros.Add(fichero);
}
}
}
}
}
// Actualizamos el JSON de Denuncias
List<DenunciasGestiona> existentesDenuncias = new List<DenunciasGestiona>();
if (File.Exists(DENUNCIAS_JSON))
{
var json = await File.ReadAllTextAsync(DENUNCIAS_JSON);
existentesDenuncias = Newtonsoft.Json.JsonConvert.DeserializeObject<List<DenunciasGestiona>>(json) ?? new List<DenunciasGestiona>();
}
foreach (var reg in registros)
{
var existente = existentesDenuncias.FirstOrDefault(d => d.Id_Denuncia == reg.Id_Denuncia);
if (existente != null)
{
if (existente.Expediente_Gestiona == "Pendiente")
{
existente.Fecha = reg.Fecha;
existente.Etiqueta = reg.Etiqueta;
existente.Estado = reg.Estado;
existente.Tipo_Denuncia = reg.Tipo_Denuncia;
existente.Asunto = reg.Asunto;
existente.A_Quien_Denuncia = reg.A_Quien_Denuncia;
existente.Descripcion_Denuncia = reg.Descripcion_Denuncia;
existente.Denunciado_Ante_Inst = reg.Denunciado_Ante_Inst;
existente.Lugar_Hechos = reg.Lugar_Hechos;
existente.Fecha_Hechos = reg.Fecha_Hechos;
existente.Condiciones = reg.Condiciones;
existente.Comments = reg.Comments;
}
}
else
{
existentesDenuncias.Add(reg);
}
}
var newJsonDenuncias = Newtonsoft.Json.JsonConvert.SerializeObject(existentesDenuncias, Newtonsoft.Json.Formatting.Indented);
await File.WriteAllTextAsync(DENUNCIAS_JSON, newJsonDenuncias);
// Actualizamos el JSON de Ficheros
List<FicherosDenuncias> existentesFicheros = new List<FicherosDenuncias>();
if (File.Exists(FICHEROS_JSON))
{
var jsonF = await File.ReadAllTextAsync(FICHEROS_JSON);
existentesFicheros = Newtonsoft.Json.JsonConvert.DeserializeObject<List<FicherosDenuncias>>(jsonF) ?? new List<FicherosDenuncias>();
}
// Eliminamos los ficheros de denuncia que vamos a actualizar
foreach (var reg in registros)
{
existentesFicheros.RemoveAll(f => f.Id_Denuncia == reg.Id_Denuncia);
}
// Añadimos los nuevos ficheros
existentesFicheros.AddRange(ficheros);
var newJsonFicheros = Newtonsoft.Json.JsonConvert.SerializeObject(existentesFicheros, Newtonsoft.Json.Formatting.Indented);
await File.WriteAllTextAsync(FICHEROS_JSON, newJsonFicheros);
}
private DenunciasGestiona ParsearReportTxt(string[] lines)
{
var denuncia = new DenunciasGestiona();
bool leyendoComments = false;
var commentsBuilder = new System.Text.StringBuilder();
for (int i = 0; i < lines.Length; i++)
{
var line = lines[i].Trim();
// Si se encuentra la marca de comentarios, todo lo siguiente se agrega a Comments
if (line.StartsWith("Comments", StringComparison.OrdinalIgnoreCase))
{
leyendoComments = true;
continue;
}
if (leyendoComments)
{
commentsBuilder.AppendLine(line);
continue;
}
// Campos generales
if (line.StartsWith("ID:", StringComparison.OrdinalIgnoreCase))
{
var idStr = line.Substring("ID:".Length).Trim();
if (int.TryParse(idStr, out int idNum))
denuncia.Id_Denuncia = idNum;
}
else if (line.StartsWith("Fecha:", StringComparison.OrdinalIgnoreCase))
{
var fechaStr = line.Substring("Fecha:".Length).Trim();
if (fechaStr.EndsWith("(UTC)"))
fechaStr = fechaStr.Substring(0, fechaStr.Length - "(UTC)".Length).Trim();
// Intentamos primero con un formato específico
string formato = "dddd dd MMMM yyyy HH:mm";
if (DateTime.TryParseExact(fechaStr, formato, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTime fechaParsed))
{
denuncia.Fecha = fechaParsed;
}
else if (DateTime.TryParse(fechaStr, out DateTime fechaParsed2))
{
denuncia.Fecha = fechaParsed2;
}
else
{
denuncia.Fecha = DateTime.MinValue;
}
}
else if (line.StartsWith("Etiqueta:", StringComparison.OrdinalIgnoreCase))
{
denuncia.Etiqueta = line.Substring("Etiqueta:".Length).Trim();
}
else if (line.StartsWith("Estado:", StringComparison.OrdinalIgnoreCase))
{
denuncia.Estado = line.Substring("Estado:".Length).Trim();
}
else if (line.StartsWith("Expediente_Gestiona:", StringComparison.OrdinalIgnoreCase))
{
denuncia.Expediente_Gestiona = line.Substring("Expediente_Gestiona:".Length).Trim();
}
else if (line.StartsWith("¿Qué tipo de denuncia desea realizar?", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Tipo_Denuncia = lines[i + 1].Trim();
}
// Sección "Datos de contacto"
else if (line.Equals("Nombre", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Nombre = lines[i + 1].Trim();
}
else if (line.Equals("Apellidos", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Apellidos = lines[i + 1].Trim();
}
else if (line.Equals("Sexo", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Sexo = lines[i + 1].Trim();
}
else if (line.Equals("DNI", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Dni = lines[i + 1].Trim();
}
// Sección "Descripción"
else if (line.StartsWith("Asunto", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Asunto = lines[i + 1].Trim();
}
else if (line.StartsWith("¿A quién denuncia?", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.A_Quien_Denuncia = lines[i + 1].Trim();
}
else if (line.StartsWith("Describa su denuncia", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Descripcion_Denuncia = lines[i + 1].Trim();
}
else if (line.StartsWith("¿Ha denunciado estos hechos ante otras instituciones", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Denunciado_Ante_Inst = lines[i + 1].Trim();
}
// Sección "Lugar y fecha de los hechos"
else if (line.StartsWith("Lugar en la que ocurrieron los hechos", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Lugar_Hechos = lines[i + 1].Trim();
}
else if (line.StartsWith("Fecha de los hechos que denuncia", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
{
var fechaHechosStr = lines[i + 1].Trim();
// Si el valor es "ayer", se asume la fecha de ayer
if (fechaHechosStr.Equals("ayer", StringComparison.OrdinalIgnoreCase))
denuncia.Fecha_Hechos = DateTime.Now.AddDays(-1);
else if (DateTime.TryParse(fechaHechosStr, out DateTime fechaHechosParsed))
denuncia.Fecha_Hechos = fechaHechosParsed;
}
}
// Sección "Notificaciones"
else if (line.Equals("Correo electrónico", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Correo_Electronico = lines[i + 1].Trim();
}
else if (line.Equals("Notificaciones Electrónicas", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Notificacion_Electronica = lines[i + 1].Trim();
}
else if (line.Equals("Notificaciones mediante SMS", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Notificacion_Sms = lines[i + 1].Trim();
}
else if (line.Equals("Seleccione su preferencia de notificación y seguimiento de su denuncia", StringComparison.OrdinalIgnoreCase)
|| line.Equals("Notificación Preferencia", StringComparison.OrdinalIgnoreCase))
{
if (i + 1 < lines.Length)
denuncia.Notificacion_Preferencia = lines[i + 1].Trim();
}
// Sección "Condiciones y reglas de uso"
else if (line.Contains("☑"))
{
denuncia.Condiciones = true;
}
}
denuncia.Comments = commentsBuilder.ToString();
return denuncia;
}
}
<script>
// Función JS que dispara el click en el elemento con el ID indicado.
window.triggerFileInput = (elementId) => {
document.getElementById(elementId).click();
};
</script>

View File

@@ -0,0 +1,383 @@
@page "/Gestiona"
@rendermode InteractiveServer
@using GestionaDenunciasAN.Models
@using System.Globalization
@attribute [StreamRendering]
@inject GestionaDenunciasAN.Models.UserState userState
@inject NavigationManager Navigation
@inject IHostEnvironment HostEnvironment
<PageTitle>Denuncias Gestión</PageTitle>
<style>
/* Contenedor para la lista de denuncias */
.gestion-list {
margin-top: 20px;
}
/* Estilo general para cada card */
.collapse-card {
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.collapse-card:hover {
transform: scale(1.02);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
/* Aplicar fondo verde a toda la card */
.Aceptada {
background-color: #d4edda;
color: #155724;
}
/* Forzar fondo transparente en cabecera y cuerpo para que hereden el color de la card */
.Aceptada .card-header,
.Aceptada .card-body {
background-color: transparent;
}
/* Estilos para la cabecera de la card */
.card-header {
padding: 0.75rem 1.25rem;
cursor: pointer;
}
.card-header h5 {
margin: 0;
font-size: 1.1rem;
}
.header-info {
font-size: 0.9rem;
margin-top: 0.5rem;
}
.header-info span {
margin-right: 15px;
}
/* Estilos para el cuerpo de la card */
.card-body {
padding: 1.25rem;
}
/* Estilos para los títulos de sección dentro de la card */
.section-heading {
text-align: center;
font-weight: bold;
text-decoration: underline;
font-size: 1.1em;
margin-top: 1rem;
margin-bottom: 0.5rem;
}
</style>
<h1>Denuncias en Gestión</h1>
<!-- Campo de búsqueda -->
<input type="text"
class="form-control"
placeholder="Buscar denuncias..."
@bind="busqueda"
@bind:event="oninput"
style="margin-bottom:20px;" />
@if (!hasLoaded)
{
<div class="alert alert-info">Cargando datos...</div>
}
else if (denunciasGestiona == null || !denunciasGestiona.Any())
{
<p>No hay denuncias en gestión.</p>
}
else
{
<div class="gestion-list">
@foreach (var denuncia in denunciasGestiona.Where(d =>
string.IsNullOrWhiteSpace(busqueda) ||
d.Id_Denuncia.ToString().Contains(busqueda) ||
(!string.IsNullOrEmpty(d.NombreDenuncia) && d.NombreDenuncia.Contains(busqueda, StringComparison.OrdinalIgnoreCase)) ||
(!string.IsNullOrEmpty(d.ArchivoElegido) && d.ArchivoElegido.Contains(busqueda, StringComparison.OrdinalIgnoreCase)) ||
(!string.IsNullOrEmpty(d.Estado) && d.Estado.Contains(busqueda, StringComparison.OrdinalIgnoreCase))
))
{
var collapseId = $"collapse{denuncia.Id_Denuncia}";
<div class="card collapse-card Aceptada">
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#@collapseId" aria-expanded="false" aria-controls="@collapseId">
<h5 class="mb-0">Denuncia ID: @denuncia.Id_Denuncia</h5>
<div class="header-info">
<span><strong>Estado:</strong> @denuncia.Estado</span>
<span><strong>Nombre:</strong> @denuncia.NombreDenuncia</span>
<span><strong>Archivo:</strong> @denuncia.ArchivoElegido</span>
<span><strong>Fecha de Subida:</strong> @denuncia.FechaSubidaAGestiona.ToString("dd/MM/yyyy")</span>
<span><strong>Hora de Subida:</strong> @denuncia.FechaSubidaAGestiona.ToString("HH:mm")</span>
</div>
</div>
<div id="@collapseId" class="collapse">
<div class="card-body">
<!-- Datos Generales -->
<h5 class="section-heading">Datos Generales</h5>
<dl class="row">
@if (denuncia.Id_RegistroDenuncia != 0)
{
<dt class="col-sm-3">ID Registro Denuncia</dt>
<dd class="col-sm-9">@denuncia.Id_RegistroDenuncia</dd>
}
@if (denuncia.Id_Denuncia != 0)
{
<dt class="col-sm-3">ID Denuncia</dt>
<dd class="col-sm-9">@denuncia.Id_Denuncia</dd>
}
@if (denuncia.Fecha != DateTime.MinValue)
{
<dt class="col-sm-3">Fecha</dt>
<dd class="col-sm-9">@denuncia.Fecha.ToString("dd/MM/yyyy HH:mm")</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Expediente_Gestiona))
{
<dt class="col-sm-3">Expediente Gestión</dt>
<dd class="col-sm-9">@denuncia.Expediente_Gestiona</dd>
}
@if (denuncia.Id_Persona_Gestiona != 0)
{
<dt class="col-sm-3">ID Persona Gestión</dt>
<dd class="col-sm-9">@denuncia.Id_Persona_Gestiona</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Etiqueta))
{
<dt class="col-sm-3">Etiqueta</dt>
<dd class="col-sm-9">@denuncia.Etiqueta</dd>
}
</dl>
<!-- Datos del Denunciante -->
@if (!string.IsNullOrWhiteSpace(denuncia.Nombre) ||
!string.IsNullOrWhiteSpace(denuncia.Apellidos) ||
!string.IsNullOrWhiteSpace(denuncia.Sexo) ||
!string.IsNullOrWhiteSpace(denuncia.Dni))
{
<h5 class="section-heading">Datos del Denunciante</h5>
<dl class="row">
@if (!string.IsNullOrWhiteSpace(denuncia.Nombre))
{
<dt class="col-sm-3">Nombre</dt>
<dd class="col-sm-9">@denuncia.Nombre</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Apellidos))
{
<dt class="col-sm-3">Apellidos</dt>
<dd class="col-sm-9">@denuncia.Apellidos</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Sexo))
{
<dt class="col-sm-3">Sexo</dt>
<dd class="col-sm-9">@denuncia.Sexo</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Dni))
{
<dt class="col-sm-3">DNI</dt>
<dd class="col-sm-9">@denuncia.Dni</dd>
}
</dl>
}
<!-- Detalles de la Denuncia -->
<h5 class="section-heading">Detalles de la Denuncia</h5>
<dl class="row">
<dt class="col-sm-3">Asunto</dt>
<dd class="col-sm-9">@denuncia.Asunto</dd>
<dt class="col-sm-3">A Quien Denuncia</dt>
<dd class="col-sm-9">@denuncia.A_Quien_Denuncia</dd>
<dt class="col-sm-3">Descripción Denuncia</dt>
<dd class="col-sm-9">@denuncia.Descripcion_Denuncia</dd>
<dt class="col-sm-3">Denunciado Ante Inst</dt>
<dd class="col-sm-9">@denuncia.Denunciado_Ante_Inst</dd>
@if (!string.IsNullOrWhiteSpace(denuncia.Modalidad_Informacion))
{
<dt class="col-sm-3">Modalidad Información</dt>
<dd class="col-sm-9">@denuncia.Modalidad_Informacion</dd>
}
<dt class="col-sm-3">Lugar Hechos</dt>
<dd class="col-sm-9">@denuncia.Lugar_Hechos</dd>
@if (denuncia.Fecha_Hechos != DateTime.MinValue)
{
<dt class="col-sm-3">Fecha Hechos</dt>
<dd class="col-sm-9">@denuncia.Fecha_Hechos.ToString("dd/MM/yyyy")</dd>
}
</dl>
<!-- Datos de Notificación -->
<h5 class="section-heading">Datos de Notificación</h5>
<dl class="row">
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Preferencia))
{
<dt class="col-sm-3">Notificación Preferencia</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Preferencia</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Electronica))
{
<dt class="col-sm-3">Notificación Electrónica</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Electronica</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Correo_Electronico))
{
<dt class="col-sm-3">Correo Electrónico</dt>
<dd class="col-sm-9">@denuncia.Correo_Electronico</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Sms))
{
<dt class="col-sm-3">Notificación SMS</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Sms</dd>
}
</dl>
<!-- Otros -->
<h5 class="section-heading">Otros</h5>
<dl class="row">
@if (denuncia.Condiciones)
{
<dt class="col-sm-3">Condiciones</dt>
<dd class="col-sm-9">Sí</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Comments))
{
<dt class="col-sm-3">Comentarios</dt>
<dd class="col-sm-9"><pre>@denuncia.Comments</pre></dd>
}
</dl>
<!-- Ficheros adjuntos -->
@if (ficherosAdjuntos.ContainsKey(denuncia.Id_Denuncia) && ficherosAdjuntos[denuncia.Id_Denuncia].Any())
{
<h5 class="section-heading">Ficheros Adjuntos</h5>
<table class="table table-striped">
<thead>
<tr>
<th>Nombre</th>
<th>Tamaño (bytes)</th>
<th>Ver</th>
</tr>
</thead>
<tbody>
@foreach (var fichero in ficherosAdjuntos[denuncia.Id_Denuncia])
{
<tr>
<td>@fichero.NombreFichero</td>
<td>@fichero.Fichero.Length</td>
<td>
<a class="btn btn-primary btn-sm" href="#"
onclick="openFile(event, '@Convert.ToBase64String(fichero.Fichero)', '@GetContentType(fichero.NombreFichero)');">
<i class="bi bi-eye"></i> Ver
</a>
</td>
</tr>
}
</tbody>
</table>
}
</div>
</div>
</div>
}
</div>
}
<script>
function createObjectUrl(base64, contentType) {
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
const blob = new Blob([bytes], { type: contentType });
return URL.createObjectURL(blob);
}
function openFile(event, base64, contentType) {
event.preventDefault();
event.stopPropagation();
const objectUrl = createObjectUrl(base64, contentType);
window.open(objectUrl, '_blank');
}
</script>
@code {
private List<DenunciasGestiona> denunciasGestiona = new();
private Dictionary<int, List<FicherosDenuncias>> ficherosAdjuntos = new();
// Variable para la búsqueda
private string busqueda = "";
private const string DENUNCIAS_JSON = @"C:\ZipsDenuncias\denuncias.json";
private const string FICHEROS_JSON = @"C:\ZipsDenuncias\ficheros.json";
private bool isLoading = false;
private bool hasLoaded = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (string.IsNullOrEmpty(userState.Token))
{
Navigation.NavigateTo("/", true);
return;
}
isLoading = true;
StateHasChanged();
await CargarGestionaAsync();
await CargarFicherosAdjuntosAsync();
isLoading = false;
hasLoaded = true;
StateHasChanged();
}
}
private async Task CargarGestionaAsync()
{
var todas = await CargarDenunciasJsonAsync();
denunciasGestiona = todas.Where(d => d.Expediente_Gestiona == "Gestiona").ToList();
}
private async Task<List<DenunciasGestiona>> CargarDenunciasJsonAsync()
{
if (File.Exists(DENUNCIAS_JSON))
{
var json = await File.ReadAllTextAsync(DENUNCIAS_JSON);
var lista = Newtonsoft.Json.JsonConvert.DeserializeObject<List<DenunciasGestiona>>(json);
return lista ?? new List<DenunciasGestiona>();
}
return new List<DenunciasGestiona>();
}
private async Task<List<FicherosDenuncias>> CargarFicherosJsonAsync()
{
if (File.Exists(FICHEROS_JSON))
{
var json = await File.ReadAllTextAsync(FICHEROS_JSON);
var lista = Newtonsoft.Json.JsonConvert.DeserializeObject<List<FicherosDenuncias>>(json);
return lista ?? new List<FicherosDenuncias>();
}
return new List<FicherosDenuncias>();
}
private async Task CargarFicherosAdjuntosAsync()
{
var listaFicheros = await CargarFicherosJsonAsync();
ficherosAdjuntos = listaFicheros.GroupBy(f => f.Id_Denuncia)
.ToDictionary(g => g.Key, g => g.ToList());
}
private string GetContentType(string fileName)
{
var ext = Path.GetExtension(fileName).ToLowerInvariant();
return ext switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".pdf" => "application/pdf",
".txt" => "text/plain",
_ => "application/octet-stream",
};
}
}

View File

@@ -0,0 +1,75 @@
@page "/Instrucciones"
@attribute [StreamRendering]
@inject GestionaDenunciasAN.Models.UserState userState
@inject NavigationManager Navigation
<PageTitle>Instrucciones</PageTitle>
<div class="container mt-4">
<h1 class="mb-4">Instrucciones de la Aplicación</h1>
<p>
Bienvenido a la aplicación de <strong>Gestión de Denuncias</strong>. Esta herramienta ha sido diseñada para
cargar, procesar y gestionar denuncias que se reciben a través de archivos ZIP. A continuación, se explica el funcionamiento y las principales funcionalidades:
</p>
<h2>1. Carga y Procesamiento de Denuncias</h2>
<ul>
<li>
<strong>Carga de archivos:</strong> Los archivos ZIP se encuentran en la carpeta <code>Ejemplos</code>. Cada ZIP contiene un archivo <code>report.txt</code> con los datos de la denuncia, y en algunos casos, ficheros asociados.
</li>
<li>
<strong>Descompresión y parseo:</strong> La aplicación descomprime cada ZIP y extrae la información del <code>report.txt</code> para crear una representación estructurada de la denuncia.
</li>
</ul>
<h2>2. Visualización y Gestión de Denuncias</h2>
<ul>
<li>
<strong>Pendientes:</strong> En esta pestaña se listan todas las denuncias recién cargadas. Cada denuncia se muestra en una tarjeta colapsable que permite ver sus detalles y los ficheros asociados. Además, se disponen de botones para <em>Enviar a gestiona</em> (verde) y <em>Rechazar denuncia</em> (rojo). Actualmente, estos botones no realizan ninguna acción, pero en futuras versiones registrarán el estado de la denuncia.
</li>
<li>
<strong>Finalizados:</strong> Una vez procesadas, las denuncias se trasladan a la pestaña <strong>Finalizados</strong>, donde se muestran diferenciadas:
<ul>
<li>Las denuncias aceptadas se destacan con un fondo verde.</li>
<li>Las denuncias rechazadas se muestran con un fondo rojo.</li>
</ul>
</li>
<li>
<strong>Visualización de ficheros:</strong> En cada tarjeta, el botón <em>Ver</em> abre una nueva pestaña para mostrar el contenido del fichero asociado.
</li>
</ul>
<h2>3. Flujo de Trabajo</h2>
<p>
El flujo de trabajo de la aplicación es el siguiente:
</p>
<ol>
<li>
Los archivos ZIP se depositan en la carpeta <code>Ejemplos</code>.
</li>
<li>
La aplicación descomprime los ZIP y parsea el archivo <code>report.txt</code> para extraer la información de cada denuncia.
</li>
<li>
Las denuncias se muestran en la pestaña <strong>Pendientes</strong> en forma de tarjetas colapsables, donde puedes ver detalles y ficheros asociados.
</li>
<li>
Las acciones para enviar o rechazar la denuncia se registrarán y, en consecuencia, se reflejará su estado en la pestaña <strong>Finalizados</strong>.
</li>
</ol>
<p>
Esta aplicación está diseñada para facilitar la gestión de denuncias de forma intuitiva. Si tienes cualquier duda o sugerencia, no dudes en ponerte en contacto con el equipo de soporte.
</p>
</div>
@code {
protected override void OnInitialized()
{
// Si no hay token, redirige al login
if (string.IsNullOrEmpty(userState.Token))
{
Navigation.NavigateTo("/", true);
}
}
}

View File

@@ -0,0 +1,344 @@
@using BlazorBootstrap
@using Layout
@using Microsoft.AspNetCore.Mvc
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@using GestionaDenunciasAN.Models
@using System.Net.Http.Headers
@using System.Text
@using System.Linq.Expressions
@using Serialize.Linq.Serializers
@using System.Security.Cryptography.X509Certificates
@using System.Security.Cryptography
@using bdAntifraude.db
@rendermode InteractiveServer
@layout EmptyLayout
@page "/"
@inject IHttpClientFactory HttpClientFactory
@inject IHttpContextAccessor HttpContextAccessor
@inject NavigationManager Navigation
@inject UserState UserState
@inject IJSRuntime JSRuntime
<PageTitle>Gestión Denuncias - Oficina Antifraude de Andalucía</PageTitle>
<style>
/* Contenedor que ocupa toda la altura de la ventana */
.full-height {
height: 100vh;
margin: 0;
padding: 0;
}
/* Columna izquierda con un degradado en azul */
.left-side {
background: linear-gradient(135deg, #5a9bd5, #2A5298);
color: #fff;
}
/* Columna derecha con fondo claro */
.right-side {
background: linear-gradient(135deg, #ffffff, #dddddd);
}
/* Contenedor para centrar contenido en ambas columnas */
.centered-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
padding: 2rem;
}
/* Logo */
.logo-img {
max-width: 300px;
margin-bottom: 2rem;
}
/* Caja de login con degradado y sombra */
.login-box {
background: linear-gradient(135deg, #5a9bd5, #2A5298);
padding: 2rem;
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
max-width: 400px;
width: 100%;
text-align: center;
color: #fff; /* Para que el texto sea legible sobre el gradiente */
}
.login-box label {
font-weight: 500;
display: block;
text-align: left;
margin-bottom: 0.3rem;
color: #fff; /* Aseguramos que la etiqueta se vea bien */
}
.login-box .form-control {
width: 100%;
padding: 0.75rem;
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 5px;
}
.error-message {
color: red;
margin-bottom: 1rem;
}
/* Botones personalizados */
.login-box .btnOAAFAzul {
background-color: #fff !important; /* Fondo blanco */
color: #000 !important; /* Texto negro */
border: none;
border-radius: 15px;
padding: 10px;
width: 100%;
margin-top: 10px;
cursor: pointer;
font-size: 16px;
}
.login-box .btnOAAFAzul:hover {
opacity: 0.8;
}
.btnOAAFBlack {
background-color: #343334;
border: none;
border-radius: 15px;
color: white;
padding: 10px;
width: 100%;
margin-top: 10px;
cursor: pointer;
font-size: 16px;
}
.btnOAAFBlack:hover {
opacity: 0.8;
}
/* Estilos para el loading */
.loadingFrame {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loadingDiv {
text-align: center;
}
.loadingImg {
background: url('Content/imagenes/loading.gif') no-repeat center center;
background-size: contain;
width: 100px;
height: 100px;
}
</style>
<link rel="icon" type="image/x-icon" href="faviconParlamento.ico" />
<link href="~/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="Content/Site.css" rel="stylesheet" />
<!-- Bloque de loading que se muestra mientras 'mostrar' es true -->
@if (mostrar)
{
<div id="cargando" class="loadingFrame">
<div class="loadingDiv">
<div class="loadingImg"></div>
</div>
</div>
}
<!-- Estructura en dos columnas -->
<div class="container-fluid full-height px-0">
<div class="row no-gutters full-height">
<!-- Columna izquierda (más ancha) -->
<div class="col-md-7 left-side">
<div class="centered-content">
<!-- Tu logo en grande -->
<img src="Content/imagenes/logo-oaaf-negativo-transparente.svg" alt="Logo Oficina Antifraude" class="logo-img" />
<h1>Oficina Antifraude de Andalucía</h1>
<p style="max-width: 500px; text-align: center;">
Bienvenido/a a la plataforma de gestión de denuncias.
Aquí podrás autenticarte para revisar y tramitar las denuncias recibidas.
</p>
</div>
</div>
<!-- Columna derecha (más estrecha) -->
<div class="col-md-5 right-side">
<div class="centered-content">
<form class="login-box">
<h3 class="mb-4">Iniciar Sesión</h3>
<p id="mensajeError" class="error-message">@mensaje</p>
<div class="form-group text-left">
<label for="Usu">Usuario</label>
<input id="Usu" type="text" class="form-control" @bind="Usu" />
</div>
<div class="form-group text-left">
<label for="Contrasena">Contraseña</label>
<input id="Contrasena" type="password" class="form-control" @bind="pass" />
</div>
<button class="btnOAAFAzul" type="button" @onclick="LogIn">ENTRAR</button>
<button class="btnOAAFBlack" type="button" @onclick="IniciarSesionConCertificado">INICIAR SESIÓN CON CERTIFICADO</button>
</form>
</div>
</div>
</div>
</div>
<!-- Iframe oculto para la autenticación con certificado -->
<iframe id="authCertIframe" style="display:none;"></iframe>
@code {
public string? Usu { get; set; }
public string? pass { get; set; }
private string? mensaje { get; set; }
public bool mostrar { get; set; } = false;
private DotNetObjectReference<Login>? dotNetRef;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// Se crea una referencia a este componente para que JS pueda invocar SetToken
dotNetRef = DotNetObjectReference.Create(this);
await JSRuntime.InvokeVoidAsync("registerTokenReceiver", dotNetRef);
}
}
protected override void OnInitialized()
{
LimpiarEstadoUsuario();
}
private void LimpiarEstadoUsuario()
{
UserState.Token = "";
UserState.NombreUsu = "";
HttpContextAccessor?.HttpContext?.Session?.Clear();
}
public async Task LogIn()
{
mostrar = true;
await Task.Delay(1);
if (string.IsNullOrWhiteSpace(Usu) || string.IsNullOrWhiteSpace(pass))
{
mostrar = false;
mensaje = "Por favor, ingrese su usuario y contraseña.";
return;
}
try
{
var client = HttpClientFactory.CreateClient();
client.BaseAddress = new Uri(Utilidades.urlSwagger());
var loginPayload = new { NombreUsuario = Usu, Contraseña = pass, Origen = "Denuncias" };
var loginContent = new StringContent(JsonConvert.SerializeObject(loginPayload), Encoding.UTF8, "application/json");
var loginResponse = await client.PostAsync("Auth/login", loginContent);
await ProcesarRespuesta(loginResponse);
}
catch (Exception ex)
{
mostrar = false;
mensaje = $"Error inesperado: {ex.Message}";
}
}
private async Task IniciarSesionConCertificado()
{
mostrar = true;
var url = Utilidades.urlSwagger() + "Auth/login-cert?iframe=true";
await JSRuntime.InvokeVoidAsync("iniciarSesionConCertificado", url);
}
private async Task ProcesarRespuesta(HttpResponseMessage response)
{
var responseContent = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var parsedJson = JObject.Parse(responseContent);
UserState.Token = parsedJson["token"]?.ToString() ?? "";
// Actualizamos el nombre del usuario (formateado como "APELLIDOS, NOMBRE")
UserState.NombreUsu = $"{parsedJson["user"]?["apellidos"]?.ToString()}, {parsedJson["user"]?["nombre"]?.ToString()}";
Navigation.NavigateTo("/GestionZip", true);
}
else
{
mostrar = false;
mensaje = "Error de autenticación. Verifique sus credenciales o el certificado.";
}
}
[JSInvokable]
public Task SetToken(string token, string userJson)
{
// Actualizamos el token en UserState
UserState.Token = token;
try
{
var userObj = JObject.Parse(userJson);
UserState.NombreUsu = $"{userObj["APELLIDOS"]?.ToString()}, {userObj["NOMBRE"]?.ToString()}";
}
catch
{
UserState.NombreUsu = "";
}
Navigation.NavigateTo("/GestionZip", true);
return Task.CompletedTask;
}
}
<script>
function registerTokenReceiver(dotnetRef) {
window.dotnetTokenReceiver = dotnetRef;
}
window.iniciarSesionConCertificado = function(url) {
console.log("Se llamó iniciarSesionConCertificado con URL:", url);
var iframe = document.getElementById("authCertIframe");
if (iframe) {
iframe.src = url;
} else {
console.error("No se encontró el iframe con id 'authCertIframe'");
}
};
window.addEventListener("message", function(event) {
var data = event.data;
if (data && data.token) {
console.log("Mensaje recibido con token:", data.token);
if (window.dotnetTokenReceiver) {
window.dotnetTokenReceiver.invokeMethodAsync("SetToken", data.token, JSON.stringify(data.user));
} else {
localStorage.setItem("token", data.token);
localStorage.setItem("user", JSON.stringify(data.user));
window.location.href = "/GestionZip";
}
}
});
</script>

View File

@@ -0,0 +1,664 @@
@page "/Pendientes"
@rendermode InteractiveServer
@using GestionaDenunciasAN.Models
@using System.Globalization
@using System.IO
@using System.Linq
@using System.Text
@using GestionaDenunciasAN.Helpers // Para PdfHelper
@attribute [StreamRendering]
@inject GestionaDenunciasAN.Models.UserState userState
@inject NavigationManager Navigation
@inject IHostEnvironment HostEnvironment
<PageTitle>Denuncias Pendientes</PageTitle>
<style>
.seleccionar-col {
width: 50px;
text-align: center;
vertical-align: middle;
}
/* Contenedor para la lista de denuncias */
.pendientes-list {
margin-top: 20px;
}
/* Estilo general para cada card, con animaciones y transiciones */
.collapse-card {
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
background-color: #fff;
}
.collapse-card:hover {
transform: scale(1.02);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
/* Estilos para la cabecera de la card */
.card-header {
padding: 0.75rem 1.25rem;
cursor: pointer;
background-color: #f7f7f7;
transition: background-color 0.3s ease;
}
.card-header:hover {
background-color: #e2e2e2;
}
/* Estilos para los títulos de sección */
.section-heading {
text-align: center;
font-weight: bold;
text-decoration: underline;
font-size: 1.1em;
margin-top: 1rem;
margin-bottom: 0.5rem;
}
/* Estilos personalizados para el modal (envío a gestión y rechazo) */
.custom-modal {
background: rgba(0, 0, 0, 0.5);
}
.custom-modal-dialog {
max-width: 500px;
margin: 2rem auto;
}
.custom-modal-content {
border-radius: 0.5rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
border: none;
}
.custom-modal-header {
background-color: #007bff;
color: white;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
padding: 1rem;
}
.custom-modal-body {
padding: 1.5rem;
background-color: #f8f9fa;
}
.custom-modal-footer {
border-top: none;
padding: 1rem;
}
.modal-section-heading {
font-weight: bold;
font-size: 1.1em;
margin-bottom: 0.5rem;
border-bottom: 2px solid #007bff;
padding-bottom: 0.25rem;
}
.custom-file-list .list-group-item {
transition: background-color 0.2s ease;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.custom-file-list .list-group-item:hover {
background-color: #e9ecef;
}
.custom-file-list .list-group-item.active {
background-color: #007bff;
color: white;
border-color: #007bff;
}
.preselect-btn {
background: none;
border: none;
padding: 0;
margin: 0;
font-size: 1.2rem;
cursor: pointer;
}
.preselect-btn i {
transition: color 0.3s ease;
}
.preselect-btn.selected i {
color: #28a745;
}
.preselect-btn:not(.selected) i {
color: #6c757d;
}
</style>
<h1>Denuncias Pendientes</h1>
<!-- Campo de búsqueda en tiempo real -->
<input type="text"
class="form-control"
placeholder="Buscar denuncias..."
@bind="busqueda"
@bind:event="oninput"
style="margin-bottom:20px;" />
@if (!hasLoaded)
{
<div class="alert alert-info">Cargando datos...</div>
}
else if (pendientes == null || !pendientes.Any())
{
<p>No hay denuncias pendientes.</p>
}
else
{
<div class="pendientes-list">
@foreach (var denuncia in pendientes.Where(d =>
string.IsNullOrWhiteSpace(busqueda) ||
d.Id_Denuncia.ToString().Contains(busqueda) ||
(!string.IsNullOrEmpty(d.Asunto) && d.Asunto.Contains(busqueda, StringComparison.OrdinalIgnoreCase)) ||
(!string.IsNullOrEmpty(d.Nombre) && d.Nombre.Contains(busqueda, StringComparison.OrdinalIgnoreCase)) ||
(!string.IsNullOrEmpty(d.Apellidos) && d.Apellidos.Contains(busqueda, StringComparison.OrdinalIgnoreCase))
))
{
var collapseId = $"collapse{denuncia.Id_Denuncia}";
<div class="card mb-2 collapse-card">
<div class="card-header d-flex justify-content-between align-items-center"
data-bs-toggle="collapse"
data-bs-target="#@collapseId"
aria-expanded="false"
aria-controls="@collapseId">
<div class="d-flex align-items-center">
<h5 class="mb-0">Denuncia ID: @denuncia.Id_Denuncia</h5>
</div>
<div>
<button class="btn btn-success btn-sm me-2"
@onclick:stopPropagation="true"
@onclick="() => OpenEnviarAGestionaModal(denuncia)">
Configurar subida
</button>
<button class="btn btn-danger btn-sm"
@onclick:stopPropagation="true"
@onclick="() => OpenRechazarModal(denuncia)">
Rechazar denuncia
</button>
</div>
</div>
<div id="@collapseId" class="collapse">
<div class="card-body">
<!-- Datos Generales -->
<h5 class="section-heading">Datos Generales</h5>
<dl class="row">
@if (denuncia.Id_RegistroDenuncia != 0)
{
<dt class="col-sm-3">ID Registro Denuncia</dt>
<dd class="col-sm-9">@denuncia.Id_RegistroDenuncia</dd>
}
@if (denuncia.Id_Denuncia != 0)
{
<dt class="col-sm-3">ID Denuncia</dt>
<dd class="col-sm-9">@denuncia.Id_Denuncia</dd>
}
@if (denuncia.Fecha != DateTime.MinValue)
{
<dt class="col-sm-3">Fecha</dt>
<dd class="col-sm-9">@denuncia.Fecha.ToString("dd/MM/yyyy HH:mm")</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Expediente_Gestiona))
{
<dt class="col-sm-3">Expediente Gestiona</dt>
<dd class="col-sm-9">@denuncia.Expediente_Gestiona</dd>
}
@if (denuncia.Id_Persona_Gestiona != 0)
{
<dt class="col-sm-3">ID Persona Gestiona</dt>
<dd class="col-sm-9">@denuncia.Id_Persona_Gestiona</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Etiqueta))
{
<dt class="col-sm-3">Etiqueta</dt>
<dd class="col-sm-9">@denuncia.Etiqueta</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Estado))
{
<dt class="col-sm-3">Estado</dt>
<dd class="col-sm-9">@denuncia.Estado</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Tipo_Denuncia))
{
<dt class="col-sm-3">Tipo Denuncia</dt>
<dd class="col-sm-9">@denuncia.Tipo_Denuncia</dd>
}
</dl>
<!-- Datos del Denunciante -->
@if (!string.IsNullOrWhiteSpace(denuncia.Nombre) ||
!string.IsNullOrWhiteSpace(denuncia.Apellidos) ||
!string.IsNullOrWhiteSpace(denuncia.Sexo) ||
!string.IsNullOrWhiteSpace(denuncia.Dni))
{
<h5 class="section-heading">Datos del Denunciante</h5>
<dl class="row">
@if (!string.IsNullOrWhiteSpace(denuncia.Nombre))
{
<dt class="col-sm-3">Nombre</dt>
<dd class="col-sm-9">@denuncia.Nombre</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Apellidos))
{
<dt class="col-sm-3">Apellidos</dt>
<dd class="col-sm-9">@denuncia.Apellidos</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Sexo))
{
<dt class="col-sm-3">Sexo</dt>
<dd class="col-sm-9">@denuncia.Sexo</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Dni))
{
<dt class="col-sm-3">DNI</dt>
<dd class="col-sm-9">@denuncia.Dni</dd>
}
</dl>
}
<!-- Detalles de la Denuncia -->
<h5 class="section-heading">Detalles de la Denuncia</h5>
<dl class="row">
<dt class="col-sm-3">Asunto</dt>
<dd class="col-sm-9">@denuncia.Asunto</dd>
<dt class="col-sm-3">A Quien Denuncia</dt>
<dd class="col-sm-9">@denuncia.A_Quien_Denuncia</dd>
<dt class="col-sm-3">Descripción Denuncia</dt>
<dd class="col-sm-9">@denuncia.Descripcion_Denuncia</dd>
<dt class="col-sm-3">Denunciado Ante Inst</dt>
<dd class="col-sm-9">@denuncia.Denunciado_Ante_Inst</dd>
@if (!string.IsNullOrWhiteSpace(denuncia.Modalidad_Informacion))
{
<dt class="col-sm-3">Modalidad Información</dt>
<dd class="col-sm-9">@denuncia.Modalidad_Informacion</dd>
}
<dt class="col-sm-3">Lugar Hechos</dt>
<dd class="col-sm-9">@denuncia.Lugar_Hechos</dd>
@if (denuncia.Fecha_Hechos != DateTime.MinValue)
{
<dt class="col-sm-3">Fecha Hechos</dt>
<dd class="col-sm-9">@denuncia.Fecha_Hechos.ToString("dd/MM/yyyy")</dd>
}
</dl>
<!-- Datos de Notificación -->
<h5 class="section-heading">Datos de Notificación</h5>
<dl class="row">
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Preferencia))
{
<dt class="col-sm-3">Notificación Preferencia</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Preferencia</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Electronica))
{
<dt class="col-sm-3">Notificación Electrónica</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Electronica</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Correo_Electronico))
{
<dt class="col-sm-3">Correo Electrónico</dt>
<dd class="col-sm-9">@denuncia.Correo_Electronico</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Sms))
{
<dt class="col-sm-3">Notificación SMS</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Sms</dd>
}
</dl>
<!-- Otros -->
<h5 class="section-heading">Otros</h5>
<dl class="row">
@if (denuncia.Condiciones)
{
<dt class="col-sm-3">Condiciones</dt>
<dd class="col-sm-9">Sí</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Comments))
{
<dt class="col-sm-3">Comentarios</dt>
<dd class="col-sm-9"><pre>@denuncia.Comments</pre></dd>
}
</dl>
<!-- Ficheros adjuntos (lista principal) -->
@if (ficherosAdjuntos.ContainsKey(denuncia.Id_Denuncia) && ficherosAdjuntos[denuncia.Id_Denuncia].Any())
{
<h5 class="section-heading">Ficheros Adjuntos</h5>
<table class="table table-striped">
<thead>
<tr>
<th class="seleccionar-col">Seleccionar</th>
<th>Nombre</th>
<th>Tamaño (bytes)</th>
<th>Ver</th>
</tr>
</thead>
<tbody>
@foreach (var fichero in ficherosAdjuntos[denuncia.Id_Denuncia])
{
<tr>
<td class="seleccionar-col">
<button class="preselect-btn @(preselectedFicheros.ContainsKey(denuncia.Id_Denuncia) && preselectedFicheros[denuncia.Id_Denuncia] == fichero.NombreFichero ? "selected" : "")"
@onclick="() => PreselectFichero(denuncia.Id_Denuncia, fichero.NombreFichero)">
@if (preselectedFicheros.ContainsKey(denuncia.Id_Denuncia) && preselectedFicheros[denuncia.Id_Denuncia] == fichero.NombreFichero)
{
<i class="bi bi-check-circle-fill"></i>
}
else
{
<i class="bi bi-circle"></i>
}
</button>
</td>
<td>@fichero.NombreFichero</td>
<td>@fichero.Fichero.Length</td>
<td>
<a class="btn btn-primary btn-sm" href="#"
onclick="openFile(event, '@Convert.ToBase64String(fichero.Fichero)', '@GetContentType(fichero.NombreFichero)');">
<i class="bi bi-eye"></i> Ver
</a>
</td>
</tr>
}
</tbody>
</table>
}
</div>
</div>
</div>
}
</div>
}
<script>
function createObjectUrl(base64, contentType) {
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
const blob = new Blob([bytes], { type: contentType });
return URL.createObjectURL(blob);
}
function openFile(event, base64, contentType) {
event.preventDefault();
event.stopPropagation();
const objectUrl = createObjectUrl(base64, contentType);
window.open(objectUrl, '_blank');
}
</script>
@* Modal para Configurar Subida a Gestión *@
@if (showModal && selectedDenuncias != null)
{
<div class="modal custom-modal fade show" style="display:block;" tabindex="-1">
<div class="modal-dialog custom-modal-dialog">
<div class="modal-content custom-modal-content">
<div class="modal-header custom-modal-header">
<h5 class="modal-title">Enviar a Gestión</h5>
<button type="button" class="btn-close" @onclick="CloseModal"></button>
</div>
<div class="modal-body custom-modal-body">
<h6 class="modal-section-heading">Información de la Denuncia</h6>
<div class="mb-3">
<input type="text" class="form-control" @bind="nuevoNombre" placeholder="Ingrese el nombre de la denuncia" />
</div>
<h6 class="modal-section-heading">Selecciona el Fichero</h6>
<div class="mb-3">
<ul class="list-group custom-file-list">
@if (ficherosAdjuntos.ContainsKey(selectedDenuncias.Id_Denuncia))
{
foreach (var fichero in ficherosAdjuntos[selectedDenuncias.Id_Denuncia])
{
<li class="list-group-item @(archivoSeleccionado == fichero.NombreFichero ? "active" : "")"
@onclick="() => SelectArchivo(fichero.NombreFichero)">
<span>@fichero.NombreFichero</span>
<button class="btn btn-primary btn-sm"
onclick="openFile(event, '@Convert.ToBase64String(fichero.Fichero)', '@GetContentType(fichero.NombreFichero)')">
<i class="bi bi-eye"></i> Ver
</button>
</li>
}
}
<li class="list-group-item @(archivoSeleccionado == reportTxt ? "active" : "")"
@onclick="() => SelectArchivo(reportTxt)">
<span>report.txt</span>
</li>
</ul>
</div>
</div>
<div class="modal-footer custom-modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CloseModal">Cancelar</button>
<button type="button" class="btn btn-primary" @onclick="ConfirmarEnvio">Confirmar</button>
</div>
</div>
</div>
</div>
<div class="modal-backdrop fade show"></div>
}
@* Modal para Rechazar Denuncia *@
@if (showModalRechazo && selectedDenuncias != null)
{
<div class="modal custom-modal fade show" style="display:block;" tabindex="-1">
<div class="modal-dialog custom-modal-dialog">
<div class="modal-content custom-modal-content">
<div class="modal-header custom-modal-header">
<h5 class="modal-title">Rechazar Denuncia</h5>
<button type="button" class="btn-close" @onclick="CloseRechazoModal"></button>
</div>
<div class="modal-body custom-modal-body">
<h6 class="modal-section-heading">Motivo de Rechazo</h6>
<div class="mb-3">
<input type="text" class="form-control" @bind="motivoRechazo" placeholder="Ingrese el motivo de rechazo" />
</div>
</div>
<div class="modal-footer custom-modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CloseRechazoModal">Cancelar</button>
<button type="button" class="btn btn-primary" @onclick="ConfirmarRechazo">Confirmar</button>
</div>
</div>
</div>
</div>
<div class="modal-backdrop fade show"></div>
}
@code {
private const string reportTxt = "report.txt";
private const string FIXED_PATH = @"C:\ZipsDenuncias";
private string busqueda = "";
private List<DenunciasGestiona> pendientes = new();
private Dictionary<int, List<FicherosDenuncias>> ficherosAdjuntos = new();
private Dictionary<int, string> preselectedFicheros = new();
private const string DENUNCIAS_JSON = @"C:\ZipsDenuncias\denuncias.json";
private const string FICHEROS_JSON = @"C:\ZipsDenuncias\ficheros.json";
private bool hasLoaded = false;
private bool showModal = false;
private bool showModalRechazo = false;
private DenunciasGestiona selectedDenuncias;
private string nuevoNombre;
private string archivoSeleccionado = reportTxt;
private string motivoRechazo;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (string.IsNullOrEmpty(userState.Token))
{
Navigation.NavigateTo("/", true);
return;
}
await CargarDatosAsync();
}
}
private async Task CargarDatosAsync()
{
var todas = await CargarDenunciasJsonAsync();
pendientes = todas.Where(d => d.Expediente_Gestiona == "Pendiente").ToList();
var listaF = await CargarFicherosJsonAsync();
ficherosAdjuntos = listaF
.GroupBy(f => f.Id_Denuncia)
.ToDictionary(g => g.Key, g => g.ToList());
hasLoaded = true;
StateHasChanged();
}
private async Task<List<DenunciasGestiona>> CargarDenunciasJsonAsync()
{
if (!File.Exists(DENUNCIAS_JSON)) return new();
var json = await File.ReadAllTextAsync(DENUNCIAS_JSON);
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<DenunciasGestiona>>(json)
?? new List<DenunciasGestiona>();
}
private async Task<List<FicherosDenuncias>> CargarFicherosJsonAsync()
{
if (!File.Exists(FICHEROS_JSON)) return new();
var json = await File.ReadAllTextAsync(FICHEROS_JSON);
return Newtonsoft.Json.JsonConvert.DeserializeObject<List<FicherosDenuncias>>(json)
?? new List<FicherosDenuncias>();
}
private async Task ConfirmarEnvio()
{
if (selectedDenuncias == null)
return;
// 1) Recolectar todos los ficheros adjuntos
var todos = new List<(string FileName, byte[] Content)>();
if (ficherosAdjuntos.TryGetValue(selectedDenuncias.Id_Denuncia, out var listaAdjuntos))
{
todos.AddRange(listaAdjuntos.Select(f => (f.NombreFichero, f.Fichero)));
}
// 2) Buscar y añadir report.txt correspondiente a esta denuncia
// Se busca en todos los subdirectorios de FIXED_PATH los report.txt
var reportFiles = Directory.GetFiles(FIXED_PATH, "report.txt", SearchOption.AllDirectories);
foreach (var rpt in reportFiles)
{
// Leer encabezado para verificar que es el report de esta denuncia
var contenido = await File.ReadAllTextAsync(rpt, Encoding.UTF8);
if (contenido.Contains($"ID: {selectedDenuncias.Id_Denuncia}"))
{
todos.Add(("report.txt", Encoding.UTF8.GetBytes(contenido)));
break;
}
}
// 3) Fusionar todos los ficheros (incluyendo report.txt) en un único PDF
var pdfBytes = PdfHelper.MergeFilesToPdf(todos);
// 4) Guardar el PDF combinado
var outDir = Path.Combine(FIXED_PATH, "CombinedPdfs");
Directory.CreateDirectory(outDir);
var pdfName = $"Denuncia_{selectedDenuncias.Id_Denuncia}.pdf";
var pdfPath = Path.Combine(outDir, pdfName);
await File.WriteAllBytesAsync(pdfPath, pdfBytes);
// 5) Actualizar la denuncia
selectedDenuncias.ArchivoElegido = pdfName;
selectedDenuncias.NombreDenuncia = nuevoNombre;
selectedDenuncias.Expediente_Gestiona = "Gestiona";
selectedDenuncias.FechaSubidaAGestiona = DateTime.Now;
await ActualizarDenunciaAsync(selectedDenuncias);
// 6) Refrescar UI
pendientes.Remove(selectedDenuncias);
CloseModal();
StateHasChanged();
}
private async Task ConfirmarRechazo()
{
if (selectedDenuncias == null) return;
selectedDenuncias.Expediente_Gestiona = "Rechazada";
selectedDenuncias.NombreDenuncia = motivoRechazo;
selectedDenuncias.FechaSubidaAGestiona = DateTime.Now;
await ActualizarDenunciaAsync(selectedDenuncias);
pendientes.Remove(selectedDenuncias);
CloseRechazoModal();
StateHasChanged();
}
private async Task ActualizarDenunciaAsync(DenunciasGestiona d)
{
var todas = await CargarDenunciasJsonAsync();
var reg = todas.FirstOrDefault(x => x.Id_Denuncia == d.Id_Denuncia);
if (reg != null)
{
reg.Expediente_Gestiona = d.Expediente_Gestiona;
reg.ArchivoElegido = d.ArchivoElegido;
reg.NombreDenuncia = d.NombreDenuncia;
reg.FechaSubidaAGestiona = d.FechaSubidaAGestiona;
}
var json = Newtonsoft.Json.JsonConvert.SerializeObject(todas, Newtonsoft.Json.Formatting.Indented);
await File.WriteAllTextAsync(DENUNCIAS_JSON, json);
}
private void OpenEnviarAGestionaModal(DenunciasGestiona d)
{
selectedDenuncias = d;
nuevoNombre = string.Empty;
archivoSeleccionado = preselectedFicheros.GetValueOrDefault(d.Id_Denuncia, reportTxt);
showModal = true;
}
private void OpenRechazarModal(DenunciasGestiona d)
{
selectedDenuncias = d;
motivoRechazo = string.Empty;
showModalRechazo = true;
}
private void CloseModal() =>
(showModal, selectedDenuncias, nuevoNombre, archivoSeleccionado) =
(false, null, string.Empty, reportTxt);
private void CloseRechazoModal() =>
(showModalRechazo, selectedDenuncias, motivoRechazo) =
(false, null, string.Empty);
private void SelectArchivo(string f) => archivoSeleccionado = f;
private void PreselectFichero(int id, string f) => preselectedFicheros[id] = f;
private string GetContentType(string fileName)
{
return Path.GetExtension(fileName).ToLowerInvariant() switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".pdf" => "application/pdf",
".txt" => "text/plain",
_ => "application/octet-stream",
};
}
}

View File

@@ -0,0 +1,380 @@
@page "/Rechazados"
@rendermode InteractiveServer
@using GestionaDenunciasAN.Models
@using System.Globalization
@attribute [StreamRendering]
@inject GestionaDenunciasAN.Models.UserState userState
@inject NavigationManager Navigation
<PageTitle>Denuncias Rechazadas</PageTitle>
<style>
/* Contenedor para la lista de denuncias */
.rechazados-list {
margin-top: 20px;
}
/* Estilo general para cada card */
.collapse-card {
margin-bottom: 1rem;
border: 1px solid #ccc;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.collapse-card:hover {
transform: scale(1.02);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
/* Aplicar fondo rojo a toda la card */
.Rechazada {
background-color: #f8d7da;
color: #721c24;
}
/* Forzar fondo transparente en cabecera y cuerpo para que hereden el color de la card */
.Rechazada .card-header,
.Rechazada .card-body {
background-color: transparent;
}
/* Estilos para la cabecera de la card */
.card-header {
padding: 0.75rem 1.25rem;
cursor: pointer;
}
.card-header h5 {
margin: 0;
font-size: 1.1rem;
}
.header-info {
font-size: 0.9rem;
margin-top: 0.5rem;
}
.header-info span {
margin-right: 15px;
}
/* Estilos para el cuerpo de la card */
.card-body {
padding: 1.25rem;
}
/* Estilos para los títulos de sección dentro de la card */
.section-heading {
text-align: center;
font-weight: bold;
text-decoration: underline;
font-size: 1.1em;
margin-top: 1rem;
margin-bottom: 0.5rem;
}
</style>
<h1>Denuncias Rechazadas</h1>
<!-- Campo de búsqueda -->
<input type="text"
class="form-control"
placeholder="Buscar denuncias..."
@bind="busqueda"
@bind:event="oninput"
style="margin-bottom:20px;" />
@if (!hasLoaded)
{
<div class="alert alert-info">Cargando datos...</div>
}
else if (denunciasRechazadas == null || !denunciasRechazadas.Any())
{
<p>No hay denuncias rechazadas.</p>
}
else
{
<div class="rechazados-list">
@foreach (var denuncia in denunciasRechazadas.Where(d =>
string.IsNullOrWhiteSpace(busqueda) ||
d.Id_Denuncia.ToString().Contains(busqueda) ||
(!string.IsNullOrEmpty(d.NombreDenuncia) && d.NombreDenuncia.Contains(busqueda, StringComparison.OrdinalIgnoreCase)) ||
(!string.IsNullOrEmpty(d.Estado) && d.Estado.Contains(busqueda, StringComparison.OrdinalIgnoreCase))
))
{
var collapseId = $"collapse{denuncia.Id_Denuncia}";
<div class="card collapse-card Rechazada">
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#@collapseId" aria-expanded="false" aria-controls="@collapseId">
<h5 class="mb-0">Denuncia ID: @denuncia.Id_Denuncia</h5>
<div class="header-info">
<span><strong>Estado:</strong> @denuncia.Estado</span>
<span><strong>Motivo de rechazo:</strong> @denuncia.NombreDenuncia</span>
<span><strong>Fecha Rechazada:</strong> @denuncia.FechaSubidaAGestiona.ToString("dd/MM/yyyy")</span>
<span><strong>Hora Rechazada:</strong> @denuncia.FechaSubidaAGestiona.ToString("HH:mm")</span>
</div>
</div>
<div id="@collapseId" class="collapse">
<div class="card-body">
<!-- Datos Generales -->
<h5 class="section-heading">Datos Generales</h5>
<dl class="row">
@if (denuncia.Id_RegistroDenuncia != 0)
{
<dt class="col-sm-3">ID Registro Denuncia</dt>
<dd class="col-sm-9">@denuncia.Id_RegistroDenuncia</dd>
}
@if (denuncia.Id_Denuncia != 0)
{
<dt class="col-sm-3">ID Denuncia</dt>
<dd class="col-sm-9">@denuncia.Id_Denuncia</dd>
}
@if (denuncia.Fecha != DateTime.MinValue)
{
<dt class="col-sm-3">Fecha</dt>
<dd class="col-sm-9">@denuncia.Fecha.ToString("dd/MM/yyyy HH:mm")</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Expediente_Gestiona))
{
<dt class="col-sm-3">Expediente Gestión</dt>
<dd class="col-sm-9">@denuncia.Expediente_Gestiona</dd>
}
@if (denuncia.Id_Persona_Gestiona != 0)
{
<dt class="col-sm-3">ID Persona Gestión</dt>
<dd class="col-sm-9">@denuncia.Id_Persona_Gestiona</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Etiqueta))
{
<dt class="col-sm-3">Etiqueta</dt>
<dd class="col-sm-9">@denuncia.Etiqueta</dd>
}
</dl>
<!-- Datos del Denunciante -->
@if (!string.IsNullOrWhiteSpace(denuncia.Nombre) ||
!string.IsNullOrWhiteSpace(denuncia.Apellidos) ||
!string.IsNullOrWhiteSpace(denuncia.Sexo) ||
!string.IsNullOrWhiteSpace(denuncia.Dni))
{
<h5 class="section-heading">Datos del Denunciante</h5>
<dl class="row">
@if (!string.IsNullOrWhiteSpace(denuncia.Nombre))
{
<dt class="col-sm-3">Nombre</dt>
<dd class="col-sm-9">@denuncia.Nombre</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Apellidos))
{
<dt class="col-sm-3">Apellidos</dt>
<dd class="col-sm-9">@denuncia.Apellidos</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Sexo))
{
<dt class="col-sm-3">Sexo</dt>
<dd class="col-sm-9">@denuncia.Sexo</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Dni))
{
<dt class="col-sm-3">DNI</dt>
<dd class="col-sm-9">@denuncia.Dni</dd>
}
</dl>
}
<!-- Detalles de la Denuncia -->
<h5 class="section-heading">Detalles de la Denuncia</h5>
<dl class="row">
<dt class="col-sm-3">Asunto</dt>
<dd class="col-sm-9">@denuncia.Asunto</dd>
<dt class="col-sm-3">A Quien Denuncia</dt>
<dd class="col-sm-9">@denuncia.A_Quien_Denuncia</dd>
<dt class="col-sm-3">Descripción Denuncia</dt>
<dd class="col-sm-9">@denuncia.Descripcion_Denuncia</dd>
<dt class="col-sm-3">Denunciado Ante Inst</dt>
<dd class="col-sm-9">@denuncia.Denunciado_Ante_Inst</dd>
@if (!string.IsNullOrWhiteSpace(denuncia.Modalidad_Informacion))
{
<dt class="col-sm-3">Modalidad Información</dt>
<dd class="col-sm-9">@denuncia.Modalidad_Informacion</dd>
}
<dt class="col-sm-3">Lugar Hechos</dt>
<dd class="col-sm-9">@denuncia.Lugar_Hechos</dd>
@if (denuncia.Fecha_Hechos != DateTime.MinValue)
{
<dt class="col-sm-3">Fecha Hechos</dt>
<dd class="col-sm-9">@denuncia.Fecha_Hechos.ToString("dd/MM/yyyy")</dd>
}
</dl>
<!-- Datos de Notificación -->
<h5 class="section-heading">Datos de Notificación</h5>
<dl class="row">
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Preferencia))
{
<dt class="col-sm-3">Notificación Preferencia</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Preferencia</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Electronica))
{
<dt class="col-sm-3">Notificación Electrónica</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Electronica</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Correo_Electronico))
{
<dt class="col-sm-3">Correo Electrónico</dt>
<dd class="col-sm-9">@denuncia.Correo_Electronico</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Notificacion_Sms))
{
<dt class="col-sm-3">Notificación SMS</dt>
<dd class="col-sm-9">@denuncia.Notificacion_Sms</dd>
}
</dl>
<!-- Otros -->
<h5 class="section-heading">Otros</h5>
<dl class="row">
@if (denuncia.Condiciones)
{
<dt class="col-sm-3">Condiciones</dt>
<dd class="col-sm-9">Sí</dd>
}
@if (!string.IsNullOrWhiteSpace(denuncia.Comments))
{
<dt class="col-sm-3">Comentarios</dt>
<dd class="col-sm-9"><pre>@denuncia.Comments</pre></dd>
}
</dl>
<!-- Ficheros adjuntos -->
@if (ficherosAdjuntos.ContainsKey(denuncia.Id_Denuncia) && ficherosAdjuntos[denuncia.Id_Denuncia].Any())
{
<h5 class="section-heading">Ficheros Adjuntos</h5>
<table class="table table-striped">
<thead>
<tr>
<th>Nombre</th>
<th>Tamaño (bytes)</th>
<th>Ver</th>
</tr>
</thead>
<tbody>
@foreach (var fichero in ficherosAdjuntos[denuncia.Id_Denuncia])
{
<tr>
<td>@fichero.NombreFichero</td>
<td>@fichero.Fichero.Length</td>
<td>
<a class="btn btn-primary btn-sm" href="#"
onclick="openFile(event, '@Convert.ToBase64String(fichero.Fichero)', '@GetContentType(fichero.NombreFichero)');">
<i class="bi bi-eye"></i> Ver
</a>
</td>
</tr>
}
</tbody>
</table>
}
</div>
</div>
</div>
}
</div>
}
<script>
function createObjectUrl(base64, contentType) {
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
const blob = new Blob([bytes], { type: contentType });
return URL.createObjectURL(blob);
}
function openFile(event, base64, contentType) {
event.preventDefault();
event.stopPropagation();
const objectUrl = createObjectUrl(base64, contentType);
window.open(objectUrl, '_blank');
}
</script>
@code {
private List<DenunciasGestiona> denunciasRechazadas = new();
private Dictionary<int, List<FicherosDenuncias>> ficherosAdjuntos = new();
// Variable para la búsqueda
private string busqueda = "";
private const string DENUNCIAS_JSON = @"C:\ZipsDenuncias\denuncias.json";
private const string FICHEROS_JSON = @"C:\ZipsDenuncias\ficheros.json";
private bool isLoading = false;
private bool hasLoaded = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (string.IsNullOrEmpty(userState.Token))
{
Navigation.NavigateTo("/", true);
return;
}
isLoading = true;
StateHasChanged();
await CargarRechazadasAsync();
await CargarFicherosAdjuntosAsync();
isLoading = false;
hasLoaded = true;
StateHasChanged();
}
}
private async Task CargarRechazadasAsync()
{
var todas = await CargarDenunciasJsonAsync();
denunciasRechazadas = todas.Where(d => d.Expediente_Gestiona == "Rechazada").ToList();
}
private async Task<List<DenunciasGestiona>> CargarDenunciasJsonAsync()
{
if (File.Exists(DENUNCIAS_JSON))
{
var json = await File.ReadAllTextAsync(DENUNCIAS_JSON);
var lista = Newtonsoft.Json.JsonConvert.DeserializeObject<List<DenunciasGestiona>>(json);
return lista ?? new List<DenunciasGestiona>();
}
return new List<DenunciasGestiona>();
}
private async Task<List<FicherosDenuncias>> CargarFicherosJsonAsync()
{
if (File.Exists(FICHEROS_JSON))
{
var json = await File.ReadAllTextAsync(FICHEROS_JSON);
var lista = Newtonsoft.Json.JsonConvert.DeserializeObject<List<FicherosDenuncias>>(json);
return lista ?? new List<FicherosDenuncias>();
}
return new List<FicherosDenuncias>();
}
private async Task CargarFicherosAdjuntosAsync()
{
var listaFicheros = await CargarFicherosJsonAsync();
ficherosAdjuntos = listaFicheros.GroupBy(f => f.Id_Denuncia)
.ToDictionary(g => g.Key, g => g.ToList());
}
private string GetContentType(string fileName)
{
var ext = Path.GetExtension(fileName).ToLowerInvariant();
return ext switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".pdf" => "application/pdf",
".txt" => "text/plain",
_ => "application/octet-stream",
};
}
}

View File

@@ -0,0 +1,6 @@
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>

View File

@@ -0,0 +1,10 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using GestionaDenunciasAN
@using GestionaDenunciasAN.Components

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup Label="Globals">
<SccProjectName>SAK</SccProjectName>
<SccProvider>SAK</SccProvider>
<SccAuxPath>SAK</SccAuxPath>
<SccLocalPath>SAK</SccLocalPath>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazor.Bootstrap" Version="3.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="PdfSharpCore" Version="1.3.67" />
<PackageReference Include="Serialize.Linq" Version="4.0.167" />
<PackageReference Include="System.Drawing.Common" Version="10.0.0-preview.3.25173.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\bdAntifraude\bdAntifraude.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
namespace GestionaDenunciasAN.Helpers
{
public static class PdfHelper
{
/// <summary>
/// Fusiona varios ficheros (PDF, imágenes, TXT) en un único PDF.
/// Los .txt se renderizan con márgenes iguales, alineación a la izquierda y ajuste de líneas,
/// preservando líneas en blanco.
/// </summary>
/// <param name="files">Secuencia de tuplas (FileName, ContentBytes)</param>
/// <returns>Bytes del PDF combinado</returns>
public static byte[] MergeFilesToPdf(IEnumerable<(string FileName, byte[] Content)> files)
{
if (files == null) throw new ArgumentNullException(nameof(files));
var outputDoc = new PdfDocument();
foreach (var (fileName, content) in files)
{
if (string.IsNullOrEmpty(fileName) || content == null || content.Length == 0)
throw new InvalidOperationException($"El archivo '{fileName}' no tiene contenido.");
var ext = Path.GetExtension(fileName).ToLowerInvariant();
switch (ext)
{
case ".pdf":
using (var src = PdfReader.Open(new MemoryStream(content), PdfDocumentOpenMode.Import))
foreach (var page in src.Pages)
outputDoc.AddPage(page);
break;
case ".jpg":
case ".jpeg":
case ".png":
case ".gif":
using (var img = XImage.FromStream(() => new MemoryStream(content)))
{
var page = outputDoc.AddPage();
page.Width = XUnit.FromPoint(img.PointWidth);
page.Height = XUnit.FromPoint(img.PointHeight);
using var gfxImg = XGraphics.FromPdfPage(page);
gfxImg.DrawImage(img, 0, 0, page.Width, page.Height);
}
break;
case ".txt":
// Renderizado de TXT con margen y ajuste de líneas, preservando líneas en blanco
var text = Encoding.UTF8.GetString(content);
PdfPage pageTxt = outputDoc.AddPage();
XGraphics gfxTxt = XGraphics.FromPdfPage(pageTxt);
const double marginLeft = 40;
const double marginRight = 40;
const double marginTop = 40;
const double marginBottom = 40;
var font = new XFont("Courier New", 10, XFontStyle.Regular);
var sample = gfxTxt.MeasureString("Ay", font);
var lineHeight = sample.Height * 1.2;
double y = marginTop;
double pageWidth = pageTxt.Width.Point;
double pageHeight = pageTxt.Height.Point;
double maxWidth = pageWidth - marginLeft - marginRight;
foreach (var origLine in text.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n'))
{
// Línea en blanco: preservarla
if (string.IsNullOrWhiteSpace(origLine))
{
y += lineHeight;
if (y + lineHeight > pageHeight - marginBottom)
{
gfxTxt.Dispose();
pageTxt = outputDoc.AddPage();
gfxTxt = XGraphics.FromPdfPage(pageTxt);
y = marginTop;
}
continue;
}
var words = origLine.Split(' ');
string currentLine = string.Empty;
foreach (var word in words)
{
var testLine = string.IsNullOrEmpty(currentLine) ? word : currentLine + " " + word;
var size = gfxTxt.MeasureString(testLine, font);
if (size.Width <= maxWidth)
{
currentLine = testLine;
}
else
{
// Dibujar la línea acumulada
gfxTxt.DrawString(
currentLine,
font,
XBrushes.Black,
new XRect(marginLeft, y, maxWidth, lineHeight),
XStringFormats.TopLeft);
y += lineHeight;
currentLine = word;
// Paginación si se sale por abajo
if (y + lineHeight > pageHeight - marginBottom)
{
gfxTxt.Dispose();
pageTxt = outputDoc.AddPage();
gfxTxt = XGraphics.FromPdfPage(pageTxt);
y = marginTop;
}
}
}
// Dibujar la última línea del párrafo
if (!string.IsNullOrEmpty(currentLine))
{
gfxTxt.DrawString(
currentLine,
font,
XBrushes.Black,
new XRect(marginLeft, y, maxWidth, lineHeight),
XStringFormats.TopLeft);
y += lineHeight;
if (y + lineHeight > pageHeight - marginBottom)
{
gfxTxt.Dispose();
pageTxt = outputDoc.AddPage();
gfxTxt = XGraphics.FromPdfPage(pageTxt);
y = marginTop;
}
}
}
gfxTxt.Dispose();
break;
default:
throw new NotSupportedException($"Extensión no soportada: {ext}");
}
}
using var ms = new MemoryStream();
outputDoc.Save(ms);
return ms.ToArray();
}
}
}

View File

@@ -0,0 +1,108 @@
using System;
namespace GestionaDenunciasAN.Models
{
public class DenunciasGestiona
{
// Propiedades existentes
public int Id_RegistroDenuncia { get; set; }
public int Id_Denuncia { get; set; }
public DateTime Fecha { get; set; }
public string Expediente_Gestiona { get; set; }
public int Id_Persona_Gestiona { get; set; }
public string Etiqueta { get; set; }
public string Estado { get; set; }
public string Tipo_Denuncia { get; set; }
public string? Nombre { get; set; }
public string? Apellidos { get; set; }
public string? Sexo { get; set; }
public string? Dni { get; set; }
public string Asunto { get; set; }
public string A_Quien_Denuncia { get; set; }
public string Descripcion_Denuncia { get; set; }
public string Denunciado_Ante_Inst { get; set; }
public string? Modalidad_Informacion { get; set; }
public string Lugar_Hechos { get; set; }
public DateTime Fecha_Hechos { get; set; }
public string? Notificacion_Preferencia { get; set; }
public string? Notificacion_Electronica { get; set; }
public string? Correo_Electronico { get; set; }
public string? Notificacion_Sms { get; set; }
public bool Condiciones { get; set; }
public string? Comments { get; set; }
// Nuevas propiedades
public string NombreDenuncia { get; set; }
public string EstadoDenuncia { get; set; }
public string ArchivoElegido { get; set; }
public DateTime FechaSubidaAGestiona { get; set; } // Nueva propiedad
// Constructor por defecto
public DenunciasGestiona()
{
}
// Constructor parametrizado que inicializa todos los campos, incluida la nueva propiedad
public DenunciasGestiona(
int idRegistroDenuncia,
int idDenuncia,
DateTime fecha,
string expedienteGestiona,
int idPersonaGestiona,
string etiqueta,
string estado,
string tipoDenuncia,
string? nombre,
string? apellidos,
string? sexo,
string? dni,
string asunto,
string aQuienDenuncia,
string descripcionDenuncia,
string denunciadoAnteInst,
string? modalidadInformacion,
string lugarHechos,
DateTime fechaHechos,
string? notificacionPreferencia,
string? notificacionElectronica,
string? correoElectronico,
string? notificacionSms,
bool condiciones,
string? comments,
string nombreDenuncia,
string estadoDenuncia,
string archivoElegido,
DateTime fechaSubidaAGestiona)
{
Id_RegistroDenuncia = idRegistroDenuncia;
Id_Denuncia = idDenuncia;
Fecha = fecha;
Expediente_Gestiona = expedienteGestiona;
Id_Persona_Gestiona = idPersonaGestiona;
Etiqueta = etiqueta;
Estado = estado;
Tipo_Denuncia = tipoDenuncia;
Nombre = nombre;
Apellidos = apellidos;
Sexo = sexo;
Dni = dni;
Asunto = asunto;
A_Quien_Denuncia = aQuienDenuncia;
Descripcion_Denuncia = descripcionDenuncia;
Denunciado_Ante_Inst = denunciadoAnteInst;
Modalidad_Informacion = modalidadInformacion;
Lugar_Hechos = lugarHechos;
Fecha_Hechos = fechaHechos;
Notificacion_Preferencia = notificacionPreferencia;
Notificacion_Electronica = notificacionElectronica;
Correo_Electronico = correoElectronico;
Notificacion_Sms = notificacionSms;
Condiciones = condiciones;
Comments = comments;
NombreDenuncia = nombreDenuncia;
EstadoDenuncia = estadoDenuncia;
ArchivoElegido = archivoElegido;
FechaSubidaAGestiona = fechaSubidaAGestiona;
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
namespace GestionaDenunciasAN.Models
{
public class FicherosDenuncias
{
// Id del fichero
public int Id_Fichero { get; set; }
// Id del tipo de fichero
public int Id_Tipo { get; set; }
// Descripción del fichero (puede ser nula)
public string? Descripcion { get; set; }
// Fecha de creación del fichero
public DateTime Fecha { get; set; }
// Observaciones
public string Observaciones { get; set; }
// Id de la denuncia al que está asociado (FK)
public int Id_Denuncia { get; set; }
// NombreCarpeta_NombreFichero
public string NombreFichero { get; set; }
// Fichero completo en formato byte array (BLOB)
public byte[] Fichero { get; set; }
// Constructor por defecto
public FicherosDenuncias()
{
}
// Constructor parametrizado que inicializa todos los campos
public FicherosDenuncias(
int id_Fichero,
int id_Tipo,
string? descripcion,
DateTime fecha,
string observaciones,
int id_Denuncia,
string nombreFichero,
byte[] fichero)
{
Id_Fichero = id_Fichero;
Id_Tipo = id_Tipo;
Descripcion = descripcion;
Fecha = fecha;
Observaciones = observaciones;
Id_Denuncia = id_Denuncia;
NombreFichero = nombreFichero;
Fichero = fichero;
}
}
}

View File

@@ -0,0 +1,62 @@
namespace GestionaDenunciasAN.Models
{
public class UserState
{
private readonly object _lock = new object();
private string _token;
private string _NombreUsu;
private bool _Mostrar;
public string Token
{
get
{
lock (_lock)
{
return _token;
}
}
set
{
lock (_lock)
{
_token = value;
}
}
}
public string NombreUsu
{
get
{
lock (_lock)
{
return _NombreUsu;
}
}
set
{
lock (_lock)
{
_NombreUsu = value;
}
}
}
public bool Mostrar
{
get
{
lock (_lock)
{
return _Mostrar;
}
}
set
{
lock (_lock)
{
_Mostrar = value;
}
}
}
}
}

View File

@@ -0,0 +1,158 @@
using System.Net.Http.Headers;
using Newtonsoft.Json;
using System.Linq.Expressions;
using Serialize.Linq.Serializers;
using System.Text;
namespace GestionaDenunciasAN.Models
{
public class Utilidades
{
private static IConfiguration Conf { get; set; }
public static string urlSwagger()
{
Conf = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
string swagger = Conf.GetSection("SwaggerCC").Value;
return swagger;
}
public static string urlSwagger2()
{
Conf = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
string swagger = Conf.GetSection("SwaggerVB").Value;
return swagger;
}
public static HttpClient ObtenerCliente(String token, IHttpClientFactory clientFactory)
{
var client = clientFactory.CreateClient();
client.BaseAddress = new Uri(Utilidades.urlSwagger());
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
public static async Task<T> ObtenerObjeto<T>(HttpClient cliente, String uri)
{
var response = await cliente.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var resultContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(resultContent);
}
else
{
return default(T);
}
}
public static async Task<T> ObtenerObjeto<T>(HttpClient cliente, String uri, Expression filtro)
{
var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer());
var serializedExpression = serializer.SerializeText(filtro);
var jsonContent = JsonConvert.SerializeObject(new { Expression = serializedExpression });
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
var response = await cliente.PostAsync(uri, content);
if (response.IsSuccessStatusCode)
{
var resultContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(resultContent);
}
else
{
return default(T);
}
}
public static async Task<T> NuevoObjeto<T>(HttpClient cliente, String uri, T objeto)
{
try
{
// Limpiar propiedades de navegación
VaciarPropiedadesDeNavegacion(objeto);
var response = await cliente.PostAsJsonAsync(uri, objeto);
if (response.IsSuccessStatusCode)
{
var resultContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(resultContent);
}
else
{
return default(T);
}
}
catch (Exception ex)
{
var mess = ex.ToString();
return default(T);
}
}
public static async Task<T> ActualizarObjeto<T>(HttpClient cliente, String uri, T objeto)
{
try
{
// Limpiar propiedades de navegación
VaciarPropiedadesDeNavegacion(objeto);
// Realizar la solicitud PUT
var response = await cliente.PutAsJsonAsync(uri, objeto);
if (response.IsSuccessStatusCode)
{
var resultContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(resultContent);
}
else
{
Console.WriteLine($"Error al actualizar: {response.StatusCode}, {await response.Content.ReadAsStringAsync()}");
return default(T);
}
}
catch (Exception ex)
{
var mess = ex.ToString();
return default(T);
}
}
// Método para limpiar las propiedades de navegación
private static void VaciarPropiedadesDeNavegacion<T>(T objeto)
{
// Obtener todas las propiedades del objeto
var propiedades = typeof(T).GetProperties();
foreach (var propiedad in propiedades)
{
// Verificar si la propiedad es de tipo de navegación
if (propiedad.PropertyType.IsClass && propiedad.PropertyType != typeof(string))
{
// Vaciar la propiedad estableciéndola en null
if (propiedad.CanWrite)
{
propiedad.SetValue(objeto, null);
Console.WriteLine($"Propiedad de navegación '{propiedad.Name}' vaciada.");
}
}
}
}
}
}

View File

@@ -0,0 +1,107 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using GestionaDenunciasAN.Components;
using GestionaDenunciasAN.Models;
var builder = WebApplication.CreateBuilder(args);
// Configurar servicios
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddHttpClient("DefaultClient", client =>
{
client.BaseAddress = new Uri(Utilidades.urlSwagger());
});
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/home";
options.AccessDeniedPath = "/AccessDenied";
});
// Necesario para ver porqu<71> est<73> fallando ciertas cosas que dan el error Circuit
builder.Services.AddServerSideBlazor().AddCircuitOptions(option => { option.DetailedErrors = true; });
builder.Services.AddHttpContextAccessor();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(1);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
builder.Services.AddHttpClient("CertClient").ConfigurePrimaryHttpMessageHandler(() =>
{
return new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual // Forzar la selecci<63>n del certificado
};
});
builder.Services.AddBlazorBootstrap();
builder.Services.AddAntiforgery();
builder.Services.AddSingleton<UserState>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSession();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.Use(async (context, next) =>
{
var userState = context.RequestServices.GetService<UserState>();
var path = context.Request.Path;
// Permitir solicitudes internas y recursos necesarios
if (path == "/" ||
path.StartsWithSegments("/_blazor") ||
path.StartsWithSegments("/Content") ||
path.StartsWithSegments("/Scripts") ||
path.StartsWithSegments("/js") ||
path.StartsWithSegments("/favicon.ico") ||
path.StartsWithSegments("/_framework"))
{
await next();
return;
}
// Redirigir al home si no hay token y la ruta no es p<>blica
if (userState?.Token == null)
{
Console.WriteLine($"Redirigiendo al home desde: {path}");
context.Response.Redirect("/");
return;
}
// Continuar con la solicitud
await next();
});
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();

View File

@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:39417",
"sslPort": 44394
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5119",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7109;http://localhost:5119",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,14 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
//SWAGGER DESARROLLO
//"SwaggerCC": "https://localhost:7135/api/",
//SWAGGER PUBLICADO
"SwaggerCC": "https://sw-antifraude.tecnosis.net/api/"
}

View File

@@ -0,0 +1,715 @@
:root {
--AzulMuyOscuro: rgb(0, 45, 72);
--AzulOscuro: rgb(0, 85, 135);
--AzulMedio: rgb(0, 161, 191);
--AzulClaro: rgb(95, 207, 228);
/* --indigo-parlamento: #172073;
--azul-parlamento: #5BA1BA;
--verde-parlamento: #70A360;*/
--AzulOAAF: #2291d0;
--GrisOscuroOAAF: #343334;
--AzulClaroOAAF: #6fafd3;
--GrisClaroOAAF: #a4a3a4;
--GrisNeutro: #e9e9e9 --blue: #007bff;
--indigo: #6610f2;
--purple: #6f42c1;
--pink: #e83e8c;
--red: #dc3545;
--orange: #fd7e14;
--yellow: #ffc107;
--green: #28a745;
--teal: #20c997;
--cyan: #17a2b8;
--white: #fff;
--gray: #6c757d;
--gray-dark: #343a40;
--primary: #172073;
--secondary: #5BA1BA;
--success: #70A360;
--info: #5BA1BA;
--warning: #ffc107;
--danger: #dc3545;
--light: #f8f9fa;
--dark: #343a40;
--gray: #e9ecef;
--gray400: #ced4da;
--gray500: #adb5bd;
--gray600: #6c757d;
--breakpoint-xs: 0;
--breakpoint-sm: 576px;
--breakpoint-md: 768px;
--breakpoint-lg: 992px;
--breakpoint-xl: 1200px;
--font-family-sans-serif: "Open Sans", sans-serif;
--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
.gestionPropia {
--AzulOAAF: #2291d0;
--GrisOscuroOAAF: #343334;
--AzulClaroOAAF: #6fafd3;
--GrisClaroOAAF: #a4a3a4;
}
.gestionPersonal {
--AzulOAAF: #343334;
--AzulClaroOAAD: #a4a3a4;
--GrisOscuroOAAF: #2291d0;
--GrisClaroOAAF: #6fafd3;
}
@font-face {
font-family: 'RobotoB';
src: url('FUENTES/Roboto-Bold.ttf') format('truetype');
}
@font-face {
font-family: 'RobotoI';
src: url('FUENTES/Roboto-Italic.ttf') format('truetype');
}
@font-face {
font-family: 'Roboto';
src: url('FUENTES/Roboto-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'SatoshiB';
src: url('FUENTES/Satoshi-Black.otf') format('opentype');
}
@font-face {
font-family: 'Satoshi';
src: url('FUENTES/Satoshi-Regular.otf') format('opentype');
}
body {
font-family: 'Satoshi';
}
.btn:focus {
box-shadow:none !important;
}
.falsoImput {
border: 1px solid black;
padding: 7px;
border-radius: 2px;
background-color: #efefef;
}
.hidden {
display:none !important
}
.text-right {
text-align:end;
}
.btnGris {
background-color: #eeeeee;
cursor: pointer;
border: none;
}
.btnGris:hover{
background-color: #bfbfbf;
}
#dropdownNavegador {
left:-30px
}
.lblinputRP {
margin: 0;
font-weight: bold;
/*margin-top: 30px;*/
}
.inputRP {
width:100%;
}
.dropdown-item:hover {
color: white !important;
background-color: var(--AzulMuyOscuro);
}
.tituloLbl {
padding-bottom: 5px;
font-weight: bold;
}
.inputForm {
padding: 10px;
}
.navMov{
display:none;
}
.tablaPicadas {
margin-left: auto;
margin-right: auto;
max-height: 540px;
/*overflow-y: scroll;*/
/*overflow-x: hidden;*/
position: relative;
border: 1px solid black;
border-collapse: collapse;
width: 95%;
background-color: #fafafa;
overflow-x: auto;
}
.tablaPicadas table {
width: 100%;
}
.tablaPicadas td {
padding-left: 5px;
padding-right: 5px;
width: auto;
border: 1px solid black;
border-collapse: collapse;
background-color: white;
}
.tablaPicadas th {
position: sticky;
top: -1px;
padding-left: 5px;
padding-right: 5px;
background-color: var(--AzulOAAF);
color: white;
font-weight: bold;
/*border: 1px solid white;*/
border-collapse: collapse;
text-align: center;
}
.table-striped > tbody > tr:nth-child(odd) > td,
.table-striped > tbody > tr:nth-child(odd) > th {
background-color: white;
}
.popupRP{
width:100%;
height:100vh;
position:fixed;
top:0;
left:0;
background-color:#00000098;
justify-content:center;
align-items:center;
z-index:20;
}
.popupRPCard{
max-width:800px;
padding:10px;
background-color:white;
}
.menuMovil{
display:none;
}
.iconBtn {
background-color: #172073;
color: white;
opacity: 0.6;
cursor: pointer;
padding: 10px;
}
.iconBtn:hover {
opacity:1;
}
.dxbs-pager, .dxbs-gridview .dxbs-pager{
gap:15px;
}
.dxbs-pager .page-size.form-inline{
display:flex;
}
#mensajeDiv {
z-index: 100;
position: fixed;
width: 100%;
height: 100vh;
background-color: #000000b2;
display: none;
justify-content: center;
align-items: center;
}
#mensajeCuerpo{
background-color:white;
padding:15px;
min-width:300px;
max-width:650px;
min-height:150px;
}
#mensajeError{}
#loadingDiv {
z-index:100;
position:fixed;
width:100%;
height:100vh;
background-color:#000000b2;
display:none;
justify-content:center;
align-items:center;
}
#loading {
background-image: url('imagenes/logo-parlamento-blanco@2x.png');
height:50px;
width:200px;
background-position:center;
background-size:cover;
}
.btnAzul {
border-radius: 0;
background-color: #172073;
padding: 15px;
padding-block: 5px;
color: white;
border: none;
opacity: 0.6;
}
.btnAzul:hover {
opacity:1;
}
/*body {
font-family:"Arial"
}*/
.pagina {
padding: 50px;
/* min-height:100vh;
*/ /*background-color:lightgray*/
}
.tituloSeccion {
font-size:30px;
/*font-family:"Arial";*/
font-weight:bold;
color:var(--AzulMuyOscuro)
}
.navBar {
position:fixed;
left:0;
width:300px;
height:100vh;
display:flex;
flex-direction:column;
background-color:var(--AzulOscuro) ;
border-collapse:collapse
}
.nav-link {
cursor:pointer
}
/*.navBarLink {
cursor:pointer;
text-decoration:none !important;
height:50px;
width:100%;*/
/*text-align:center;*/
/*padding-inline:20px;
color:white;
font-size:20px;
line-height:50px;
border-block:solid 1px white;
border-collapse:collapse;
}
.navBarLink:hover {
color:white;
background-color:var(--AzulMuyOscuro)
}
.navBarLinkSecundario {
padding-left:40px;
padding-right:20px;
font-size:15px;
background-color: var(--AzulClaro);
height:40px;
line-height:40px;
}
.navBarLinkSecundario:hover {
background-color: var(--AzulMedio)
}*/
.inputRegPers {
border-radius: 5px;
height: 50px;
padding:10px;
border: solid 1px var(--AzulMuyOscuro);
}
.cbdxbt input {
border-radius: 5px;
height: 50px;
padding: 10px;
border: solid 1px var(--AzulMuyOscuro);
}
.btnOAAFAzul {
user-select: none;
cursor: pointer;
background-color: var(--AzulOAAF);
border-radius: 15px;
font-family: 'Satoshi';
border: none;
color: white !important;
padding: 10px;
text-align: center;
}
.btnOAAFAzul:hover {
opacity: 0.6;
}
.btnOAAFBlack {
user-select: none;
cursor: pointer;
padding: 10px;
text-align: center;
background-color: var(--GrisOscuroOAAF);
border-radius: 15px;
font-family: 'Satoshi';
border: none;
color: white !important;
}
.btnOAAFBlack:hover {
opacity: 0.6;
}
.cbdxbt .input-group-append {
border-bottom-right-radius: 5px;
border-top-right-radius: 5px;
height: 50px;
padding: 10px;
border: solid 1px var(--AzulMuyOscuro);
background-color: var(--AzulOscuro)
}
.cbdxbt .input-group-append::before {
content: "";
height: 100%;
width: 100%;
display:flex;
background-image: url('imagenes/dropParlamento.svg');
background-size:80% 80%;
background-position:center;
background-repeat:no-repeat;
}
.cbdxbt .input-group-append:hover {
background-color: var(--AzulMuyOscuro)
}
.tabsRegPers {
margin-top:50px !important;
display:flex;
gap:30px;
}
.tabsRegPers li a {
text-decoration: none !important;
color: var(--AzulOscuro) !important;
/*font-family: "Open Sans" !important;*/
}
.tabsRegPers li .active a {
font-weight:bold !important;
}
.btnRegPers {
background-color: var(--AzulOscuro);
border-radius: 10px;
min-width: 100%;
color: white;
transition: all 0.2s
}
.btnRegPers:hover {
color: white !important;
background-color: var(--AzulMuyOscuro);
}
.filaInvisible {
display: none;
}
.tablaRegPers {
margin-top:50px;
width:100%;
border-collapse:collapse
}
.camposficha {
padding-left: 15px;
padding-top: 20px;
}
.formatoCampos {
flex-direction: column;
display: flex;
}
.tablaRegPers th {
text-align:center;
background-color:var(--AzulOscuro);
color:white !important;
text-decoration:none !important;
padding:20px;
padding-inline:20px;
overflow-inline:visible;
border-inline:solid 1px white;
width:auto;
white-space:nowrap;
}
.tablaRegPers th .btn-link {
color:white;
}
.tablaRegPers th a {
color: white !important;
text-decoration: none !important;
}
.tablaRegPers th a:hover {
opacity: 0.6;
}
.tablaRegPers td {
padding: 15px !important;
padding-inline: 30px !important;
border: solid 1px black;
}
.linkNavActivo {
background-color: var(--AzulMuyOscuro) !important;
}
.table .card {
border: none !important;
}
.table thead {
text-align: left;
color: var(--AzulOscuro);
border: none !important;
}
.table th {
/*padding: 10px !important;*/
border: none !important;
}
.table td {
/*padding: 5px !important;*/
border: none !important;
}
.table a {
color: var(--AzulOscuro) !important;
}
.table a:hover {
color: var(--AzulMuyOscuro) !important;
}
.table {
border:none !important;
margin-top: 20px;
border-collapse: collapse !important;
}
.table-bordered > :not(caption) > * {
border-width: 0px 0px !important;
}
.table > thead > tr > th {
/*padding: 10px !important;*/
}
.table > tbody > tr > td {
/*padding: 5px !important;*/
border-bottom: 1px solid gray !important;
}
.table > tbody > tr {
border-bottom: 1px solid gray !important;
}
.table > thead > tr {
border-bottom: 1px solid gray !important;
}
/*.table > tbody {
border-collapse: collapse !important;
}
.table > thead {
border-collapse: collapse !important;
}*/
table {
border-collapse: unset;
}
.marTop {
margin-top:25px !important;
}
.nuevaRPT p {
margin-bottom:0;
margin-top:20px;
}
.lblInput {
margin:0;
font-size:12px;
font-weight:bold;
margin-top:30px;
}
.barraArribaHeader {
background-color: var(--azul-parlamento) !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
padding-inline: 10px !important;
}
.barraArribaHeader .nav-link {
padding: 5px !important;
filter: invert(1);
}
.barraArribaHeader .nav-link :hover {
filter: brightness(5);
}
#pa-main-menu {
align-content: center !important;
}
#pa-main-menu .nav-item {
padding-left: 30px !important;
padding-right: 30px !important;
border-bottom: 3px solid white !important;
}
#pa-main-menu .nav-item:hover {
color: var(--AzulMuyOscuro) !important;
border-bottom: 3px solid var(--AzulMuyOscuro) !important;
}
.loadingFrame {
display: flex;
background-color: #000000e5;
position: fixed;
left: 0;
top: 0;
z-index: 10;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
}
.loadingDiv{
padding:20px;
border-radius:5px;
background-color:white;
display:flex;
justify-content:center;
align-items:center;
}
.loadingImg {
background-image: url(imagenes/cargaOAAF.gif);
width: 80px;
height: 80px;
background-size: cover;
background-position: center;
transform: scale(1);
/*animation: pulse 2s infinite;*/
}
#loginImg {
height: 80px;
margin-inline: auto;
margin-bottom: 30px
}
@keyframes pulse {
0% {
transform: scale(0.95);
}
70% {
transform: scale(1);
}
100% {
transform: scale(0.95);
}
}
@media (max-width: 992px) {
.pagina {
padding: 15px;
}
.tablaRegPers {
font-size: 10px;
overflow-x: auto;
}
#cajaLogIn {
padding: 25px;
}
.loginImg {
height: 65px;
}
}
@media (max-width: 767px) {
.navMov {
display: flex;
position:fixed;
top:0;
left:0;
width:100%;
background-color: white;
}
.page {
margin-top:60px !important;
}
.menuMovil {
width: 100%;
position: fixed;
flex-direction: column;
margin-top: 11px;
z-index: 100;
}
.btnNavMov {
border-radius: 0;
background-color: #172073;
padding: 15px;
padding-block: 5px;
color: white;
border: none;
text-decoration:none;
text-align:center;
}
.btnGrisNav {
border-radius: 0;
background-color: #eeeeee;
padding: 15px;
border-top: solid 1px darkgray;
border-bottom: solid 1px darkgray;
padding-block: 7px;
color: black;
border: none;
text-decoration: none;
text-align: center;
margin-block: 1px;
}
.nav-tabs .nav-link {
font-size: 12px;
padding: 4px;
}
}
/*header .pa-header-t1 .pa-header-row1 .navbar.navbar-dark .navbar-nav:after {
content: "|";
color: rgba(255, 255, 255, 0.75);
}*/

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-facebook" viewBox="0 0 16 16">
<path d="M16 8.049c0-4.446-3.582-8.05-8-8.05C3.58 0-.002 3.603-.002 8.05c0 4.017 2.926 7.347 6.75 7.951v-5.625h-2.03V8.05H6.75V6.275c0-2.017 1.195-3.131 3.022-3.131.876 0 1.791.157 1.791.157v1.98h-1.009c-.993 0-1.303.621-1.303 1.258v1.51h2.218l-.354 2.326H9.25V16c3.824-.604 6.75-3.934 6.75-7.951z"/>
</svg>

After

Width:  |  Height:  |  Size: 436 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-instagram" viewBox="0 0 16 16">
<path d="M8 0C5.829 0 5.556.01 4.703.048 3.85.088 3.269.222 2.76.42a3.917 3.917 0 0 0-1.417.923A3.927 3.927 0 0 0 .42 2.76C.222 3.268.087 3.85.048 4.7.01 5.555 0 5.827 0 8.001c0 2.172.01 2.444.048 3.297.04.852.174 1.433.372 1.942.205.526.478.972.923 1.417.444.445.89.719 1.416.923.51.198 1.09.333 1.942.372C5.555 15.99 5.827 16 8 16s2.444-.01 3.298-.048c.851-.04 1.434-.174 1.943-.372a3.916 3.916 0 0 0 1.416-.923c.445-.445.718-.891.923-1.417.197-.509.332-1.09.372-1.942C15.99 10.445 16 10.173 16 8s-.01-2.445-.048-3.299c-.04-.851-.175-1.433-.372-1.941a3.926 3.926 0 0 0-.923-1.417A3.911 3.911 0 0 0 13.24.42c-.51-.198-1.092-.333-1.943-.372C10.443.01 10.172 0 7.998 0h.003zm-.717 1.442h.718c2.136 0 2.389.007 3.232.046.78.035 1.204.166 1.486.275.373.145.64.319.92.599.28.28.453.546.598.92.11.281.24.705.275 1.485.039.843.047 1.096.047 3.231s-.008 2.389-.047 3.232c-.035.78-.166 1.203-.275 1.485a2.47 2.47 0 0 1-.599.919c-.28.28-.546.453-.92.598-.28.11-.704.24-1.485.276-.843.038-1.096.047-3.232.047s-2.39-.009-3.233-.047c-.78-.036-1.203-.166-1.485-.276a2.478 2.478 0 0 1-.92-.598 2.48 2.48 0 0 1-.6-.92c-.109-.281-.24-.705-.275-1.485-.038-.843-.046-1.096-.046-3.233 0-2.136.008-2.388.046-3.231.036-.78.166-1.204.276-1.486.145-.373.319-.64.599-.92.28-.28.546-.453.92-.598.282-.11.705-.24 1.485-.276.738-.034 1.024-.044 2.515-.045v.002zm4.988 1.328a.96.96 0 1 0 0 1.92.96.96 0 0 0 0-1.92zm-4.27 1.122a4.109 4.109 0 1 0 0 8.217 4.109 4.109 0 0 0 0-8.217zm0 1.441a2.667 2.667 0 1 1 0 5.334 2.667 2.667 0 0 1 0-5.334z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-linkedin" viewBox="0 0 16 16">
<path d="M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854V1.146zm4.943 12.248V6.169H2.542v7.225h2.401zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248-.822 0-1.359.54-1.359 1.248 0 .694.521 1.248 1.327 1.248h.016zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016a5.54 5.54 0 0 1 .016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225h2.4z"/>
</svg>

After

Width:  |  Height:  |  Size: 711 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-list" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-fill" viewBox="0 0 16 16">
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3Zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"/>
</svg>

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-rss" viewBox="0 0 16 16">
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
<path d="M5.5 12a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm-3-8.5a1 1 0 0 1 1-1c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1-1-1zm0 4a1 1 0 0 1 1-1 6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1-1-1z"/>
</svg>

After

Width:  |  Height:  |  Size: 507 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-twitter" viewBox="0 0 16 16">
<path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"/>
</svg>

After

Width:  |  Height:  |  Size: 640 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-universal-access-circle" viewBox="0 0 16 16">
<path d="M8 4.143A1.071 1.071 0 1 0 8 2a1.071 1.071 0 0 0 0 2.143Zm-4.668 1.47 3.24.316v2.5l-.323 4.585A.383.383 0 0 0 7 13.14l.826-4.017c.045-.18.301-.18.346 0L9 13.139a.383.383 0 0 0 .752-.125L9.43 8.43v-2.5l3.239-.316a.38.38 0 0 0-.047-.756H3.379a.38.38 0 0 0-.047.756Z"/>
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0ZM1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Z"/>
</svg>

After

Width:  |  Height:  |  Size: 509 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-youtube" viewBox="0 0 16 16">
<path d="M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.007 2.007 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.007 2.007 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31.4 31.4 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.007 2.007 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A99.788 99.788 0 0 1 7.858 2h.193zM6.4 5.209v4.818l4.157-2.408L6.4 5.209z"/>
</svg>

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="201" height="176" viewBox="0 0 201 176">
<g id="Grupo_579" data-name="Grupo 579" transform="translate(-347.5 -588)">
<path id="Trazado_526" data-name="Trazado 526" d="M181.25-162.5H18.75A18.755,18.755,0,0,0,0-143.75V-6.25A18.755,18.755,0,0,0,18.75,12.5h162.5A18.755,18.755,0,0,0,200-6.25v-137.5A18.755,18.755,0,0,0,181.25-162.5ZM12.5-143.75A6.268,6.268,0,0,1,18.75-150H37.5v25h-25Zm175,137.5A6.268,6.268,0,0,1,181.25,0H18.75A6.268,6.268,0,0,1,12.5-6.25V-112.5h175Zm0-118.75H50v-25H181.25a6.268,6.268,0,0,1,6.25,6.25Z" transform="translate(348 751)" fill="#172073" stroke="rgba(0,0,0,0)" stroke-width="1"/>
<path id="Trazado_525" data-name="Trazado 525" d="M12.239,10.473V19.35l-1.611.615L10.53,21H21.312l.293-3.535-1.211-.146L19.2,19.525H15.013V15.512h2.119l.322,1.65,1.357-.225.332-4.141-1.406.264-.43,1.26H15.013v-4.18h3.975l.605,2.129,1.26.186.3-3.6H10.569l-.146,1.074Zm16.494,9.365-1.611-.576V16.137h1.211l1.211,2.246a6.46,6.46,0,0,0,1.67,2.109,3.668,3.668,0,0,0,2.334.742A5.345,5.345,0,0,0,35.2,21l.225-.977-.352-.1a3.412,3.412,0,0,1-1.118-.508,4.82,4.82,0,0,1-.874-.8q-.391-.454-1-1.274l-1.475-2.012a4.463,4.463,0,0,0,1.362-.767,4.051,4.051,0,0,0,1.006-1.2,2.9,2.9,0,0,0,.376-1.416A2.585,2.585,0,0,0,32,9.491a8.46,8.46,0,0,0-3.965-.737q-1.025,0-2.6.146t-2.734.332L22.6,10.395l1.768.371V19.35l-1.738.615L22.483,21h6.143Zm-1.611-9.766q.039,0,.249-.015t.493-.015a2.721,2.721,0,0,1,1.343.317,2.181,2.181,0,0,1,.874.869,2.525,2.525,0,0,1,.3,1.235,2.146,2.146,0,0,1-.425,1.357,2.794,2.794,0,0,1-1.128.85H27.122Zm15.469,9.766-1.611-.576V16.137H42.19L43.4,18.383a6.46,6.46,0,0,0,1.67,2.109,3.668,3.668,0,0,0,2.334.742A5.345,5.345,0,0,0,49.056,21l.225-.977-.352-.1a3.412,3.412,0,0,1-1.118-.508,4.82,4.82,0,0,1-.874-.8q-.391-.454-1-1.274l-1.475-2.012a4.463,4.463,0,0,0,1.362-.767,4.051,4.051,0,0,0,1.006-1.2,2.9,2.9,0,0,0,.376-1.416,2.585,2.585,0,0,0-1.348-2.466A8.46,8.46,0,0,0,41.9,8.754q-1.025,0-2.6.146t-2.734.332l-.107,1.162,1.768.371V19.35l-1.738.615L36.341,21h6.143Zm-1.611-9.766q.039,0,.249-.015t.493-.015a2.721,2.721,0,0,1,1.343.317,2.181,2.181,0,0,1,.874.869,2.525,2.525,0,0,1,.3,1.235,2.146,2.146,0,0,1-.425,1.357,2.794,2.794,0,0,1-1.128.85H40.979Zm15.2,11.191a7.692,7.692,0,0,0,3.574-.791,5.688,5.688,0,0,0,2.363-2.212,6.375,6.375,0,0,0,.83-3.276,7.549,7.549,0,0,0-.728-3.447,5.016,5.016,0,0,0-2.1-2.187A6.678,6.678,0,0,0,56.858,8.6a7.535,7.535,0,0,0-3.579.8A5.418,5.418,0,0,0,51,11.63a6.865,6.865,0,0,0-.781,3.325,7.619,7.619,0,0,0,.669,3.271A5.043,5.043,0,0,0,52.9,20.458,6.26,6.26,0,0,0,56.175,21.264ZM56.39,9.916a2.5,2.5,0,0,1,1.982.879,5.208,5.208,0,0,1,1.089,2.134,9.785,9.785,0,0,1,.327,2.476q0,4.414-3.115,4.414a2.5,2.5,0,0,1-1.958-.85,5,5,0,0,1-1.084-2.09,9.617,9.617,0,0,1-.327-2.461,6.54,6.54,0,0,1,.7-3.306A2.539,2.539,0,0,1,56.39,9.916Zm13.7,9.922-1.611-.576V16.137H69.69L70.9,18.383a6.46,6.46,0,0,0,1.67,2.109,3.668,3.668,0,0,0,2.334.742A5.345,5.345,0,0,0,76.556,21l.225-.977-.352-.1a3.412,3.412,0,0,1-1.118-.508,4.82,4.82,0,0,1-.874-.8q-.391-.454-1-1.274l-1.475-2.012a4.463,4.463,0,0,0,1.362-.767,4.051,4.051,0,0,0,1.006-1.2,2.9,2.9,0,0,0,.376-1.416,2.585,2.585,0,0,0-1.348-2.466A8.46,8.46,0,0,0,69.4,8.754q-1.025,0-2.6.146t-2.734.332l-.107,1.162,1.768.371V19.35l-1.738.615L63.841,21h6.143Zm-1.611-9.766q.039,0,.249-.015t.493-.015a2.721,2.721,0,0,1,1.343.317,2.181,2.181,0,0,1,.874.869,2.525,2.525,0,0,1,.3,1.235,2.146,2.146,0,0,1-.425,1.357,2.794,2.794,0,0,1-1.128.85H68.479ZM87.8,17.738v1.8l-2.363.586-.049.889h6.9l.1-1.006-2.109-.439v-1.8H92.61l.156-1.611h-2.49V14.34q0-1.289.029-2.686t.078-2.3l-.928-.244-6.777,7.52.234,1.113Zm-.02-5.176v3.594H85.12Zm10.322,8.7a4.07,4.07,0,0,0,2.095-.576,4.223,4.223,0,0,0,1.616-1.914,8.152,8.152,0,0,0,.635-3.477,7.393,7.393,0,0,0-1.157-4.619,3.738,3.738,0,0,0-3.071-1.445,4.065,4.065,0,0,0-2.163.62,4.455,4.455,0,0,0-1.65,1.982,8.158,8.158,0,0,0-.64,3.462q0,3.3,1.216,4.634A4.033,4.033,0,0,0,98.1,21.264ZM98.06,10.629a.97.97,0,0,1,.859.459,3.536,3.536,0,0,1,.43,1.45,22.388,22.388,0,0,1,.127,2.7,24.843,24.843,0,0,1-.117,2.71,4.145,4.145,0,0,1-.4,1.519.867.867,0,0,1-.781.5,1,1,0,0,1-.9-.488,3.6,3.6,0,0,1-.43-1.484,24.607,24.607,0,0,1-.117-2.725,24.491,24.491,0,0,1,.117-2.715,3.69,3.69,0,0,1,.405-1.46A.9.9,0,0,1,98.06,10.629ZM108.6,17.738v1.8l-2.363.586-.049.889h6.9l.1-1.006-2.109-.439v-1.8h2.334l.156-1.611h-2.49V14.34q0-1.289.029-2.686t.078-2.3l-.928-.244-6.777,7.52.234,1.113Zm-.02-5.176v3.594h-2.656Z" transform="translate(386 682)" fill="#172073" stroke="rgba(0,0,0,0)" stroke-width="1"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-list" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View File

@@ -0,0 +1 @@
<svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 335.08 121.82"><defs><style>.cls-1{fill:#343334;}.cls-2{fill:#fff;}</style></defs><title>Mesa de trabajo 1</title><path class="cls-1" d="M29.72,115c-3.7,0-6.22-2.81-6.22-6.75s2.54-6.77,6.22-6.77,6.22,2.85,6.22,6.77S33.4,115,29.72,115Zm0-12.23c-2.9,0-4.85,2.23-4.85,5.47s1.95,5.47,4.85,5.47,4.87-2.23,4.87-5.47S32.6,102.77,29.72,102.77Z"/><path class="cls-1" d="M39.69,109v5.75H38.38V101.72h7.7V103H39.7v4.89h5.36V109H39.69Z"/><path class="cls-1" d="M49.6,114.78H48.29V101.72H49.6Z"/><path class="cls-1" d="M51.93,108.25c0-4.07,2.57-6.77,6.2-6.77a5.42,5.42,0,0,1,5.51,4.29H62.22a4.13,4.13,0,0,0-4.14-3c-2.86,0-4.78,2.19-4.78,5.47s2,5.45,4.78,5.45a4.36,4.36,0,0,0,4.27-2.85h1.41A5.6,5.6,0,0,1,58.07,115C54.47,115,51.93,112.31,51.93,108.25Z"/><path class="cls-1" d="M67.36,114.78H66.05V101.72h1.31Z"/><path class="cls-1" d="M70.49,114.78V101.72h1.33L79,112.63V101.72h1.3v13.06H79l-7.19-10.87v10.87Z"/><path class="cls-1" d="M82.16,114.78,87,101.72h1.61l4.83,13.06H92l-1.24-3.47h-6l-1.28,3.47Zm3-4.58h5.24L88,103.56c0-.15-.13-.4-.16-.53,0,.13-.11.38-.15.53Z"/><path class="cls-1" d="M98.88,114.78l4.82-13.5h2.88l4.8,13.5h-3.14l-.95-2.85h-4.36l-1,2.85Zm4.9-5.33h2.66l-1-2.88a8.54,8.54,0,0,1-.36-1.31,6.45,6.45,0,0,1-.35,1.31Z"/><path class="cls-1" d="M113,114.78v-13.5h2.92l5.69,8.83v-8.83h2.92v13.5h-2.92L115.87,106v8.81Z"/><path class="cls-1" d="M127.28,114.78v-13.5h5.29c3.8,0,6.53,2.79,6.53,6.79a6.34,6.34,0,0,1-6.53,6.71ZM130.23,104v8h2c2.3,0,3.7-1.64,3.7-4s-1.44-4-3.7-4h-2Z"/><path class="cls-1" d="M139.16,114.78l4.82-13.5h2.88l4.8,13.5h-3.14l-1-2.85h-4.36l-1,2.85Zm4.91-5.33h2.66l-1-2.88a8.54,8.54,0,0,1-.36-1.31,6.45,6.45,0,0,1-.35,1.31Z"/><path class="cls-1" d="M156.22,112.13h5.29v2.65h-8.28v-13.5h3Z"/><path class="cls-1" d="M163.27,101.28h3v8.34a2.47,2.47,0,0,0,2.68,2.57,2.58,2.58,0,0,0,2.72-2.57v-8.34h3v8.43c0,3.37-2.61,5.29-5.67,5.29s-5.64-2-5.64-5.29Z"/><path class="cls-1" d="M176.52,114.78V112l6.07-7.88-6.07,0v-2.86h9.63v2.64L180,112h6v2.83Z"/><path class="cls-1" d="M187.14,114.78l4.82-13.5h2.88l4.8,13.5H196.5l-.95-2.85h-4.36l-1,2.85Zm4.91-5.33h2.66l-1-2.88a8.54,8.54,0,0,1-.36-1.31,6.45,6.45,0,0,1-.35,1.31Z"/><path class="cls-1" d="M204.9,114.78l4.8-13.06h1.61l4.83,13.06h-1.37l-1.24-3.47h-6l-1.28,3.47Zm3-4.58h5.24l-2.43-6.64c0-.15-.13-.4-.16-.53,0,.13-.11.38-.15.53Z"/><path class="cls-1" d="M218,114.78V101.72h1.33l7.19,10.91V101.72h1.3v13.06h-1.3l-7.19-10.87v10.87Z"/><path class="cls-1" d="M229.61,101.72h9.52v1.22H235v11.84h-1.31V102.94h-4.12v-1.22Z"/><path class="cls-1" d="M242.26,114.78H241V101.72h1.31Z"/><path class="cls-1" d="M246.75,109v5.75h-1.31V101.72h7.7V103h-6.39v4.89h5.36V109h-5.36Z"/><path class="cls-1" d="M255.35,114.78V101.72h5c2.66,0,4.27,1.44,4.27,3.78a3.52,3.52,0,0,1-2.7,3.67l2.68,5.62h-1.48l-2.54-5.42h-3.92v5.42h-1.31Zm1.31-6.64h3.7c1.86,0,2.86-1,2.86-2.61s-1.08-2.61-2.88-2.61h-3.69v5.22Z"/><path class="cls-1" d="M265.7,114.78l4.8-13.06h1.61l4.83,13.06h-1.37l-1.24-3.47h-6L267,114.78Zm3-4.58h5.24l-2.43-6.64c0-.15-.13-.4-.16-.53,0,.13-.11.38-.15.53Z"/><path class="cls-1" d="M278,101.72h1.33v8.59c0,2.68,1.92,3.45,3.59,3.45a3.28,3.28,0,0,0,3.59-3.45v-8.59h1.31v8.63c0,3.25-2.39,4.69-4.91,4.69s-4.93-1.28-4.93-4.69v-8.63Z"/><path class="cls-1" d="M290.87,114.78V101.72h4a6.53,6.53,0,0,1,0,13.06ZM292.18,103v10.58h2.61a5.29,5.29,0,0,0,0-10.58Z"/><path class="cls-1" d="M303.75,114.78V101.72h7.81V103h-6.51v4.67H311v1.17h-5.91v4.73h6.53v1.24h-7.83Z"/><path class="cls-2" d="M98.45,37.53a40.49,40.49,0,1,0-29.54,49A40.48,40.48,0,0,0,98.45,37.53ZM45.7,62.94a15.06,15.06,0,1,1,11-18.25A15.08,15.08,0,0,1,45.7,62.94Z"/><polygon class="cls-1" points="100.23 87.78 119.32 87.78 127.19 64.56 150.42 64.56 158.17 87.78 177.6 87.78 163.37 47.72 114.52 47.7 100.23 87.78"/><polygon class="cls-1" points="129.12 6.78 118.22 37.34 158.34 33.57 148.82 6.78 129.12 6.78"/><polygon class="cls-1" points="180.68 87.78 199.77 87.78 207.64 64.56 230.87 64.56 238.63 87.78 258.05 87.78 243.83 47.77 194.96 47.74 180.68 87.78"/><polygon class="cls-1" points="209.57 6.78 201.46 29.53 236.19 26.27 229.27 6.78 209.57 6.78"/><polygon class="cls-1" points="262.47 87.78 281.05 87.78 281.05 64.56 310.58 64.56 310.58 47.81 262.47 47.81 262.47 87.78"/><polygon class="cls-1" points="262.47 6.78 262.47 23.8 316.42 18.74 316.42 6.78 262.47 6.78"/></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,34 @@
function pestanaActiva(pestana) {
var links = document.getElementsByClassName("navBarLink")
for (var i = 0; i < links.length; i++) {
if (links[i].classList.contains("linkNavActivo") == true)
links[i].classList.remove("linkNavActivo")
}
if (document.getElementById(pestana).classList.contains("linkNavActivo") == false) {
document.getElementById(pestana).classList.add("linkNavActivo")
}
}
function eliminarCookies(id, cut) {
var theCookies = document.cookie.split(';');
for (var i = 0; i < theCookies.length; i++) {
if (theCookies[i].trim().indexOf(id) >= 0 && theCookies[i].trim().indexOf(cut) == -1) {
ASPxClientUtils.DeleteCookie(theCookies[i].trim().split("=")[0])
}
}
}
function getUrlParameter(sParam) {
var sPageURL = window.location.search.substring(1),
sURLVariables = sPageURL.split('&'),
sParameterName,
i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
}
}
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,51 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #006bb7;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
padding-top: 1.1rem;
}
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB