mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 11:12:11 +01:00
Security Enhancements: - Implemented proper rate limiting with automatic cleanup for /register and /forgot-password endpoints - Added memory usage protection with MAX_ENTRIES limit (10000) - Fixed rate limiter memory leaks by adding cleanup intervals - Improved IP extraction with x-real-ip and x-client-ip header support Code Quality Improvements: - Refactored ProcessingStatusManager from individual functions to class-based architecture - Maintained backward compatibility with singleton instance pattern - Fixed TypeScript strict mode violations across the codebase - Resolved all build errors and type mismatches UI Component Fixes: - Removed unused chart components (Charts.tsx, DonutChart.tsx) - Fixed calendar component type issues by removing unused custom implementations - Resolved theme provider type imports - Fixed confetti component default options handling - Corrected pointer component coordinate type definitions Type System Improvements: - Extended NextAuth types to support dual auth systems (regular and platform users) - Fixed nullable type handling throughout the codebase - Resolved Prisma JSON field type compatibility issues - Corrected SessionMessage and ImportRecord interface definitions - Fixed ES2015 iteration compatibility issues Database & Performance: - Updated database pool configuration for Prisma adapter compatibility - Fixed pagination response structure in user management endpoints - Improved error handling with proper error class usage Testing & Build: - All TypeScript compilation errors resolved - ESLint warnings remain but no errors - Build completes successfully with proper static generation
88 lines
2.8 KiB
TypeScript
88 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import type { TopQuestion } from "../lib/types";
|
|
|
|
interface TopQuestionsChartProps {
|
|
data: TopQuestion[];
|
|
title?: string;
|
|
}
|
|
|
|
export default function TopQuestionsChart({
|
|
data,
|
|
title = "Top 5 Asked Questions",
|
|
}: TopQuestionsChartProps) {
|
|
if (!data || data.length === 0) {
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-lg font-semibold">{title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-center py-8 text-muted-foreground">
|
|
No questions data available
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
// Find the maximum count to calculate relative bar widths
|
|
const maxCount = Math.max(...data.map((q) => q.count));
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-lg font-semibold">{title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{data.map((question, index) => {
|
|
const percentage =
|
|
maxCount > 0 ? (question.count / maxCount) * 100 : 0;
|
|
|
|
return (
|
|
<div key={question.question} className="relative pl-8">
|
|
{/* Question text */}
|
|
<div className="flex justify-between items-start mb-2">
|
|
<p className="text-sm font-medium leading-tight pr-4 flex-1 text-foreground">
|
|
{question.question}
|
|
</p>
|
|
<Badge variant="secondary" className="whitespace-nowrap">
|
|
{question.count}
|
|
</Badge>
|
|
</div>
|
|
|
|
{/* Progress bar */}
|
|
<div className="w-full bg-muted rounded-full h-2">
|
|
<div
|
|
className="bg-primary h-2 rounded-full transition-all duration-300 ease-in-out"
|
|
style={{ width: `${percentage}%` }}
|
|
/>
|
|
</div>
|
|
|
|
{/* Rank indicator */}
|
|
<div className="absolute -left-1 top-0 w-6 h-6 bg-primary text-primary-foreground text-xs font-bold rounded-full flex items-center justify-center">
|
|
{index + 1}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<Separator className="my-6" />
|
|
|
|
{/* Summary */}
|
|
<div className="flex justify-between text-sm text-muted-foreground">
|
|
<span>Total questions analyzed</span>
|
|
<span className="font-medium text-foreground">
|
|
{data.reduce((sum, q) => sum + q.count, 0)}
|
|
</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|