2025-09-03 14:35:47 -06:00
|
|
|
{{ define "content" }}
|
|
|
|
|
<div class="flex-1 flex flex-col overflow-hidden">
|
|
|
|
|
<!-- Toolbar with Report Selection -->
|
|
|
|
|
<div class="bg-gray-50 border-b border-gray-200 px-6 py-3">
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
<div class="flex items-center gap-4 text-sm">
|
|
|
|
|
<form method="GET" action="/reports" class="flex items-center gap-3">
|
|
|
|
|
<!-- Category Dropdown -->
|
|
|
|
|
<div class="relative">
|
|
|
|
|
<label for="category" class="text-gray-700 font-medium mr-2">Category:</label>
|
|
|
|
|
<select
|
|
|
|
|
name="category"
|
|
|
|
|
id="category"
|
|
|
|
|
onchange="updateReports()"
|
|
|
|
|
class="px-3 py-2 text-sm border border-gray-200 bg-white focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-purple-500 min-w-48"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Category</option>
|
|
|
|
|
<option value="users" {{if eq .Category "users"}}selected{{end}}>Users & Teams</option>
|
2025-09-05 15:39:06 -06:00
|
|
|
<option value="address" {{if eq .Category "address"}}selected{{end}}>Addresses</option>
|
2025-09-03 14:35:47 -06:00
|
|
|
<option value="appointments" {{if eq .Category "appointments"}}selected{{end}}>Appointments</option>
|
|
|
|
|
<option value="polls" {{if eq .Category "polls"}}selected{{end}}>Polls</option>
|
|
|
|
|
<option value="availability" {{if eq .Category "availability"}}selected{{end}}>Availability</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Report Dropdown -->
|
|
|
|
|
<div class="relative">
|
|
|
|
|
<label for="report" class="text-gray-700 font-medium mr-2">Report:</label>
|
|
|
|
|
<select
|
|
|
|
|
name="report"
|
|
|
|
|
id="report"
|
|
|
|
|
class="px-3 py-2 text-sm border border-gray-200 bg-white focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-purple-500 min-w-64"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Select Report</option>
|
|
|
|
|
{{if .Category}}
|
|
|
|
|
{{range .AvailableReports}}
|
|
|
|
|
<option value="{{.ID}}" {{if eq .ID $.ReportID}}selected{{end}}>{{.Name}}</option>
|
|
|
|
|
{{end}}
|
|
|
|
|
{{end}}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-09-05 15:39:06 -06:00
|
|
|
<!-- Date Range -->
|
2025-09-03 14:35:47 -06:00
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
<label for="date_from" class="text-gray-700 font-medium">From:</label>
|
2025-09-05 15:39:06 -06:00
|
|
|
<input type="date" name="date_from" id="date_from" value="{{.DateFrom}}" class="px-3 py-2 text-sm border border-gray-200 bg-white focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-purple-500"/>
|
2025-09-03 14:35:47 -06:00
|
|
|
<label for="date_to" class="text-gray-700 font-medium">To:</label>
|
2025-09-05 15:39:06 -06:00
|
|
|
<input type="date" name="date_to" id="date_to" value="{{.DateTo}}" class="px-3 py-2 text-sm border border-gray-200 bg-white focus:outline-none focus:ring-1 focus:ring-purple-500 focus:border-purple-500"/>
|
2025-09-03 14:35:47 -06:00
|
|
|
</div>
|
|
|
|
|
|
2025-09-05 15:39:06 -06:00
|
|
|
<button type="submit" class="px-4 py-2 bg-purple-600 text-white font-medium hover:bg-purple-700 transition-all duration-200 text-sm">
|
2025-09-03 14:35:47 -06:00
|
|
|
<i class="fas fa-chart-bar mr-2"></i>Generate Report
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Actions -->
|
|
|
|
|
{{if .Result}}
|
|
|
|
|
<div class="flex items-center gap-3 text-sm">
|
|
|
|
|
<div class="text-gray-600">
|
|
|
|
|
<span>{{.Result.Count}} results</span>
|
|
|
|
|
</div>
|
2025-09-05 15:39:06 -06:00
|
|
|
<button onclick="exportResults()" class="px-3 py-1.5 bg-green-600 text-white hover:bg-green-700 transition-colors">
|
2025-09-03 14:35:47 -06:00
|
|
|
<i class="fas fa-download mr-1"></i>Export CSV
|
|
|
|
|
</button>
|
2025-09-05 15:39:06 -06:00
|
|
|
<button onclick="printReport()" class="px-3 py-1.5 bg-blue-600 text-white hover:bg-blue-700 transition-colors">
|
2025-09-03 14:35:47 -06:00
|
|
|
<i class="fas fa-print mr-1"></i>Print
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{{end}}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-09-05 15:39:06 -06:00
|
|
|
<!-- Main Content -->
|
2025-09-03 14:35:47 -06:00
|
|
|
<div class="flex-1 overflow-auto">
|
|
|
|
|
{{if .Result}}
|
|
|
|
|
{{if .Result.Error}}
|
|
|
|
|
<div class="p-6">
|
|
|
|
|
<div class="bg-red-50 border border-red-200 p-6">
|
2025-09-05 15:39:06 -06:00
|
|
|
<h3 class="text-lg font-semibold text-red-800 mb-2">Report Error</h3>
|
|
|
|
|
<p class="text-red-700">{{.Result.Error}}</p>
|
2025-09-03 14:35:47 -06:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{{else}}
|
|
|
|
|
<!-- Report Header -->
|
2025-09-05 15:39:06 -06:00
|
|
|
<div class="bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h2 class="text-xl font-semibold text-gray-900">{{.ReportTitle}}</h2>
|
|
|
|
|
<p class="text-sm text-gray-600 mt-1">{{.ReportDescription}}</p>
|
2025-09-03 14:35:47 -06:00
|
|
|
</div>
|
2025-09-05 15:39:06 -06:00
|
|
|
<div class="text-sm text-gray-500">Generated: {{.GeneratedAt}}</div>
|
2025-09-03 14:35:47 -06:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Results Table -->
|
|
|
|
|
{{if gt .Result.Count 0}}
|
|
|
|
|
<div class="flex-1 overflow-x-auto overflow-y-auto bg-white">
|
|
|
|
|
<table class="w-full divide-gray-200 text-sm table-auto">
|
2025-09-05 15:39:06 -06:00
|
|
|
<thead class="bg-gray-50 sticky top-0">
|
2025-09-03 14:35:47 -06:00
|
|
|
<tr class="text-left text-gray-700 font-medium border-b border-gray-200">
|
|
|
|
|
{{range .Result.Columns}}
|
|
|
|
|
<th class="px-6 py-3 whitespace-nowrap">{{formatColumnName .}}</th>
|
|
|
|
|
{{end}}
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody class="divide-y divide-gray-200">
|
|
|
|
|
{{range .Result.Rows}}
|
|
|
|
|
<tr class="hover:bg-gray-50">
|
|
|
|
|
{{range .}}
|
|
|
|
|
<td class="px-6 py-3 text-sm text-gray-900">{{.}}</td>
|
|
|
|
|
{{end}}
|
|
|
|
|
</tr>
|
|
|
|
|
{{end}}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-09-05 15:39:06 -06:00
|
|
|
<!-- Summary Stats -->
|
2025-09-03 14:35:47 -06:00
|
|
|
{{if .SummaryStats}}
|
|
|
|
|
<div class="bg-gray-50 border-t border-gray-200 px-6 py-4">
|
|
|
|
|
<h4 class="text-sm font-semibold text-gray-700 mb-3">Summary Statistics</h4>
|
|
|
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
|
|
|
{{range .SummaryStats}}
|
|
|
|
|
<div class="bg-white border border-gray-200 px-3 py-2">
|
|
|
|
|
<div class="text-xs text-gray-500">{{.Label}}</div>
|
|
|
|
|
<div class="text-lg font-semibold text-gray-900">{{.Value}}</div>
|
|
|
|
|
</div>
|
|
|
|
|
{{end}}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{{end}}
|
|
|
|
|
|
|
|
|
|
{{else}}
|
|
|
|
|
<div class="flex-1 flex items-center justify-center">
|
2025-09-05 15:39:06 -06:00
|
|
|
<p class="text-gray-500">No results match your selected criteria</p>
|
2025-09-03 14:35:47 -06:00
|
|
|
</div>
|
|
|
|
|
{{end}}
|
|
|
|
|
{{end}}
|
|
|
|
|
{{else}}
|
|
|
|
|
<div class="flex-1 flex items-center justify-center">
|
2025-09-05 15:39:06 -06:00
|
|
|
<p class="text-gray-600">Select a category and report to generate results</p>
|
2025-09-03 14:35:47 -06:00
|
|
|
</div>
|
|
|
|
|
{{end}}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
const reportDefinitions = {
|
|
|
|
|
users: [
|
2025-09-05 15:39:06 -06:00
|
|
|
{ id: 'participation', name: 'Volunteer Participation Rate' },
|
|
|
|
|
{ id: 'top_performers', name: 'Top Performing Volunteers' },
|
|
|
|
|
{ id: 'efficiency', name: 'Response-to-Donation Ratio' },
|
|
|
|
|
{ id: 'coverage', name: 'User Address Coverage' }
|
2025-09-03 14:35:47 -06:00
|
|
|
],
|
2025-09-05 15:39:06 -06:00
|
|
|
address: [
|
|
|
|
|
{ id: 'responses_by_address', name: 'Total Responses by Address' },
|
|
|
|
|
{ id: 'donations_by_address', name: 'Total Donations by Address' },
|
|
|
|
|
{ id: 'street_breakdown', name: 'Street-Level Breakdown' },
|
|
|
|
|
{ id: 'quadrant_summary', name: 'Quadrant Summary' }
|
2025-09-03 14:35:47 -06:00
|
|
|
],
|
|
|
|
|
appointments: [
|
2025-09-05 15:39:06 -06:00
|
|
|
{ id: 'upcoming', name: 'Upcoming Appointments' },
|
|
|
|
|
{ id: 'completion', name: 'Appointments Completion Rate' },
|
|
|
|
|
{ id: 'geo_distribution', name: 'Appointments by Quadrant' },
|
|
|
|
|
{ id: 'lead_time', name: 'Average Lead Time' }
|
2025-09-03 14:35:47 -06:00
|
|
|
],
|
|
|
|
|
polls: [
|
2025-09-05 15:39:06 -06:00
|
|
|
{ id: 'distribution', name: 'Response Distribution' },
|
|
|
|
|
{ id: 'average', name: 'Average Poll Response' },
|
|
|
|
|
{ id: 'donations_by_poll', name: 'Donations by Poll' },
|
|
|
|
|
{ id: 'correlation', name: 'Response-to-Donation Correlation' }
|
2025-09-03 14:35:47 -06:00
|
|
|
],
|
|
|
|
|
availability: [
|
2025-09-05 15:39:06 -06:00
|
|
|
{ id: 'by_date', name: 'Volunteer Availability by Date' },
|
|
|
|
|
{ id: 'gaps', name: 'Coverage Gaps' },
|
|
|
|
|
{ id: 'overlaps', name: 'Volunteer Overlaps' },
|
|
|
|
|
{ id: 'fulfillment', name: 'Volunteer Fulfillment' }
|
2025-09-03 14:35:47 -06:00
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function updateReports() {
|
2025-09-05 15:39:06 -06:00
|
|
|
const category = document.getElementById('category').value;
|
2025-09-03 14:35:47 -06:00
|
|
|
const reportSelect = document.getElementById('report');
|
|
|
|
|
reportSelect.innerHTML = '<option value="">Select Report</option>';
|
2025-09-05 15:39:06 -06:00
|
|
|
if (reportDefinitions[category]) {
|
|
|
|
|
reportDefinitions[category].forEach(r => {
|
|
|
|
|
const opt = document.createElement('option');
|
|
|
|
|
opt.value = r.id;
|
|
|
|
|
opt.textContent = r.name;
|
|
|
|
|
reportSelect.appendChild(opt);
|
2025-09-03 14:35:47 -06:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function exportResults() {
|
2025-09-05 15:39:06 -06:00
|
|
|
const params = new URLSearchParams(new FormData(document.querySelector('form')));
|
2025-09-03 14:35:47 -06:00
|
|
|
params.set('export', 'csv');
|
2025-09-05 15:39:06 -06:00
|
|
|
window.location.href = `/reports?${params.toString()}`;
|
2025-09-03 14:35:47 -06:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 15:39:06 -06:00
|
|
|
function printReport() { window.print(); }
|
2025-09-03 14:35:47 -06:00
|
|
|
|
2025-09-05 15:39:06 -06:00
|
|
|
document.addEventListener("DOMContentLoaded", updateReports);
|
2025-09-03 14:35:47 -06:00
|
|
|
</script>
|
2025-09-05 15:39:06 -06:00
|
|
|
{{ end }}
|