Rankings
Buscar
Búsqueda Avanzada

Top Votantes con v0

Descubre cómo usar la herramienta de IA v0 para crear una interfaz moderna de clasificación de votantes con nuestra API de Top-Games.

Generado por IA
React / Next.js
Top-Games API

Prompt Utilizado

Aquí está el prompt que se usó con v0 para generar esta interfaz:

A list of the best voters from a game server. The list can be obtained by this API : https://api.top-games.net/v1/servers/:token/players-ranking

Resultado obtenido

La IA v0 generó automáticamente una interfaz React completa con gestión de estado, llamadas API y diseño moderno:

voter-ranking.tsx
"use client"

import { useEffect, useState } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { Users, TrendingUp } from "lucide-react"

interface Player {
  id: string
  playername: string
  votes: number
  position: number
}

export default function Component() {
  const [players, setPlayers] = useState<Player[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [totalVotes, setTotalVotes] = useState(0)

  useEffect(() => {
    const fetchVoterRankings = async () => {
      try {
        setLoading(true)
        const response = await fetch("https://api.top-games.net/v1/servers/TOKEN/players-ranking")

        if (!response.ok) {
          throw new Error("Error al obtener clasificaciones de votantes")
        }

        const data = await response.json()
        console.log("API Response:", data) // Log de depuración

        // Manejar diferentes estructuras de respuesta posibles
        let playersData: Player[] = []
        if (data.players && Array.isArray(data.players)) {
          playersData = data.players
        } else if (Array.isArray(data)) {
          playersData = data
        } else if (data.data && Array.isArray(data.data)) {
          playersData = data.data
        } else {
          console.error("Formato de respuesta inesperado:", data)
          throw new Error("Formato de respuesta inesperado")
        }

        // Asegurar que cada jugador tenga los campos requeridos
        const processedPlayers = playersData.map((player: Player, index: number) => ({
          id: `player-${index}`,
          playername: player.playername || `Jugador ${index + 1}`,
          votes: player.votes || 0,
          position: index + 1,
        }))

        setPlayers(processedPlayers)
        setTotalVotes(processedPlayers.reduce((sum, player) => sum + player.votes, 0))
      } catch (err) {
        setError(err instanceof Error ? err.message : "Ocurrió un error")
      } finally {
        setLoading(false)
      }
    }

    fetchVoterRankings()
  }, [])

  const getRankBadgeVariant = (position: number) => {
    switch (position) {
      case 1:
        return "default"
      case 2:
        return "secondary"
      case 3:
        return "outline"
      default:
        return "outline"
    }
  }

  if (loading) {
    return (
      <div className="w-full max-w-4xl mx-auto p-6">
        <Card>
          <CardHeader>
            <CardTitle className="flex items-center gap-2">
              <Users className="w-6 h-6" />
              Mejores Votantes
            </CardTitle>
            <CardDescription>Cargando clasificaciones de votantes...</CardDescription>
          </CardHeader>
          <CardContent>
            <div className="space-y-4">
              {[...Array(5)].map((_, i) => (
                <div key={i} className="flex items-center gap-4 p-4 border rounded-lg animate-pulse">
                  <div className="w-12 h-12 bg-muted rounded-full" />
                  <div className="flex-1 space-y-2">
                    <div className="h-4 bg-muted rounded w-1/3" />
                    <div className="h-3 bg-muted rounded w-1/4" />
                  </div>
                  <div className="h-6 bg-muted rounded w-16" />
                </div>
              ))}
            </div>
          </CardContent>
        </Card>
      </div>
    )
  }

  if (error) {
    return (
      <div className="w-full max-w-4xl mx-auto p-6">
        <Card>
          <CardHeader>
            <CardTitle className="flex items-center gap-2 text-destructive">
              <Users className="w-6 h-6" />
              Error al cargar clasificaciones de votantes
            </CardTitle>
            <CardDescription>{error}</CardDescription>
          </CardHeader>
        </Card>
      </div>
    )
  }

  return (
    <div className="w-full max-w-4xl mx-auto p-6 space-y-6">
      <Card>
        <CardHeader>
          <CardTitle className="flex items-center gap-2">
            <Users className="w-6 h-6" />
            Tabla de Clasificación de Mejores Votantes
          </CardTitle>
          <CardDescription className="flex items-center gap-4">
            <span>Clasificaciones de votación del servidor</span>
            <Badge variant="outline" className="flex items-center gap-1">
              <TrendingUp className="w-3 h-3" />
              {totalVotes} votos totales
            </Badge>
          </CardDescription>
        </CardHeader>
        <CardContent>
          <div className="space-y-3">
            {players.length === 0 ? (
              <div className="text-center py-8 text-muted-foreground">No hay datos de votantes disponibles</div>
            ) : (
              players.map((player, index) => {
                const position = index + 1
                return (
                  <div
                    key={player.id || player.playername}
                    className={`flex items-center gap-4 p-4 border rounded-lg transition-colors hover:bg-muted/50 ${
                      position <= 3 ? "bg-muted/30" : ""
                    }`}
                  >
                    <div className="flex-1">
                      <div className="flex items-center gap-2">
                        <h3 className="font-semibold">{player.playername}</h3>
                        {position <= 3 && (
                          <Badge variant={getRankBadgeVariant(position)} className="text-xs">
                            {position === 1 ? "Campeón" : position === 2 ? "Subcampeón" : "Tercer lugar"}
                          </Badge>
                        )}
                      </div>
                      <p className="text-sm text-muted-foreground">Rango #{position}</p>
                    </div>

                    <div className="text-right">
                      <div className="text-lg font-bold">{player.votes}</div>
                      <div className="text-xs text-muted-foreground">{player.votes === 1 ? "voto" : "votos"}</div>
                    </div>
                  </div>
                )
              })
            )}
          </div>
        </CardContent>
      </Card>
    </div>
  )
}
Interfaz de Usuario Generada:
app/page.tsx
"use client"

import VoterRankings from "../voter-rankings"

export default function Page() {
  return (
    <main>
      <VoterRankings />
    </main>
  )
}

Demostración

Aquí está la interfaz generada funcionando con datos reales:

Información API
Endpoint: /v1/servers/:token/players-ranking
Método: GET
Formato: JSON
Autenticación: Token del Servidor
Herramientas Utilizadas
Framework: Next.js 14
UI: Tailwind CSS
Componentes: shadcn/ui
Despliegue: Vercel
Recursos

Enlaces útiles para reproducir este ejemplo: