Tasks 12-13: Fix release date input to type=date, add sort direction toggle to games page

This commit is contained in:
2026-04-29 01:45:04 +08:00
parent 8a063bfb6c
commit 7903c9fbbd
4 changed files with 21 additions and 7 deletions
+11 -1
View File
@@ -31,6 +31,7 @@ interface DeviceOption {
} }
type SortOption = "recent" | "name" | "benchmarks" type SortOption = "recent" | "name" | "benchmarks"
type SortDirection = "asc" | "desc"
const DECK_STATUS_CONFIG: Record<string, { label: string; className: string }> = { const DECK_STATUS_CONFIG: Record<string, { label: string; className: string }> = {
native: { label: "Native", className: "bg-green-500/10 border-green-500/20 text-green-400" }, native: { label: "Native", className: "bg-green-500/10 border-green-500/20 text-green-400" },
@@ -62,6 +63,7 @@ export function GamesPageClient({
const [selectedGenres, setSelectedGenres] = useState<string[]>([]) const [selectedGenres, setSelectedGenres] = useState<string[]>([])
const [selectedDevice, setSelectedDevice] = useState("") const [selectedDevice, setSelectedDevice] = useState("")
const [sort, setSort] = useState<SortOption>("recent") const [sort, setSort] = useState<SortOption>("recent")
const [sortDirection, setSortDirection] = useState<SortDirection>("desc")
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [showFilters, setShowFilters] = useState(false) const [showFilters, setShowFilters] = useState(false)
@@ -76,12 +78,13 @@ export function GamesPageClient({
params.set("offset", String(offset)) params.set("offset", String(offset))
params.set("limit", "24") params.set("limit", "24")
params.set("sort", sort) params.set("sort", sort)
params.set("order", sortDirection)
if (search) params.set("search", search) if (search) params.set("search", search)
if (selectedDevice) params.set("device", selectedDevice) if (selectedDevice) params.set("device", selectedDevice)
if (selectedGenres.length === 1) params.set("genre", selectedGenres[0]) if (selectedGenres.length === 1) params.set("genre", selectedGenres[0])
return `/api/games/listing?${params.toString()}` return `/api/games/listing?${params.toString()}`
}, },
[sort, search, selectedDevice, selectedGenres], [sort, sortDirection, search, selectedDevice, selectedGenres],
) )
// Load more function for infinite scroll // Load more function for infinite scroll
@@ -217,6 +220,13 @@ export function GamesPageClient({
</option> </option>
))} ))}
</select> </select>
<button
onClick={() => setSortDirection(prev => prev === "asc" ? "desc" : "asc")}
className="px-2 py-2 rounded-md text-sm bg-text/5 border border-border hover:bg-text/10 transition-colors cursor-pointer"
title={sortDirection === "asc" ? "Sort ascending" : "Sort descending"}
>
{sortDirection === "asc" ? "↑" : "↓"}
</button>
<button <button
onClick={() => setShowFilters(!showFilters)} onClick={() => setShowFilters(!showFilters)}
className={`px-3 py-2 rounded-md text-sm transition-colors cursor-pointer border ${ className={`px-3 py-2 rounded-md text-sm transition-colors cursor-pointer border ${
+1 -1
View File
@@ -179,7 +179,7 @@ export function NonSteamEditForm({ game, platformSupport, hardwareList, isOwner,
<div className="space-y-1.5"> <div className="space-y-1.5">
<label className="text-xs font-medium text-text/60">Release Date</label> <label className="text-xs font-medium text-text/60">Release Date</label>
<input <input
type="text" type="date"
value={releaseDate} value={releaseDate}
onChange={e => setReleaseDate(e.target.value)} onChange={e => setReleaseDate(e.target.value)}
className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary"
@@ -230,10 +230,9 @@ export function NonSteamBasicInfoStep({ value, onChange }: NonSteamBasicInfoStep
<div className="space-y-1.5"> <div className="space-y-1.5">
<label className="text-xs font-medium text-text/60">Release Date</label> <label className="text-xs font-medium text-text/60">Release Date</label>
<input <input
type="text" type="date"
value={value.releaseDate} value={value.releaseDate}
onChange={(e) => update("releaseDate", e.target.value)} onChange={(e) => update("releaseDate", e.target.value)}
placeholder="e.g. 2017-02-24"
className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors" className="w-full px-4 py-3 rounded-lg border border-border bg-text/5 text-text text-sm placeholder:text-text/40 outline-none focus:border-primary focus:ring-2 focus:ring-primary/50 transition-colors"
/> />
</div> </div>
+8 -3
View File
@@ -21,6 +21,7 @@ export const gamesListingRoutes = new Elysia({ prefix: "/games/listing" }).get(
const genre = query.genre || "" const genre = query.genre || ""
const device = query.device || "" const device = query.device || ""
const sort = query.sort || "recent" const sort = query.sort || "recent"
const order = query.order === "asc" ? asc : desc
// Build where conditions // Build where conditions
const conditions = [] const conditions = []
@@ -106,12 +107,12 @@ export const gamesListingRoutes = new Elysia({ prefix: "/games/listing" }).get(
let orderBy let orderBy
switch (sort) { switch (sort) {
case "name": case "name":
orderBy = asc(games.title) orderBy = order(games.title)
break break
case "benchmarks": case "benchmarks":
case "recent": case "recent":
default: default:
orderBy = desc(games.createdAt) orderBy = order(games.createdAt)
break break
} }
@@ -195,7 +196,10 @@ export const gamesListingRoutes = new Elysia({ prefix: "/games/listing" }).get(
// If sorting by benchmarks, re-sort the enriched data // If sorting by benchmarks, re-sort the enriched data
if (sort === "benchmarks") { if (sort === "benchmarks") {
enrichedData.sort((a, b) => b.benchmarkCount - a.benchmarkCount) const sortFn = query.order === "asc"
? (a: any, b: any) => a.benchmarkCount - b.benchmarkCount
: (a: any, b: any) => b.benchmarkCount - a.benchmarkCount
enrichedData.sort(sortFn)
} }
const [{ count: total }] = await countQuery const [{ count: total }] = await countQuery
@@ -217,6 +221,7 @@ export const gamesListingRoutes = new Elysia({ prefix: "/games/listing" }).get(
genre: t.Optional(t.String()), genre: t.Optional(t.String()),
device: t.Optional(t.String()), device: t.Optional(t.String()),
sort: t.Optional(t.String()), sort: t.Optional(t.String()),
order: t.Optional(t.String()),
}), }),
}, },
) )