289 lines
13 KiB
Plaintext
289 lines
13 KiB
Plaintext
@model List<QRRapidoApp.Models.QRCodeHistory>
|
|
@using Microsoft.Extensions.Localization
|
|
@inject IStringLocalizer<QRRapidoApp.Resources.SharedResource> Localizer
|
|
|
|
@{
|
|
ViewData["Title"] = Localizer["HistoryTitle"];
|
|
Layout = "~/Views/Shared/_Layout.cshtml";
|
|
var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
|
}
|
|
|
|
<div class="container mt-4">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h2><i class="fas fa-history text-primary"></i> @Localizer["QRCodeHistory"]</h2>
|
|
<p class="text-muted">@Localizer["QRCodesSavedHere"]</p>
|
|
</div>
|
|
<div>
|
|
<a href="/" class="btn btn-primary">
|
|
<i class="fas fa-plus"></i> @Localizer["GenerateNewQRCode"]
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
@if (Model != null && Model.Any())
|
|
{
|
|
<div class="row">
|
|
@foreach (var qr in Model)
|
|
{
|
|
<div class="col-12 col-md-6 col-lg-4 mb-4">
|
|
<div class="card h-100 shadow-sm position-relative">
|
|
<!-- Delete button in top-right corner -->
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline-danger position-absolute"
|
|
style="top: 8px; right: 8px; z-index: 10; padding: 4px 8px;"
|
|
onclick="confirmDeleteQR('@qr.Id')"
|
|
title="@Localizer["DeleteQRCode"]">
|
|
<i class="fas fa-trash fa-sm"></i>
|
|
</button>
|
|
<div class="card-body">
|
|
<div class="text-center mb-3">
|
|
<img src="data:image/png;base64,@qr.QRCodeBase64"
|
|
alt="QR Code"
|
|
class="img-fluid border"
|
|
style="max-width: 150px; max-height: 150px;">
|
|
</div>
|
|
|
|
<div class="mb-2">
|
|
<small class="text-muted">@Localizer["Type"]</small>
|
|
<span class="badge bg-secondary">@qr.Type</span>
|
|
</div>
|
|
|
|
<div class="mb-2">
|
|
<small class="text-muted">@Localizer["Content"]</small>
|
|
<p class="small mb-0" style="word-break: break-all;">
|
|
@if (qr.Content.Length > 50)
|
|
{
|
|
@(qr.Content.Substring(0, 50) + "...")
|
|
}
|
|
else
|
|
{
|
|
@qr.Content
|
|
}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<small class="text-muted">@Localizer["CreatedOn"]</small>
|
|
<br>
|
|
<small>@qr.CreatedAt.ToString("dd/MM/yyyy HH:mm")</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-footer bg-light">
|
|
<div class="btn-group w-100" role="group">
|
|
<a href="/api/QR/Download/@qr.Id?format=png"
|
|
class="btn btn-sm btn-outline-primary"
|
|
title="Download PNG">
|
|
<i class="fas fa-download"></i> PNG
|
|
</a>
|
|
<a href="/api/QR/Download/@qr.Id?format=svg"
|
|
class="btn btn-sm btn-outline-primary"
|
|
title="Download SVG">
|
|
<i class="fas fa-download"></i> SVG
|
|
</a>
|
|
<a href="/api/QR/Download/@qr.Id?format=pdf"
|
|
class="btn btn-sm btn-outline-primary"
|
|
title="Download PDF">
|
|
<i class="fas fa-download"></i> PDF
|
|
</a>
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline-secondary"
|
|
onclick="regenerateQR('@qr.Id')"
|
|
title="Regenerar">
|
|
<i class="fas fa-redo"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
@if (Model.Count == 50)
|
|
{
|
|
<div class="alert alert-info text-center">
|
|
<i class="fas fa-info-circle"></i>
|
|
@Localizer["ShowingRecent50QRCodes"]
|
|
</div>
|
|
}
|
|
}
|
|
else
|
|
{
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-qrcode fa-4x text-muted mb-3"></i>
|
|
<h4 class="text-muted">@Localizer["NoQRCodeFound"]</h4>
|
|
<p class="text-muted">
|
|
@Localizer["QRCodesWillAppearHere"]
|
|
</p>
|
|
<a href="/" class="btn btn-primary">
|
|
<i class="fas fa-plus"></i> @Localizer["GenerateFirstQRCode"]
|
|
</a>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal de Confirmação de Exclusão -->
|
|
<div class="modal fade" id="deleteConfirmModal" tabindex="-1" aria-labelledby="deleteConfirmModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="deleteConfirmModalLabel">@Localizer["ConfirmDeleteTitle"]</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>@Localizer["ConfirmDeleteMessage"]</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">@Localizer["No"]</button>
|
|
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">@Localizer["Yes"]</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@section Scripts {
|
|
<script>
|
|
let qrToDelete = null;
|
|
|
|
function regenerateQR(qrId) {
|
|
// Get QR data from history and regenerate
|
|
fetch(`/api/QR/History`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const qrData = data.find(q => q.id === qrId);
|
|
if (qrData) {
|
|
// Parse customization settings and redirect to home with parameters
|
|
const settings = JSON.parse(qrData.customizationSettings || '{}');
|
|
const params = new URLSearchParams({
|
|
content: qrData.content,
|
|
type: qrData.type,
|
|
size: settings.size || 300,
|
|
primaryColor: settings.primaryColor || '#000000',
|
|
backgroundColor: settings.backgroundColor || '#FFFFFF'
|
|
});
|
|
|
|
window.location.href = `/?${params.toString()}`;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error regenerating QR:', error);
|
|
showToast('@Localizer["ErrorRegeneratingQR"]', 'danger');
|
|
});
|
|
}
|
|
|
|
function confirmDeleteQR(qrId) {
|
|
qrToDelete = qrId;
|
|
const modal = new bootstrap.Modal(document.getElementById('deleteConfirmModal'));
|
|
modal.show();
|
|
}
|
|
|
|
function deleteQR(qrId) {
|
|
// Show loading state
|
|
const confirmBtn = document.getElementById('confirmDeleteBtn');
|
|
const originalText = confirmBtn.textContent;
|
|
confirmBtn.disabled = true;
|
|
confirmBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> @Localizer["Deleting"]...';
|
|
|
|
fetch(`/api/QR/History/${qrId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Show success message
|
|
showToast(data.message || '@Localizer["QRCodeDeleted"]', 'success');
|
|
|
|
// Remove the card from the UI
|
|
const card = document.querySelector(`[onclick="confirmDeleteQR('${qrId}')"]`).closest('.col-12');
|
|
if (card) {
|
|
card.style.transition = 'opacity 0.3s ease';
|
|
card.style.opacity = '0';
|
|
setTimeout(() => {
|
|
card.remove();
|
|
|
|
// Check if no more cards exist
|
|
const remainingCards = document.querySelectorAll('.card');
|
|
if (remainingCards.length === 0) {
|
|
location.reload(); // Reload to show "no QR codes" message
|
|
}
|
|
}, 300);
|
|
}
|
|
|
|
// Hide modal
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('deleteConfirmModal'));
|
|
modal.hide();
|
|
} else {
|
|
showToast(data.message || '@Localizer["ErrorDeletingQR"]', 'error');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error deleting QR:', error);
|
|
showToast('@Localizer["ErrorDeletingQR"]', 'error');
|
|
})
|
|
.finally(() => {
|
|
// Reset button state
|
|
confirmBtn.disabled = false;
|
|
confirmBtn.textContent = originalText;
|
|
});
|
|
}
|
|
|
|
function showToast(message, type = 'info') {
|
|
// Create toast container if doesn't exist
|
|
let toastContainer = document.getElementById('toast-container');
|
|
if (!toastContainer) {
|
|
toastContainer = document.createElement('div');
|
|
toastContainer.id = 'toast-container';
|
|
toastContainer.className = 'toast-container position-fixed top-0 start-0 p-3';
|
|
toastContainer.style.zIndex = '1060';
|
|
toastContainer.style.marginTop = '80px';
|
|
document.body.appendChild(toastContainer);
|
|
}
|
|
|
|
// Create toast element
|
|
const toastElement = document.createElement('div');
|
|
toastElement.innerHTML = `
|
|
<div class="toast align-items-center text-white bg-${type === 'success' ? 'success' : 'danger'} border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
|
<div class="d-flex">
|
|
<div class="toast-body">${message}</div>
|
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
toastContainer.appendChild(toastElement);
|
|
const toast = new bootstrap.Toast(toastElement.querySelector('.toast'), { delay: 5000 });
|
|
toast.show();
|
|
|
|
// Remove toast element after it's hidden
|
|
toastElement.querySelector('.toast').addEventListener('hidden.bs.toast', function() {
|
|
toastElement.remove();
|
|
});
|
|
}
|
|
|
|
// Event listeners
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Handle confirm delete button
|
|
document.getElementById('confirmDeleteBtn').addEventListener('click', function() {
|
|
if (qrToDelete) {
|
|
deleteQR(qrToDelete);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Auto-refresh the page periodically to show new QR codes
|
|
setInterval(() => {
|
|
// Only refresh if user is still on this page and there are QR codes
|
|
if (document.visibilityState === 'visible' && document.querySelector('.card')) {
|
|
location.reload();
|
|
}
|
|
}, 300000); // Refresh every 5 minutes
|
|
</script>
|
|
} |