1const e=`import { tx } from '@/i18n/t'; 2import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; 3import { Badge } from '@/components/ui/badge'; 4import { Skeleton } from '@/components/ui/skeleton'; 5import { 6 Award, 7 TrendingUp, 8 TrendingDown, 9 CheckCircle, 10 XCircle, 11 BarChart3 12} from 'lucide-react'; 13import { useCertificateStats } from '@/hooks/useCertificates'; 14 15export function CertificateStatsPanel() { 16 const { data: stats, isLoading, error } = useCertificateStats(); 17 18 if (isLoading) { 19 return ( 20 <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4"> 21 {[...Array(4)].map((_, i) => ( 22 <Card key={i}> 23 <CardContent className="pt-6"> 24 <Skeleton className="h-8 w-16 mb-2" /> 25 <Skeleton className="h-4 w-24" /> 26 </CardContent> 27 </Card> 28 ))} 29 </div> 30 ); 31 } 32 33 if (error || !stats) { 34 return ( 35 <Card className="border-destructive/30 bg-destructive/5"> 36 <CardContent className="py-6 text-center text-destructive"> 37 {tx("Erro ao carregar estatÃsticas de certificados")} 38 </CardContent> 39 </Card> 40 ); 41 } 42 43 const monthChange = stats.thisMonth - stats.lastMonth; 44 const monthChangePercent = stats.lastMonth > 0 45 ? Math.round((monthChange / stats.lastMonth) * 100) 46 : stats.thisMonth > 0 ? 100 : 0; 47 48 return ( 49 <div className="space-y-6"> 50 {/* Overview Stats */} 51 <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4"> 52 <Card> 53 <CardContent className="pt-6"> 54 <div className="flex items-center justify-between"> 55 <div> 56 <p className="text-3xl font-bold">{stats.total}</p> 57 <p className="text-sm text-muted-foreground">{tx("Total de Certificados")}</p> 58 </div> 59 <div className="p-3 rounded-full bg-primary/10"> 60 <Award className="h-6 w-6 text-primary" /> 61 </div> 62 </div> 63 </CardContent> 64 </Card> 65 66 <Card> 67 <CardContent className="pt-6"> 68 <div className="flex items-center justify-between"> 69 <div> 70 <p className="text-3xl font-bold text-green-600">{stats.active}</p> 71 <p className="text-sm text-muted-foreground">{tx("Ativos")}</p> 72 </div> 73 <div className="p-3 rounded-full bg-green-500/10"> 74 <CheckCircle className="h-6 w-6 text-green-600" /> 75 </div> 76 </div> 77 </CardContent> 78 </Card> 79 80 <Card> 81 <CardContent className="pt-6"> 82 <div className="flex items-center justify-between"> 83 <div> 84 <p className="text-3xl font-bold text-red-600">{stats.revoked}</p> 85 <p className="text-sm text-muted-foreground">{tx("Revogados")}</p> 86 </div> 87 <div className="p-3 rounded-full bg-red-500/10"> 88 <XCircle className="h-6 w-6 text-red-600" /> 89 </div> 90 </div> 91 </CardContent> 92 </Card> 93 94 <Card> 95 <CardContent className="pt-6"> 96 <div className="flex items-center justify-between"> 97 <div> 98 <div className="flex items-center gap-2"> 99 <p className="text-3xl font-bold">{stats.thisMonth}</p> 100 {monthChange !== 0 && ( 101 <Badge 102 variant="outline" 103 className={monthChange > 0 104 ? "bg-green-500/10 text-green-700 border-green-500/30" 105 : "bg-red-500/10 text-red-700 border-red-500/30" 106 } 107 > 108 {monthChange > 0 ? ( 109 <TrendingUp className="h-3 w-3 mr-1" /> 110 ) : ( 111 <TrendingDown className="h-3 w-3 mr-1" /> 112 )} 113 {monthChange > 0 ? '+' : ''}{monthChangePercent}% 114 </Badge> 115 )} 116 </div> 117 <p className="text-sm text-muted-foreground">{tx("Este mês")}</p> 118 </div> 119 <div className="p-3 rounded-full bg-blue-500/10"> 120 <BarChart3 className="h-6 w-6 text-blue-600" /> 121 </div> 122 </div> 123 </CardContent> 124 </Card> 125 </div> 126 127 {/* By Pillar */} 128 <Card> 129 <CardHeader> 130 <CardTitle className="text-base">
130{tx("Certificados por Pilar")}</CardTitle> 131 <CardDescription>{tx("Distribuição de certificados emitidos por área")}</CardDescription> 132 </CardHeader> 133 <CardContent> 134 <div className="grid gap-4 md:grid-cols-3"> 135 <div className="flex items-center justify-between p-4 rounded-lg bg-blue-500/5 border border-blue-500/20"> 136 <div> 137 <p className="text-2xl font-bold text-blue-700">{stats.byPillar.RA}</p> 138 <p className="text-sm text-muted-foreground">{tx("Relações Ambientais")}</p> 139 </div> 140 <Badge variant="outline" className="bg-blue-500/10 text-blue-700 border-blue-500/30"> 141 RA 142 </Badge> 143 </div> 144 145 <div className="flex items-center justify-between p-4 rounded-lg bg-green-500/5 border border-green-500/20"> 146 <div> 147 <p className="text-2xl font-bold text-green-700">{stats.byPillar.OE}</p> 148 <p className="text-sm text-muted-foreground">{tx("Organização Estrutural")}</p> 149 </div> 150 <Badge variant="outline" className="bg-green-500/10 text-green-700 border-green-500/30"> 151 OE 152 </Badge> 153 </div> 154 155 <div className="flex items-center justify-between p-4 rounded-lg bg-purple-500/5 border border-purple-500/20"> 156 <div> 157 <p className="text-2xl font-bold text-purple-700">{stats.byPillar.AO}</p> 158 <p className="text-sm text-muted-foreground">{tx("Ações Operacionais")}</p> 159 </div> 160 <Badge variant="outline" className="bg-purple-500/10 text-purple-700 border-purple-500/30"> 161 AO 162 </Badge> 163 </div> 164 </div> 165 </CardContent> 166 </Card> 167 </div> 168 ); 169} 170`;export{e as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.