Add print functionality to cashier dashboard for pending matches

- Added Print Matches button in header
- Added print modal with options to print all or last N matches
- Added professional print layout with fixture info, summary, and match table
- Print includes cashier username and timestamp
parent f50aad8a
......@@ -3,10 +3,59 @@
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="mb-4">
<i class="fas fa-cash-register me-2"></i>Cashier Dashboard
<small class="text-muted">Welcome, {{ current_user.username }}</small>
</h1>
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="mb-0">
<i class="fas fa-cash-register me-2"></i>Cashier Dashboard
</h1>
<small class="text-muted">Welcome, {{ current_user.username }}</small>
</div>
<div class="d-flex gap-2">
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#printModal">
<i class="fas fa-print me-1"></i>Print Matches
</button>
</div>
</div>
</div>
</div>
<!-- Print Modal -->
<div class="modal fade" id="printModal" tabindex="-1" aria-labelledby="printModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="printModalLabel"><i class="fas fa-print me-2"></i>Print Pending Matches</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Print Options</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="printOption" id="printAll" value="all" checked>
<label class="form-check-label" for="printAll">
Print all pending matches
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="printOption" id="printLastN" value="lastN">
<label class="form-check-label" for="printLastN">
Print last N matches
</label>
</div>
</div>
<div class="mb-3" id="lastNInput" style="display: none;">
<label for="numberOfMatches" class="form-label">Number of matches to print (from the end)</label>
<input type="number" class="form-control" id="numberOfMatches" min="1" value="10">
<div class="form-text">Enter the number of most recent matches to include in the printout.</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="printPendingMatches()">
<i class="fas fa-print me-1"></i>Print
</button>
</div>
</div>
</div>
</div>
......@@ -737,6 +786,242 @@ function loadAvailableTemplates() {
messageTemplateSelect.innerHTML = '<option value="text" selected>Text Message</option>';
});
}
// Print functionality
document.addEventListener('DOMContentLoaded', function() {
// Toggle last N input visibility
document.querySelectorAll('input[name="printOption"]').forEach(radio => {
radio.addEventListener('change', function() {
const lastNInput = document.getElementById('lastNInput');
if (this.value === 'lastN') {
lastNInput.style.display = 'block';
} else {
lastNInput.style.display = 'none';
}
});
});
});
function printPendingMatches() {
if (!cachedMatchesData) {
alert('No match data available. Please wait for matches to load.');
return;
}
const printOption = document.querySelector('input[name="printOption"]:checked').value;
const data = JSON.parse(cachedMatchesData);
let matchesToPrint = [...data.matches];
// Apply last N filter
if (printOption === 'lastN') {
const n = parseInt(document.getElementById('numberOfMatches').value) || 10;
matchesToPrint = matchesToPrint.slice(-n);
}
// Generate print content
const printContent = generateCashierPrintContent(matchesToPrint, data.fixture_id);
// Create print window
const printWindow = window.open('', '_blank');
printWindow.document.write(printContent);
printWindow.document.close();
printWindow.focus();
// Close modal
const modal = bootstrap.Modal.getInstance(document.getElementById('printModal'));
if (modal) {
modal.hide();
}
// Trigger print after a short delay
setTimeout(() => {
printWindow.print();
}, 500);
}
function generateCashierPrintContent(matches, fixtureId) {
const now = new Date().toLocaleString();
let matchesHTML = '';
matches.forEach(match => {
const startTime = match.start_time ? new Date(match.start_time).toLocaleString() : 'Not scheduled';
const status = match.status || 'pending';
const statusText = status.toUpperCase();
matchesHTML += `
<tr>
<td style="text-align: center; font-weight: bold;">#${match.match_number}</td>
<td style="font-weight: bold;">${match.fighter1_township}</td>
<td style="font-weight: bold;">${match.fighter2_township}</td>
<td>${match.venue_kampala_township}</td>
<td style="font-size: 11px;">${startTime}</td>
<td style="text-align: center;">${statusText}</td>
</tr>
`;
});
return `
<!DOCTYPE html>
<html>
<head>
<title>Pending Matches - Cashier Dashboard</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
font-size: 12px;
line-height: 1.4;
color: #333;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 20px;
border-bottom: 2px solid #333;
padding-bottom: 15px;
}
.header h1 {
font-size: 24px;
margin-bottom: 5px;
}
.header .subtitle {
font-size: 14px;
color: #666;
}
.info {
margin-bottom: 20px;
padding: 10px;
background: #f5f5f5;
border-radius: 5px;
}
.info table {
width: 100%;
}
.info td {
padding: 3px 10px;
}
.info td:first-child {
font-weight: bold;
width: 150px;
}
.summary {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
}
.summary-box {
text-align: center;
padding: 10px 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
.summary-box .number {
font-size: 24px;
font-weight: bold;
color: #333;
}
.summary-box .label {
font-size: 11px;
color: #666;
}
table.matches {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
table.matches th {
background: #333;
color: white;
padding: 8px 5px;
text-align: center;
font-size: 11px;
}
table.matches td {
border: 1px solid #ddd;
padding: 6px 5px;
vertical-align: middle;
}
.footer {
text-align: center;
font-size: 10px;
color: #666;
margin-top: 20px;
border-top: 1px solid #ddd;
padding-top: 10px;
}
@media print {
body {
padding: 0;
}
}
</style>
</head>
<body>
<div class="header">
<h1>Pending Matches Report</h1>
<div class="subtitle">Cashier Dashboard - Generated on ${now}</div>
</div>
<div class="info">
<table>
<tr>
<td>Fixture ID:</td>
<td>${fixtureId || 'N/A'}</td>
</tr>
<tr>
<td>Total Matches:</td>
<td>${matches.length}</td>
</tr>
<tr>
<td>Printed By:</td>
<td>{{ current_user.username }} (Cashier)</td>
</tr>
</table>
</div>
<div class="summary">
<div class="summary-box">
<div class="number">${matches.length}</div>
<div class="label">Total Matches</div>
</div>
<div class="summary-box">
<div class="number">${matches.filter(m => m.status === 'bet').length}</div>
<div class="label">Open for Betting</div>
</div>
<div class="summary-box">
<div class="number">${matches.filter(m => m.status === 'scheduled' || m.status === 'pending').length}</div>
<div class="label">Scheduled</div>
</div>
</div>
<table class="matches">
<thead>
<tr>
<th style="width: 50px;">#</th>
<th style="width: 120px;">Fighter 1</th>
<th style="width: 120px;">Fighter 2</th>
<th style="width: 100px;">Venue</th>
<th style="width: 100px;">Start Time</th>
<th style="width: 60px;">Status</th>
</tr>
</thead>
<tbody>
${matchesHTML}
</tbody>
</table>
<div class="footer">
<p>This report was generated by MbetterClient Cashier Dashboard</p>
<p>Fixture ID: ${fixtureId || 'N/A'} | Printed: ${now}</p>
</div>
</body>
</html>
`;
}
</script>
<script>
// Initialize dashboard with timer functionality
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment