Рейтинги
Поиск
Расширенный поиск

Топ голосующих с v0

Узнайте как использовать AI инструмент v0 для создания современного интерфейса рейтинга голосующих с нашим API Top-Games.

Создано ИИ
React / Next.js
Top-Games API

Использованный промпт

Вот промпт, который был использован с v0 для генерации этого интерфейса:

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

Полученный результат

ИИ v0 автоматически сгенерировал полный интерфейс React с управлением состоянием, вызовами API и современным дизайном:

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("Не удалось получить рейтинги голосующих")
        }

        const data = await response.json()
        console.log("API Response:", data) // Лог отладки

        // Обработка различных возможных структур ответа
        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("Неожиданный формат ответа:", data)
          throw new Error("Неожиданный формат ответа")
        }

        // Убедиться, что у каждого игрока есть необходимые поля
        const processedPlayers = playersData.map((player: Player, index: number) => ({
          id: `player-${index}`,
          playername: player.playername || `Игрок ${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 : "Произошла ошибка")
      } 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" />
              Лучшие Голосующие
            </CardTitle>
            <CardDescription>Загрузка рейтингов голосующих...</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" />
              Ошибка загрузки рейтингов голосующих
            </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" />
            Таблица Лидеров Лучших Голосующих
          </CardTitle>
          <CardDescription className="flex items-center gap-4">
            <span>Рейтинги голосования сервера</span>
            <Badge variant="outline" className="flex items-center gap-1">
              <TrendingUp className="w-3 h-3" />
              {totalVotes} всего голосов
            </Badge>
          </CardDescription>
        </CardHeader>
        <CardContent>
          <div className="space-y-3">
            {players.length === 0 ? (
              <div className="text-center py-8 text-muted-foreground">Данные о голосующих недоступны</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 ? "Чемпион" : position === 2 ? "Второе место" : "Третье место"}
                          </Badge>
                        )}
                      </div>
                      <p className="text-sm text-muted-foreground">Ранг #{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 ? "голос" : "голосов"}</div>
                    </div>
                  </div>
                )
              })
            )}
          </div>
        </CardContent>
      </Card>
    </div>
  )
}
Сгенерированный пользовательский интерфейс:
app/page.tsx
"use client"

import VoterRankings from "../voter-rankings"

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

Демонстрация

Вот созданный интерфейс, работающий с реальными данными:

Информация об API
Конечная точка: /v1/servers/:token/players-ranking
Метод: GET
Формат: JSON
Аутентификация: Токен сервера
Использованные инструменты
Фреймворк: Next.js 14
UI: Tailwind CSS
Компоненты: shadcn/ui
Развёртывание: Vercel
Ресурсы

Полезные ссылки для воспроизведения этого примера: