771 lines
28 KiB
Plaintext
771 lines
28 KiB
Plaintext
@page "/GestionZip"
|
|
@rendermode @(new InteractiveServerRenderMode(prerender: false))
|
|
@attribute [Authorize]
|
|
@using System.Globalization
|
|
@using GestionaDenunciasAN.Models
|
|
@inject AuthenticationStateProvider AuthenticationStateProvider
|
|
@inject ApiDenunciasClient ApiDenuncias
|
|
|
|
<PageTitle>Entrada de denuncias</PageTitle>
|
|
|
|
<div class="container py-4">
|
|
<div class="d-flex flex-column flex-lg-row justify-content-between align-items-lg-center gap-3 mb-4">
|
|
<div>
|
|
<h3 class="mb-1">Entrada de denuncias</h3>
|
|
<p class="text-muted mb-0">
|
|
La app mantiene su propia sesion activa. Cuando caduque GlobalLeaks, aqui solo hara falta renovar el 2FA
|
|
para seguir trayendo denuncias.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
@if (!string.IsNullOrWhiteSpace(StatusMessage))
|
|
{
|
|
<div class="alert @StatusCss mb-4">@StatusMessage</div>
|
|
}
|
|
|
|
@if (!string.IsNullOrWhiteSpace(FilterWarningMessage))
|
|
{
|
|
<div class="alert @FilterWarningCss mb-4">@FilterWarningMessage</div>
|
|
}
|
|
|
|
<div class="row g-4">
|
|
<div class="col-lg-4">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Sesion GlobalLeaks</h5>
|
|
<p class="mb-2"><strong>Usuario:</strong> @CurrentUsername</p>
|
|
<p class="mb-2"><strong>Estado:</strong> @SessionStatusText</p>
|
|
<p class="mb-2">
|
|
<strong>Ultima descarga registrada:</strong>
|
|
@(UserInboxState.LastDownloadedReportMomentUtc is null
|
|
? "Sin descargas previas"
|
|
: UserInboxState.LastDownloadedReportMomentUtc.Value.ToLocalTime().ToString("dd/MM/yyyy HH:mm"))
|
|
</p>
|
|
<p class="text-muted small mb-4">
|
|
La base de datos lleva la cuenta de lo que ya ha descargado este usuario, de lo que han descargado otros
|
|
y de si el expediente ya esta creado en Gestiona.
|
|
</p>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Nuevo codigo 2FA</label>
|
|
<input class="form-control"
|
|
@bind="RenewAuthcode"
|
|
maxlength="6"
|
|
inputmode="numeric"
|
|
placeholder="123456" />
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-primary w-100" @onclick="RenewSessionAsync" disabled="@RenewBusy">
|
|
@(RenewBusy ? "Renovando..." : SessionRenewButtonText)
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-8">
|
|
<div class="card shadow-sm">
|
|
<div class="card-body">
|
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3 mb-3">
|
|
<div>
|
|
<h5 class="card-title mb-1">Bandeja GlobalLeaks</h5>
|
|
<p class="text-muted mb-0">
|
|
Puedes revisar solo lo nuevo, las actualizaciones desde tu ultima descarga o un intervalo de fechas.
|
|
</p>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary" @onclick="LoadReportsAsync" disabled="@ReportsBusy || !CanUseGlobalLeaks">
|
|
@(ReportsBusy ? "Actualizando..." : "Actualizar denuncias")
|
|
</button>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label">Tipo</label>
|
|
<select class="form-select" value="@Filter" @onchange="OnFilterChanged">
|
|
<option value="all">Todas</option>
|
|
<option value="new">Nuevas / sin leer</option>
|
|
<option value="updated">Actualizaciones</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<label class="form-label">Periodo</label>
|
|
<select class="form-select" value="@DateScope" @onchange="OnDateScopeChanged">
|
|
<option value="all">Todo el buzon</option>
|
|
<option value="since_mine">Desde mi ultima descarga</option>
|
|
<option value="range">Intervalo de fechas</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<label class="form-label">Canal</label>
|
|
<select class="form-select" value="@SelectedChannel" @onchange="OnChannelChanged">
|
|
<option value="">Todos</option>
|
|
@foreach (var context in Contexts)
|
|
{
|
|
<option value="@context.Id">@context.Name</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-md-3">
|
|
<label class="form-label">Desde</label>
|
|
<input class="form-control"
|
|
type="date"
|
|
value="@DateFrom"
|
|
disabled="@(DateScope != "range")"
|
|
@onchange="OnDateFromChanged" />
|
|
</div>
|
|
|
|
<div class="col-md-3">
|
|
<label class="form-label">Hasta</label>
|
|
<input class="form-control"
|
|
type="date"
|
|
value="@DateTo"
|
|
disabled="@(DateScope != "range")"
|
|
@onchange="OnDateToChanged" />
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<label class="form-label">Buscar</label>
|
|
<input class="form-control"
|
|
value="@SearchTerm"
|
|
@oninput="OnSearchChanged"
|
|
placeholder="# denuncia o canal" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3 mb-3">
|
|
<div class="text-muted">
|
|
@VisibleReports.Count denuncia(s) visibles, @SelectedReportsCount seleccionada(s).
|
|
@if (UserInboxState.LastDownloadedReportMomentUtc is not null)
|
|
{
|
|
<span class="d-block small">
|
|
Referencia de ultima descarga: @UserInboxState.LastDownloadedReportMomentUtc.Value.ToLocalTime().ToString("dd/MM/yyyy HH:mm")
|
|
</span>
|
|
}
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" @onclick="ToggleSelectAll" disabled="@(!VisibleReports.Any())">
|
|
@SelectAllLabel
|
|
</button>
|
|
<button type="button" class="btn btn-success btn-sm" @onclick="ImportSelectedAsync" disabled="@(!CanImportSelected)">
|
|
@(ImportBusy ? "Importando..." : "Importar seleccionadas")
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 3rem;"></th>
|
|
<th>#</th>
|
|
<th>Canal</th>
|
|
<th>Presentacion</th>
|
|
<th>Ultima actualizacion</th>
|
|
<th>Estado</th>
|
|
<th>Seguimiento</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@if (!CanUseGlobalLeaks)
|
|
{
|
|
<tr>
|
|
<td colspan="7" class="text-muted">
|
|
Renueva la sesion de GlobalLeaks con un 2FA valido para cargar la bandeja.
|
|
</td>
|
|
</tr>
|
|
}
|
|
else if (ReportsBusy)
|
|
{
|
|
<tr>
|
|
<td colspan="7" class="text-muted">Cargando denuncias...</td>
|
|
</tr>
|
|
}
|
|
else if (!VisibleReports.Any())
|
|
{
|
|
<tr>
|
|
<td colspan="7" class="text-muted">No hay denuncias con los filtros actuales.</td>
|
|
</tr>
|
|
}
|
|
else
|
|
{
|
|
@foreach (var report in VisibleReports)
|
|
{
|
|
<tr @key="report.Id" class="@GetReportRowCss(report)">
|
|
<td>
|
|
<input type="checkbox"
|
|
checked="@SelectedIds.Contains(report.Id)"
|
|
@onchange="@((ChangeEventArgs args) => ToggleSelection(report.Id, args))" />
|
|
</td>
|
|
<td><strong>#@(report.Progressive ?? 0)</strong></td>
|
|
<td>@(report.ContextName ?? report.ContextId ?? "-")</td>
|
|
<td>@FormatDate(report.CreationDate)</td>
|
|
<td>@FormatDate(report.UpdateDate)</td>
|
|
<td>
|
|
<span class="badge @GetStatusBadgeCss(report)">
|
|
@GetStatusLabel(report)
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<span class="badge @GetTrackingBadgeCss(report)">
|
|
@GetTrackingLabel(report)
|
|
</span>
|
|
@if (!string.IsNullOrWhiteSpace(report.TrackingNote))
|
|
{
|
|
<div class="small text-muted mt-1">@report.TrackingNote</div>
|
|
}
|
|
</td>
|
|
</tr>
|
|
}
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@code {
|
|
private readonly List<ContextDto> Contexts = [];
|
|
private readonly List<ReportDto> Reports = [];
|
|
private List<ReportDto> VisibleReports = [];
|
|
private HashSet<string> SelectedIds = [];
|
|
|
|
private ApiGlobalLeaksSessionDto? SessionInfo;
|
|
private InboxUserState UserInboxState = new();
|
|
private string CurrentUsername { get; set; } = string.Empty;
|
|
private string RenewAuthcode { get; set; } = string.Empty;
|
|
private string Filter { get; set; } = "all";
|
|
private string DateScope { get; set; } = "all";
|
|
private string DateFrom { get; set; } = string.Empty;
|
|
private string DateTo { get; set; } = string.Empty;
|
|
private string SelectedChannel { get; set; } = string.Empty;
|
|
private string SearchTerm { get; set; } = string.Empty;
|
|
private string StatusMessage { get; set; } = string.Empty;
|
|
private string StatusCss { get; set; } = "alert-info";
|
|
private string FilterWarningMessage { get; set; } = string.Empty;
|
|
private string FilterWarningCss { get; set; } = "alert-warning";
|
|
private bool ReportsBusy { get; set; }
|
|
private bool RenewBusy { get; set; }
|
|
private bool ImportBusy { get; set; }
|
|
|
|
private bool CanUseGlobalLeaks => SessionInfo?.HasActiveSession == true;
|
|
private int SelectedReportsCount => SelectedIds.Count;
|
|
private bool CanImportSelected => CanUseGlobalLeaks && SelectedReportsCount > 0 && !ImportBusy;
|
|
private string SessionStatusText => SessionInfo is null
|
|
? "Sin credenciales guardadas"
|
|
: SessionInfo.HasActiveSession
|
|
? "Activa"
|
|
: "Pendiente de renovacion 2FA";
|
|
private string SessionRenewButtonText => SessionInfo?.HasActiveSession == true
|
|
? "Renovar sesion"
|
|
: "Activar sesion";
|
|
private string SelectAllLabel => VisibleReports.Count > 0 && VisibleReports.All(report => SelectedIds.Contains(report.Id))
|
|
? "Deseleccionar todas"
|
|
: "Seleccionar todas";
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
|
CurrentUsername = authState.User.Identity?.Name ?? string.Empty;
|
|
|
|
await LoadSessionStateAsync();
|
|
|
|
if (CanUseGlobalLeaks)
|
|
{
|
|
await LoadReportsAsync();
|
|
}
|
|
else if (SessionInfo is not null)
|
|
{
|
|
SetStatus(
|
|
"La aplicacion sigue iniciada, pero la sesion de GlobalLeaks no esta activa. Introduce un nuevo 2FA para renovarla.",
|
|
"alert-warning");
|
|
}
|
|
}
|
|
|
|
private async Task LoadSessionStateAsync()
|
|
{
|
|
SessionInfo = string.IsNullOrWhiteSpace(CurrentUsername)
|
|
? null
|
|
: await ApiDenuncias.GetGlobalLeaksSessionAsync();
|
|
}
|
|
|
|
private async Task RenewSessionAsync()
|
|
{
|
|
if (SessionInfo is null)
|
|
{
|
|
SetStatus("No hay credenciales guardadas para este usuario. Cierra sesion y vuelve a entrar.", "alert-danger");
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(RenewAuthcode) || RenewAuthcode.Trim().Length != 6)
|
|
{
|
|
SetStatus("Introduce un codigo 2FA valido de 6 digitos.", "alert-warning");
|
|
return;
|
|
}
|
|
|
|
RenewBusy = true;
|
|
try
|
|
{
|
|
SessionInfo = await ApiDenuncias.RenewGlobalLeaksSessionAsync(RenewAuthcode.Trim(), CancellationToken.None);
|
|
RenewAuthcode = string.Empty;
|
|
await LoadReportsAsync();
|
|
SetStatus("Sesion de GlobalLeaks renovada correctamente.", "alert-success");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
SetStatus(ex.Message, "alert-danger");
|
|
}
|
|
finally
|
|
{
|
|
RenewBusy = false;
|
|
}
|
|
}
|
|
|
|
private async Task LoadReportsAsync()
|
|
{
|
|
if (!CanUseGlobalLeaks)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ReportsBusy = true;
|
|
try
|
|
{
|
|
var inbox = await ApiDenuncias.LoadInboxAsync(CancellationToken.None);
|
|
|
|
Contexts.Clear();
|
|
Contexts.AddRange(inbox.Contexts);
|
|
|
|
Reports.Clear();
|
|
Reports.AddRange(inbox.Reports);
|
|
UserInboxState = inbox.UserState;
|
|
ApplyFilters();
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
await LoadSessionStateAsync();
|
|
Reports.Clear();
|
|
VisibleReports.Clear();
|
|
SelectedIds.Clear();
|
|
SetStatus(ex.Message, "alert-warning");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
SetStatus(ex.Message, "alert-danger");
|
|
}
|
|
finally
|
|
{
|
|
ReportsBusy = false;
|
|
}
|
|
}
|
|
|
|
private async Task ImportSelectedAsync()
|
|
{
|
|
if (!CanUseGlobalLeaks)
|
|
{
|
|
SetStatus("Renueva antes la sesion de GlobalLeaks para importar denuncias.", "alert-warning");
|
|
return;
|
|
}
|
|
|
|
ImportBusy = true;
|
|
var importedCount = 0;
|
|
var errors = new List<string>();
|
|
|
|
try
|
|
{
|
|
var selectedReports = Reports
|
|
.Where(report => SelectedIds.Contains(report.Id))
|
|
.OrderBy(report => report.Progressive ?? 0)
|
|
.ToList();
|
|
|
|
foreach (var report in selectedReports)
|
|
{
|
|
try
|
|
{
|
|
var result = await ApiDenuncias.ImportReportAsync(report, CancellationToken.None);
|
|
importedCount += result.ImportedCount;
|
|
errors.AddRange(result.Errors.Select(error => $"#{report.Progressive ?? 0}: {error}"));
|
|
}
|
|
catch (UnauthorizedAccessException ex)
|
|
{
|
|
await LoadSessionStateAsync();
|
|
SetStatus(ex.Message, "alert-warning");
|
|
break;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errors.Add($"#{report.Progressive ?? 0}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
SelectedIds.Clear();
|
|
await LoadReportsAsync();
|
|
|
|
var warnings = selectedReports
|
|
.Where(report => report.DownloadedByAnotherUser || report.AlreadyInGestiona)
|
|
.Select(report => $"#{report.Progressive ?? 0}: {report.TrackingNote}")
|
|
.Where(message => !string.IsNullOrWhiteSpace(message))
|
|
.ToList();
|
|
|
|
if (errors.Count == 0 && warnings.Count == 0)
|
|
{
|
|
SetStatus($"Se han importado {importedCount} denuncia(s) desde GlobalLeaks.", "alert-success");
|
|
}
|
|
else
|
|
{
|
|
var parts = new List<string>
|
|
{
|
|
$"Se han importado {importedCount} denuncia(s)."
|
|
};
|
|
|
|
if (warnings.Count > 0)
|
|
{
|
|
parts.Add($"Avisos: {string.Join(" | ", warnings)}");
|
|
}
|
|
|
|
if (errors.Count > 0)
|
|
{
|
|
parts.Add($"Incidencias: {string.Join(" | ", errors)}");
|
|
}
|
|
|
|
SetStatus(string.Join(" ", parts), errors.Count == 0 ? "alert-warning" : "alert-danger");
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ImportBusy = false;
|
|
}
|
|
}
|
|
|
|
private void ApplyFilters()
|
|
{
|
|
IEnumerable<ReportDto> filtered = Reports;
|
|
FilterWarningMessage = string.Empty;
|
|
FilterWarningCss = "alert-warning";
|
|
|
|
filtered = Filter switch
|
|
{
|
|
"new" => filtered.Where(report => string.IsNullOrWhiteSpace(report.AccessDate) || string.Equals(report.Status, "new", StringComparison.OrdinalIgnoreCase)),
|
|
"updated" => filtered.Where(report => report.Updated),
|
|
_ => filtered
|
|
};
|
|
|
|
filtered = ApplyDateScope(filtered);
|
|
|
|
if (!string.IsNullOrWhiteSpace(SelectedChannel))
|
|
{
|
|
filtered = filtered.Where(report => string.Equals(report.ContextId, SelectedChannel, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(SearchTerm))
|
|
{
|
|
var search = SearchTerm.Trim();
|
|
filtered = filtered.Where(report =>
|
|
(report.Progressive?.ToString() ?? string.Empty).Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
|
(report.ContextName ?? report.ContextId ?? string.Empty).Contains(search, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
VisibleReports = filtered
|
|
.OrderByDescending(report => report.Progressive ?? 0)
|
|
.ToList();
|
|
|
|
var validIds = Reports.Select(report => report.Id).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
SelectedIds.RemoveWhere(id => !validIds.Contains(id));
|
|
}
|
|
|
|
private IEnumerable<ReportDto> ApplyDateScope(IEnumerable<ReportDto> reports)
|
|
{
|
|
if (DateScope == "since_mine")
|
|
{
|
|
if (UserInboxState.LastDownloadedReportMomentUtc is null)
|
|
{
|
|
FilterWarningMessage = "Este usuario aun no tiene descargas previas. Se muestran todas las denuncias del filtro seleccionado.";
|
|
FilterWarningCss = "alert-info";
|
|
return reports;
|
|
}
|
|
|
|
var lastMoment = UserInboxState.LastDownloadedReportMomentUtc.Value;
|
|
return reports.Where(report => GetEffectiveMoment(report) is DateTimeOffset moment && moment > lastMoment);
|
|
}
|
|
|
|
if (DateScope == "range")
|
|
{
|
|
var hasFrom = DateOnly.TryParseExact(DateFrom, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var fromDate);
|
|
var hasTo = DateOnly.TryParseExact(DateTo, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var toDate);
|
|
|
|
if (hasFrom && hasTo && fromDate > toDate)
|
|
{
|
|
FilterWarningMessage = "La fecha inicial no puede ser posterior a la final.";
|
|
FilterWarningCss = "alert-danger";
|
|
return Enumerable.Empty<ReportDto>();
|
|
}
|
|
|
|
var filtered = reports.Where(report =>
|
|
{
|
|
var moment = GetEffectiveMoment(report);
|
|
if (moment is null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var reportDate = DateOnly.FromDateTime(moment.Value.UtcDateTime);
|
|
var matchesFrom = !hasFrom || reportDate >= fromDate;
|
|
var matchesTo = !hasTo || reportDate <= toDate;
|
|
return matchesFrom && matchesTo;
|
|
});
|
|
|
|
if (hasFrom &&
|
|
UserInboxState.LastDownloadedReportMomentUtc is DateTimeOffset lastDownloaded &&
|
|
fromDate > DateOnly.FromDateTime(lastDownloaded.UtcDateTime))
|
|
{
|
|
var gapExists = Reports.Any(report =>
|
|
{
|
|
var moment = GetEffectiveMoment(report);
|
|
if (moment is null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var reportDate = DateOnly.FromDateTime(moment.Value.UtcDateTime);
|
|
return moment.Value > lastDownloaded && reportDate < fromDate;
|
|
});
|
|
|
|
if (gapExists)
|
|
{
|
|
FilterWarningMessage =
|
|
$"Ojo: entre tu ultima descarga ({lastDownloaded.ToLocalTime():dd/MM/yyyy HH:mm}) y el inicio del intervalo ({fromDate:dd/MM/yyyy}) hay denuncias o actualizaciones sin revisar.";
|
|
}
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
return reports;
|
|
}
|
|
|
|
private void ToggleSelection(string reportId, ChangeEventArgs args)
|
|
{
|
|
var isChecked = GetCheckedValue(args);
|
|
if (isChecked)
|
|
{
|
|
SelectedIds.Add(reportId);
|
|
}
|
|
else
|
|
{
|
|
SelectedIds.Remove(reportId);
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
private void ToggleSelectAll()
|
|
{
|
|
var shouldSelect = !VisibleReports.All(report => SelectedIds.Contains(report.Id));
|
|
|
|
foreach (var report in VisibleReports)
|
|
{
|
|
if (shouldSelect)
|
|
{
|
|
SelectedIds.Add(report.Id);
|
|
}
|
|
else
|
|
{
|
|
SelectedIds.Remove(report.Id);
|
|
}
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
private void OnFilterChanged(ChangeEventArgs args)
|
|
{
|
|
Filter = args.Value?.ToString() ?? "all";
|
|
ApplyFilters();
|
|
}
|
|
|
|
private void OnDateScopeChanged(ChangeEventArgs args)
|
|
{
|
|
DateScope = args.Value?.ToString() ?? "all";
|
|
if (DateScope != "range")
|
|
{
|
|
DateFrom = string.Empty;
|
|
DateTo = string.Empty;
|
|
}
|
|
|
|
ApplyFilters();
|
|
}
|
|
|
|
private void OnDateFromChanged(ChangeEventArgs args)
|
|
{
|
|
DateFrom = args.Value?.ToString() ?? string.Empty;
|
|
ApplyFilters();
|
|
}
|
|
|
|
private void OnDateToChanged(ChangeEventArgs args)
|
|
{
|
|
DateTo = args.Value?.ToString() ?? string.Empty;
|
|
ApplyFilters();
|
|
}
|
|
|
|
private void OnChannelChanged(ChangeEventArgs args)
|
|
{
|
|
SelectedChannel = args.Value?.ToString() ?? string.Empty;
|
|
ApplyFilters();
|
|
}
|
|
|
|
private void OnSearchChanged(ChangeEventArgs args)
|
|
{
|
|
SearchTerm = args.Value?.ToString() ?? string.Empty;
|
|
ApplyFilters();
|
|
}
|
|
|
|
private void SetStatus(string message, string cssClass)
|
|
{
|
|
StatusMessage = message;
|
|
StatusCss = cssClass;
|
|
}
|
|
|
|
private static bool GetCheckedValue(ChangeEventArgs args)
|
|
{
|
|
return args.Value switch
|
|
{
|
|
bool b => b,
|
|
string s when bool.TryParse(s, out var parsed) => parsed,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
private static string FormatDate(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
return "-";
|
|
}
|
|
|
|
return DateTimeOffset.TryParse(value, out var parsed)
|
|
? parsed.ToLocalTime().ToString("dd/MM/yyyy HH:mm")
|
|
: "-";
|
|
}
|
|
|
|
private static DateTimeOffset? GetEffectiveMoment(ReportDto report)
|
|
{
|
|
return ParseDate(report.UpdateDate) ?? ParseDate(report.CreationDate);
|
|
}
|
|
|
|
private static DateTimeOffset? ParseDate(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return DateTimeOffset.TryParse(value.Replace("Z", "+00:00", StringComparison.Ordinal), out var parsed)
|
|
? parsed
|
|
: null;
|
|
}
|
|
|
|
private static string GetStatusLabel(ReportDto report)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(report.AccessDate))
|
|
{
|
|
return "Sin leer";
|
|
}
|
|
|
|
if (report.Updated)
|
|
{
|
|
return "Actualizada";
|
|
}
|
|
|
|
return string.Equals(report.Status, "closed", StringComparison.OrdinalIgnoreCase)
|
|
? "Cerrada"
|
|
: "Abierta";
|
|
}
|
|
|
|
private static string GetStatusBadgeCss(ReportDto report)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(report.AccessDate))
|
|
{
|
|
return "bg-warning text-dark";
|
|
}
|
|
|
|
if (report.Updated)
|
|
{
|
|
return "bg-info text-dark";
|
|
}
|
|
|
|
return string.Equals(report.Status, "closed", StringComparison.OrdinalIgnoreCase)
|
|
? "bg-secondary"
|
|
: "bg-primary";
|
|
}
|
|
|
|
private static string GetTrackingLabel(ReportDto report)
|
|
{
|
|
if (report.AlreadyInGestiona)
|
|
{
|
|
return "Expediente creado";
|
|
}
|
|
|
|
if (report.DownloadedByAnotherUser)
|
|
{
|
|
return "Descargada por otro";
|
|
}
|
|
|
|
if (report.DownloadedByCurrentUser)
|
|
{
|
|
return "Descargada por ti";
|
|
}
|
|
|
|
if (report.AlreadyImported)
|
|
{
|
|
return "Ya incorporada";
|
|
}
|
|
|
|
return "Pendiente";
|
|
}
|
|
|
|
private static string GetTrackingBadgeCss(ReportDto report)
|
|
{
|
|
if (report.AlreadyInGestiona)
|
|
{
|
|
return "bg-success";
|
|
}
|
|
|
|
if (report.DownloadedByAnotherUser)
|
|
{
|
|
return "bg-warning text-dark";
|
|
}
|
|
|
|
if (report.DownloadedByCurrentUser)
|
|
{
|
|
return "bg-secondary";
|
|
}
|
|
|
|
if (report.AlreadyImported)
|
|
{
|
|
return "bg-info text-dark";
|
|
}
|
|
|
|
return "bg-light text-dark";
|
|
}
|
|
|
|
private static string? GetReportRowCss(ReportDto report)
|
|
{
|
|
if (report.AlreadyInGestiona)
|
|
{
|
|
return "table-success";
|
|
}
|
|
|
|
if (report.DownloadedByAnotherUser)
|
|
{
|
|
return "table-warning";
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|