Update dashboard layout
This commit is contained in:
+114
-7
@@ -308,6 +308,16 @@
|
|||||||
color: var(--white);
|
color: var(--white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.qr-action-btn.download {
|
||||||
|
background: rgba(16, 185, 129, 0.1);
|
||||||
|
color: #059669;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-action-btn.download:hover {
|
||||||
|
background: #059669;
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
.qr-action-btn.delete {
|
.qr-action-btn.delete {
|
||||||
background: rgba(239, 68, 68, 0.1);
|
background: rgba(239, 68, 68, 0.1);
|
||||||
color: #dc2626;
|
color: #dc2626;
|
||||||
@@ -493,6 +503,19 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-3);
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: var(--spacing-4);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions .btn {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Animations */
|
||||||
@keyframes fadeIn {
|
@keyframes fadeIn {
|
||||||
from { opacity: 0; }
|
from { opacity: 0; }
|
||||||
@@ -693,14 +716,14 @@
|
|||||||
{% for qr in project_qr_codes %}
|
{% for qr in project_qr_codes %}
|
||||||
<div class="qr-card {{ 'inactive' if not qr.active_status }}" onclick="openQRModal({
|
<div class="qr-card {{ 'inactive' if not qr.active_status }}" onclick="openQRModal({
|
||||||
name: '{{ qr.name }}',
|
name: '{{ qr.name }}',
|
||||||
image: '{{ qr.qr_code_image }}',
|
image: 'data:image/png;base64,{{ qr.qr_code_image }}',
|
||||||
location: '{{ qr.location }}',
|
location: '{{ qr.location }}',
|
||||||
address: '{{ qr.location_address }}',
|
address: '{{ qr.location_address }}',
|
||||||
event: '{{ qr.location_event }}'
|
event: '{{ qr.location_event }}'
|
||||||
})">
|
})">
|
||||||
<div class="qr-card-header">
|
<div class="qr-card-header">
|
||||||
<div class="qr-preview">
|
<div class="qr-preview">
|
||||||
<img src="{{ qr.qr_code_image }}" alt="QR Code for {{ qr.name }}" loading="lazy">
|
<img src="data:image/png;base64,{{ qr.qr_code_image }}" alt="QR Code for {{ qr.name }}" loading="lazy">
|
||||||
</div>
|
</div>
|
||||||
<div class="qr-info">
|
<div class="qr-info">
|
||||||
<h4 class="qr-name">{{ qr.name }}</h4>
|
<h4 class="qr-name">{{ qr.name }}</h4>
|
||||||
@@ -717,6 +740,10 @@
|
|||||||
|
|
||||||
<!-- QR Actions (Show on Hover) -->
|
<!-- QR Actions (Show on Hover) -->
|
||||||
<div class="qr-actions">
|
<div class="qr-actions">
|
||||||
|
<button class="qr-action-btn download" onclick="event.stopPropagation(); downloadQR('{{ qr.qr_code_image }}', '{{ qr.name }}')" title="Download QR Code">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
<a href="{{ url_for('edit_qr_code', qr_id=qr.id) }}" class="qr-action-btn edit" onclick="event.stopPropagation()" title="Edit QR Code">
|
<a href="{{ url_for('edit_qr_code', qr_id=qr.id) }}" class="qr-action-btn edit" onclick="event.stopPropagation()" title="Edit QR Code">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -769,14 +796,14 @@
|
|||||||
{% for qr in unassigned_qr_codes %}
|
{% for qr in unassigned_qr_codes %}
|
||||||
<div class="qr-card {{ 'inactive' if not qr.active_status }}" onclick="openQRModal({
|
<div class="qr-card {{ 'inactive' if not qr.active_status }}" onclick="openQRModal({
|
||||||
name: '{{ qr.name }}',
|
name: '{{ qr.name }}',
|
||||||
image: '{{ qr.qr_code_image }}',
|
image: 'data:image/png;base64,{{ qr.qr_code_image }}',
|
||||||
location: '{{ qr.location }}',
|
location: '{{ qr.location }}',
|
||||||
address: '{{ qr.location_address }}',
|
address: '{{ qr.location_address }}',
|
||||||
event: '{{ qr.location_event }}'
|
event: '{{ qr.location_event }}'
|
||||||
})">
|
})">
|
||||||
<div class="qr-card-header">
|
<div class="qr-card-header">
|
||||||
<div class="qr-preview">
|
<div class="qr-preview">
|
||||||
<img src="{{ qr.qr_code_image }}" alt="QR Code for {{ qr.name }}" loading="lazy">
|
<img src="data:image/png;base64,{{ qr.qr_code_image }}" alt="QR Code for {{ qr.name }}" loading="lazy">
|
||||||
</div>
|
</div>
|
||||||
<div class="qr-info">
|
<div class="qr-info">
|
||||||
<h4 class="qr-name">{{ qr.name }}</h4>
|
<h4 class="qr-name">{{ qr.name }}</h4>
|
||||||
@@ -793,6 +820,10 @@
|
|||||||
|
|
||||||
<!-- QR Actions -->
|
<!-- QR Actions -->
|
||||||
<div class="qr-actions">
|
<div class="qr-actions">
|
||||||
|
<button class="qr-action-btn download" onclick="event.stopPropagation(); downloadQR('{{ qr.qr_code_image }}', '{{ qr.name }}')" title="Download QR Code">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
<a href="{{ url_for('edit_qr_code', qr_id=qr.id) }}" class="qr-action-btn edit" onclick="event.stopPropagation()" title="Edit QR Code">
|
<a href="{{ url_for('edit_qr_code', qr_id=qr.id) }}" class="qr-action-btn edit" onclick="event.stopPropagation()" title="Edit QR Code">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -847,7 +878,17 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<img id="modalQRImage" src="" alt="QR Code" style="max-width: 100%; height: auto;">
|
<img id="modalQRImage" src="" alt="QR Code" style="max-width: 100%; height: auto; margin-bottom: 1rem;">
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button class="btn btn-primary" onclick="downloadModalQR()">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
Download QR Code
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline" onclick="copyModalQRData()">
|
||||||
|
<i class="fas fa-copy"></i>
|
||||||
|
Copy Info
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -936,23 +977,29 @@ class ProjectDashboardManager {
|
|||||||
// QR Code Management Functions
|
// QR Code Management Functions
|
||||||
async toggleQRCodeStatus(qrId) {
|
async toggleQRCodeStatus(qrId) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/qr-codes/${qrId}/toggle`, {
|
const response = await fetch(`/qr-codes/${qrId}/toggle-status`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.success) {
|
||||||
// Show success message
|
// Show success message
|
||||||
if (window.showToast) {
|
if (window.showToast) {
|
||||||
window.showToast('QR code status updated successfully', 'success');
|
window.showToast(result.message, 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload page to reflect changes
|
// Reload page to reflect changes
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || 'Failed to toggle QR code status');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Failed to toggle QR code status');
|
throw new Error('Failed to toggle QR code status');
|
||||||
}
|
}
|
||||||
@@ -1062,6 +1109,15 @@ class ProjectDashboardManager {
|
|||||||
modalImage.src = qrData.image;
|
modalImage.src = qrData.image;
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
|
|
||||||
|
// Store current modal QR data for download and copy functions
|
||||||
|
this.currentModalQR = {
|
||||||
|
name: qrData.name,
|
||||||
|
image: qrData.image,
|
||||||
|
location: qrData.location,
|
||||||
|
address: qrData.address,
|
||||||
|
event: qrData.event
|
||||||
|
};
|
||||||
|
|
||||||
// Add keyboard listener for escape key
|
// Add keyboard listener for escape key
|
||||||
document.addEventListener('keydown', this.handleModalKeydown.bind(this));
|
document.addEventListener('keydown', this.handleModalKeydown.bind(this));
|
||||||
}
|
}
|
||||||
@@ -1071,6 +1127,7 @@ class ProjectDashboardManager {
|
|||||||
const modal = document.getElementById('qrModal');
|
const modal = document.getElementById('qrModal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.style.display = 'none';
|
modal.style.display = 'none';
|
||||||
|
this.currentModalQR = null;
|
||||||
|
|
||||||
// Remove keyboard listener
|
// Remove keyboard listener
|
||||||
document.removeEventListener('keydown', this.handleModalKeydown.bind(this));
|
document.removeEventListener('keydown', this.handleModalKeydown.bind(this));
|
||||||
@@ -1083,6 +1140,53 @@ class ProjectDashboardManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Download QR Code functionality
|
||||||
|
downloadQR(base64Image, filename) {
|
||||||
|
try {
|
||||||
|
// Extract base64 data if it includes the data URL prefix
|
||||||
|
const base64Data = base64Image.includes('base64,')
|
||||||
|
? base64Image.split('base64,')[1]
|
||||||
|
: base64Image;
|
||||||
|
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = `data:image/png;base64,${base64Data}`;
|
||||||
|
link.download = `${filename.replace(/[^a-z0-9]/gi, '_').toLowerCase()}_qr_code.png`;
|
||||||
|
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
// Show success toast
|
||||||
|
if (window.showToast) {
|
||||||
|
window.showToast('QR code downloaded successfully!', 'success');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Download error:', error);
|
||||||
|
if (window.showToast) {
|
||||||
|
window.showToast('Failed to download QR code', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download from modal
|
||||||
|
downloadModalQR() {
|
||||||
|
if (this.currentModalQR) {
|
||||||
|
this.downloadQR(this.currentModalQR.image, this.currentModalQR.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy QR data from modal
|
||||||
|
copyModalQRData() {
|
||||||
|
if (this.currentModalQR) {
|
||||||
|
this.copyQRData(
|
||||||
|
this.currentModalQR.name,
|
||||||
|
this.currentModalQR.location,
|
||||||
|
this.currentModalQR.address,
|
||||||
|
this.currentModalQR.event
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Utility Methods
|
// Utility Methods
|
||||||
copyQRData(name, location, address, event) {
|
copyQRData(name, location, address, event) {
|
||||||
const data = `QR Code: ${name}\nLocation: ${location}\nAddress: ${address}\nEvent: ${event}`;
|
const data = `QR Code: ${name}\nLocation: ${location}\nAddress: ${address}\nEvent: ${event}`;
|
||||||
@@ -1160,6 +1264,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
window.closeQRModal = () => dashboardManager.closeQRModal();
|
window.closeQRModal = () => dashboardManager.closeQRModal();
|
||||||
window.copyQRData = (name, location, address, event) =>
|
window.copyQRData = (name, location, address, event) =>
|
||||||
dashboardManager.copyQRData(name, location, address, event);
|
dashboardManager.copyQRData(name, location, address, event);
|
||||||
|
window.downloadModalQR = () => dashboardManager.downloadModalQR();
|
||||||
|
window.copyModalQRData = () => dashboardManager.copyModalQRData();
|
||||||
|
window.downloadQR = (base64Image, filename) => dashboardManager.downloadQR(base64Image, filename);
|
||||||
|
|
||||||
// Add keyboard shortcuts (optional)
|
// Add keyboard shortcuts (optional)
|
||||||
document.addEventListener('keydown', function(event) {
|
document.addEventListener('keydown', function(event) {
|
||||||
|
|||||||
Reference in New Issue
Block a user