ランキング
検索
詳細検索 Ctrl + K 検索を開く

v0による上位投票者

v0 AIツールを使用して、Top-Games APIで現代的な投票者ランキングインターフェースを作成する方法をご覧ください。

AI生成
React / Next.js
Top-Games API

使用されたプロンプト

このインターフェースを生成するためにv0で使用されたプロンプトです:

ゲームサーバーからの最優秀投票者のリスト。リストはこのAPIで取得できます:https://api.top-games.net/v1/servers/:token/players-ranking

得られた結果

v0 AIは状態管理、API呼び出し、モダンなデザインを備えた完全なReactインターフェースを自動生成しました:

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 ? "準優勝" : "3位"}
                          </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(CSSフレームワーク)
コンポーネント: shadcn/ui
デプロイメント: Vercel
リソース

この例を再現するのに役立つリンク: