feat: Add indicator version filtering to analytics dashboard

- Add version dropdown selector (v9, v8, v6, v5, all) to frontend
- Update backend API to accept ?version= query parameter
- Add version filter to all 5 broken SQL queries using Prisma parameterized queries
- Update Data Collection Status to use selected version instead of hardcoded v8
- Add version context to all recommendations
- Add URL encoding for version parameter (security best practice)
- Validate version parameter against whitelist (SQL injection protection)

Co-authored-by: mindesbunister <32161838+mindesbunister@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-05 14:01:41 +00:00
parent 9fdabb20ae
commit 4f913a7ab8
2 changed files with 297 additions and 134 deletions

View File

@@ -11,15 +11,25 @@ interface AnalysisResult {
action?: string
}
// Available indicator versions for filtering
const INDICATOR_VERSIONS = [
{ value: 'v9', label: 'v9 - Current Production' },
{ value: 'v8', label: 'v8 - Previous Version' },
{ value: 'v6', label: 'v6 - Legacy' },
{ value: 'v5', label: 'v5 - Legacy' },
{ value: 'all', label: 'All Versions (Historical)' },
]
export default function OptimizationPage() {
const [analyses, setAnalyses] = useState<AnalysisResult[]>([])
const [loading, setLoading] = useState(true)
const [lastRefresh, setLastRefresh] = useState<Date | null>(null)
const [selectedVersion, setSelectedVersion] = useState<string>('v9') // Default to current production
const loadAnalyses = async () => {
setLoading(true)
try {
const response = await fetch('/api/optimization/analyze')
const response = await fetch(`/api/optimization/analyze?version=${encodeURIComponent(selectedVersion)}`)
const data = await response.json()
setAnalyses(data.analyses)
setLastRefresh(new Date())
@@ -32,7 +42,8 @@ export default function OptimizationPage() {
useEffect(() => {
loadAnalyses()
}, [])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedVersion]) // Reload when version changes
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white p-8">
@@ -60,8 +71,25 @@ export default function OptimizationPage() {
)}
</div>
{/* Refresh Button */}
<div className="mb-6">
{/* Version Selector and Refresh Button */}
<div className="mb-6 flex items-center gap-4 flex-wrap">
<div className="flex items-center gap-3">
<label htmlFor="version-select" className="text-sm font-medium text-slate-300">
Indicator Version:
</label>
<select
id="version-select"
value={selectedVersion}
onChange={(e) => setSelectedVersion(e.target.value)}
className="px-4 py-2 bg-slate-700 rounded-lg border border-slate-600 text-white focus:border-blue-500 focus:outline-none"
>
{INDICATOR_VERSIONS.map((v) => (
<option key={v.value} value={v.value}>
{v.label}
</option>
))}
</select>
</div>
<button
onClick={loadAnalyses}
disabled={loading}
@@ -69,6 +97,11 @@ export default function OptimizationPage() {
>
{loading ? '🔄 Analyzing...' : '🔄 Refresh All Analyses'}
</button>
{selectedVersion !== 'all' && (
<span className="text-sm text-green-400 font-medium">
Filtering by {selectedVersion.toUpperCase()}
</span>
)}
</div>
{/* Loading State */}