feat: non-Steam game submission wizard with SteamGridDB integration

This commit is contained in:
2026-04-29 00:00:10 +08:00
parent 74fe0447ee
commit c6d4e7b3d1
10 changed files with 1303 additions and 0 deletions
+3
View File
@@ -23,6 +23,7 @@ import { steamSearchRoutes } from "@/lib/api/steam-search"
import { searchUnifiedRoutes } from "@/lib/api/search-unified"
import { gameStubRoutes } from "@/lib/api/game-stub"
import { gameStatsRoutes } from "@/lib/api/game-stats"
import { gamesManualRoutes } from "@/lib/api/games-manual"
import { gamesListingRoutes } from "@/lib/api/games-listing"
import { steamgridProxyRoutes } from "@/lib/api/steamgrid-proxy"
@@ -89,6 +90,8 @@ export const app = new Elysia({ prefix: "/api" })
.use(steamgridProxyRoutes)
// Game stats aggregation
.use(gameStatsRoutes)
// Manual game creation
.use(gamesManualRoutes)
// Saved games
.use(savedGamesRoutes)
// Contact form
+30
View File
@@ -0,0 +1,30 @@
import { redirect } from "next/navigation"
import { headers } from "next/headers"
import { auth } from "@/lib/auth"
import { NonSteamWizard } from "@/components/wizard/non-steam-wizard"
export const dynamic = "force-dynamic"
export const metadata = {
title: "Add Non-Steam Game",
}
export default async function AddGamePage() {
const h = await headers()
const session = await auth.api.getSession({ headers: h })
if (!session?.user) {
redirect("/login")
}
return (
<div className="max-w-3xl mx-auto px-4 py-8 w-full">
<div className="mb-8">
<h1 className="text-2xl font-bold mb-2">Add a Game</h1>
<p className="text-sm text-text/60">
Add a non-Steam game to DeckyVault. Search for cover art, set platform support, and submit.
</p>
</div>
<NonSteamWizard />
</div>
)
}
+14
View File
@@ -14,6 +14,8 @@ import {
} from "lucide-react"
import { FaSteam } from "react-icons/fa"
import Image from "next/image"
import Link from "next/link"
import { useSession } from "@/lib/auth-client"
import { WindowsIcon, MacIcon, LinuxIcon } from "@/app/components/PlatformIcons"
interface UnifiedResult {
@@ -49,6 +51,7 @@ interface UnifiedResult {
function SearchContent() {
const searchParams = useSearchParams()
const router = useRouter()
const { data: session } = useSession()
const query = searchParams.get("q") || ""
const [results, setResults] = useState<UnifiedResult[]>([])
@@ -191,6 +194,17 @@ function SearchContent() {
</AnimatePresence>
</motion.div>
)}
{session?.user && (
<div className="mt-6 text-center">
<p className="text-sm text-text/50">
Can&apos;t find your game?{" "}
<Link href="/game/add" className="text-primary hover:underline cursor-pointer">
Add it manually
</Link>
</p>
</div>
)}
</div>
</section>
)