Update: Few issues to resolve see readme

this push will conclude the majority of pulls. this repos will now, not be actively be managed or any further code pushes will not be frequent.
This commit is contained in:
Mann Patel
2025-09-11 16:54:30 -06:00
parent 144436bbf3
commit b21e76eed0
19 changed files with 1953 additions and 1442 deletions

View File

@@ -1,149 +1,241 @@
{{ 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>
<option value="addresses" {{if eq .Category "addresses"}}selected{{end}}>Addresses</option>
<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>
<!-- Date Range -->
<div class="flex items-center gap-2">
<label for="date_from" class="text-gray-700 font-medium">From:</label>
<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"/>
<label for="date_to" class="text-gray-700 font-medium">To:</label>
<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"/>
</div>
<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">
<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 class="flex-1 flex flex-col overflow-hidden" x-data="reportsData()">
<!-- Toolbar -->
<div class="bg-white border-b border-gray-200 px-4 md:px-6 py-4">
<div class="flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4">
<!-- Report Selection Form -->
<form method="GET" action="/reports" class="flex flex-col sm:flex-row items-start sm:items-center gap-4 w-full lg:w-auto">
<!-- Category Selection -->
<div class="flex items-center gap-2">
<label for="category" class="text-sm text-gray-600 whitespace-nowrap font-medium">Category:</label>
<select
name="category"
id="category"
onchange="updateReports()"
class="px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 min-w-48"
>
<option value="">Select Category</option>
<option value="users" {{if eq .Category "users"}}selected{{end}}>Users & Teams</option>
<option value="addresses" {{if eq .Category "addresses"}}selected{{end}}>Addresses</option>
<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>
<!-- <button onclick="exportResults()" class="px-3 py-1.5 bg-green-600 text-white hover:bg-green-700 transition-colors">
<i class="fas fa-download mr-1"></i>Export CSV
<!-- Report Selection -->
<div class="flex items-center gap-2">
<label for="report" class="text-sm text-gray-600 whitespace-nowrap font-medium">Report:</label>
<select
name="report"
id="report"
class="px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-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>
<!-- Date Range -->
<div class="flex items-center gap-2">
<label for="date_from" class="text-sm text-gray-600 whitespace-nowrap font-medium">From:</label>
<input
type="date"
name="date_from"
id="date_from"
value="{{.DateFrom}}"
class="px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<div class="flex items-center gap-2">
<label for="date_to" class="text-sm text-gray-600 whitespace-nowrap font-medium">To:</label>
<input
type="date"
name="date_to"
id="date_to"
value="{{.DateTo}}"
class="px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
<!-- Generate Button -->
<button
type="submit"
class="px-6 py-2.5 bg-blue-600 text-white text-sm font-medium hover:bg-blue-700 transition-colors rounded-lg"
>
<i class="fas fa-chart-bar mr-2"></i>Generate Report
</button>
<button onclick="printReport()" class="px-3 py-1.5 bg-blue-600 text-white hover:bg-blue-700 transition-colors">
<i class="fas fa-print mr-1"></i>Print
</button> -->
</form>
<!-- Actions & Results Count -->
{{if .Result}}
<div class="flex flex-col sm:flex-row items-start sm:items-center gap-4">
<div class="text-sm text-gray-600">
<span class="font-medium">{{.Result.Count}}</span> results
</div>
</div>
{{end}}
</div>
</div>
<!-- Main Content -->
<div class="flex-1 overflow-auto">
<div class="flex-1 p-4 md:p-6 overflow-auto">
{{if .Result}}
{{if .Result.Error}}
<div class="p-6">
<div class="bg-red-50 border border-red-200 p-6">
<h3 class="text-lg font-semibold text-red-800 mb-2">Report Error</h3>
<p class="text-red-700">{{.Result.Error}}</p>
<!-- Error State -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<div class="bg-red-50 border border-red-200 rounded-lg p-6">
<div class="flex items-center space-x-3">
<i class="fas fa-exclamation-triangle text-red-500 text-xl"></i>
<div>
<h3 class="text-lg font-semibold text-red-800 mb-2">Report Error</h3>
<p class="text-red-700">{{.Result.Error}}</p>
</div>
</div>
</div>
</div>
{{else}}
<!-- Report Header -->
<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>
<div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
<!-- Report Header -->
<div class="bg-gray-50 border-b border-gray-200 px-6 py-4">
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div>
<h2 class="text-xl font-semibold text-gray-900">{{.ReportTitle}}</h2>
<p class="text-sm text-gray-600 mt-1">{{.ReportDescription}}</p>
</div>
<div class="text-sm text-gray-500">
<i class="fas fa-clock mr-1"></i>Generated: {{.GeneratedAt}}
</div>
</div>
</div>
<div class="text-sm text-gray-500">Generated: {{.GeneratedAt}}</div>
</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">
<thead class="bg-gray-50 sticky top-0">
<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>
{{if gt .Result.Count 0}}
<!-- Desktop Table -->
<div class="hidden lg:block overflow-x-auto">
<table class="w-full min-w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
{{range .Result.Columns}}
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{{formatColumnName .}}
</th>
{{end}}
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-100">
{{range .Result.Rows}}
<tr class="hover:bg-gray-50">
{{range .}}
<td class="px-6 py-4 text-sm text-gray-900">{{.}}</td>
{{end}}
</tr>
{{end}}
</tbody>
</table>
</div>
<!-- Summary Stats -->
{{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>
<!-- Mobile Cards -->
<div class="lg:hidden">
<div class="space-y-4 p-4">
{{range $rowIndex, $row := .Result.Rows}}
<div class="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden">
<!-- Card Header -->
<div class="bg-gray-50 px-4 py-3 border-b border-gray-200">
<div class="flex items-center space-x-2">
<i class="fas fa-chart-line text-gray-400"></i>
<span class="text-sm font-semibold text-gray-900">Record {{add $rowIndex 1}}</span>
</div>
</div>
<!-- Card Content -->
<div class="p-4 space-y-3">
{{range $colIndex, $column := $.Result.Columns}}
<div class="flex justify-between items-start">
<span class="text-sm text-gray-500 font-medium">{{formatColumnName $column}}:</span>
<span class="text-sm text-gray-900 text-right ml-4">{{index $row $colIndex}}</span>
</div>
{{end}}
</div>
</div>
{{end}}
</div>
</div>
<!-- Summary Stats -->
{{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-4">
<i class="fas fa-chart-pie mr-2 text-blue-500"></i>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 rounded-lg p-4">
<div class="text-xs text-gray-500 uppercase tracking-wide font-medium">{{.Label}}</div>
<div class="text-2xl font-bold text-gray-900 mt-1">{{.Value}}</div>
</div>
{{end}}
</div>
</div>
{{end}}
</div>
</div>
{{end}}
{{else}}
<div class="flex-1 flex items-center justify-center">
<p class="text-gray-500">No results match your selected criteria</p>
{{else}}
<!-- No Results -->
<div class="text-center py-16">
<div class="text-gray-400 mb-4">
<i class="fas fa-chart-bar text-4xl"></i>
</div>
<h3 class="text-lg font-medium text-gray-900 mb-2">No Results Found</h3>
<p class="text-gray-500">No results match your selected criteria. Try adjusting your filters or date range.</p>
</div>
{{end}}
</div>
{{end}}
{{end}}
{{else}}
<div class="flex-1 flex items-center justify-center">
<p class="text-gray-600">Select a category and report to generate results</p>
<!-- Initial State -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
<div class="text-center py-16">
<div class="text-gray-400 mb-6">
<i class="fas fa-chart-line text-5xl"></i>
</div>
<h3 class="text-xl font-medium text-gray-900 mb-4">Generate Report</h3>
<p class="text-gray-500 mb-6 max-w-md mx-auto">
Select a category and report type above to generate comprehensive analytics and insights.
</p>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 max-w-2xl mx-auto">
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<i class="fas fa-users text-blue-500 text-2xl mb-2"></i>
<h4 class="font-medium text-gray-900 mb-1">User Analytics</h4>
<p class="text-sm text-gray-600">Track volunteer performance and participation rates</p>
</div>
<div class="bg-green-50 border border-green-200 rounded-lg p-4">
<i class="fas fa-map-marker-alt text-green-500 text-2xl mb-2"></i>
<h4 class="font-medium text-gray-900 mb-1">Location Insights</h4>
<p class="text-sm text-gray-600">Analyze address coverage and geographic data</p>
</div>
<div class="bg-purple-50 border border-purple-200 rounded-lg p-4">
<i class="fas fa-calendar-check text-purple-500 text-2xl mb-2"></i>
<h4 class="font-medium text-gray-900 mb-1">Schedule Reports</h4>
<p class="text-sm text-gray-600">Review appointments and availability patterns</p>
</div>
</div>
</div>
</div>
{{end}}
</div>
</div>
<script>
function reportsData() {
return {
// Any Alpine.js data can go here if needed
};
}
const reportDefinitions = {
users: [
{ id: 'volunteer_participation_rate', name: 'Volunteer Participation Rate' },
@@ -189,14 +281,46 @@
}
}
function exportResults() {
const params = new URLSearchParams(new FormData(document.querySelector('form')));
params.set('export', 'csv');
window.location.href = `/reports?${params.toString()}`;
}
function printReport() { window.print(); }
document.addEventListener("DOMContentLoaded", updateReports);
// Initialize reports on page load
document.addEventListener("DOMContentLoaded", function() {
updateReports();
});
</script>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<style>
/* Consistent styling */
input, select, button {
transition: all 0.2s ease;
}
button {
font-weight: 500;
letter-spacing: 0.025em;
}
/* Print styles */
@media print {
.no-print {
display: none !important;
}
.bg-gray-50 {
background-color: white !important;
}
}
/* Mobile responsive adjustments */
@media (max-width: 640px) {
.space-x-4 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(1rem * var(--tw-space-x-reverse));
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
}
}
</style>
{{ end }}