Files
EpicNext-Cms/src/app/(site)/search/page.tsx
T
openhands 7a41775c7e fix: disable route prefetching app-wide to avoid spurious requests
Wrap next/link in a shared Link component that ships prefetch=false by
default, so no route is ever prefetched (viewport or hover) anymore, and
drop the DNS prefetch hint. Removes hidden background requests that were
the source of intermittent issues.
2026-08-29 19:27:13 +02:00

193 lines
5.1 KiB
TypeScript

import { and, eq, like } from "drizzle-orm";
import type { Metadata } from "next";
import Link from "@/components/link";
import { UserAvatarThumbnail } from "@/components/shared/user-avatar-thumbnail";
import { SurfaceCard } from "@/components/surface-card";
import { db, Rooms, User } from "@/lib/db";
export const metadata: Metadata = { title: "Search" };
type SearchParams = Promise<{ q?: string }>;
export default async function SearchPage({
searchParams,
}: {
searchParams: SearchParams;
}) {
const { q } = await searchParams;
const query = q?.trim();
if (!query) {
return (
<SurfaceCard className="p-10 text-center">
<p
className="text-sm font-bold"
style={{ color: "var(--color-text-muted)" }}
>
Enter a search term to find users.
</p>
</SurfaceCard>
);
}
const [users, rooms] = await Promise.all([
db
.select({
id: User.id,
username: User.username,
look: User.look,
motto: User.motto,
online: User.online,
rank: User.rank,
})
.from(User)
.where(like(User.username, `%${query}%`))
.limit(30)
.catch(() => []),
db
.select({
id: Rooms.id,
name: Rooms.name,
ownerName: Rooms.ownerName,
users: Rooms.users,
usersMax: Rooms.usersMax,
})
.from(Rooms)
.where(and(like(Rooms.name, `%${query}%`), eq(Rooms.state, "open")))
.limit(12)
.catch(() => []),
]);
return (
<div className="flex flex-col gap-6">
<SurfaceCard className="p-4">
<form action="/search" method="GET" className="flex gap-2">
<input
type="text"
name="q"
defaultValue={query}
placeholder="Search users or rooms..."
className="flex-1 rounded-xl border-2 px-4 py-2.5 text-sm font-medium"
style={{
backgroundColor: "var(--color-background)",
color: "var(--color-text-readable)",
borderColor:
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
}}
/>
<button
type="submit"
className="rounded-xl px-6 py-2.5 font-extrabold text-sm transition-all duration-200 hover:scale-[1.02] active:scale-95"
style={{
background: "var(--color-primary)",
color: "var(--color-primary-foreground)",
}}
>
Search
</button>
</form>
</SurfaceCard>
<p className="text-sm" style={{ color: "var(--color-text-muted)" }}>
Results for &quot;{query}&quot; — {users.length} user
{users.length !== 1 ? "s" : ""}
{rooms.length > 0 &&
`, ${rooms.length} room${rooms.length !== 1 ? "s" : ""}`}
</p>
{users.length > 0 && (
<SurfaceCard className="p-5">
<h2
className="text-lg font-black tracking-tight mb-4"
style={{ color: "var(--color-text-readable)" }}
>
Users
</h2>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3">
{users.map((u) => (
<Link
key={u.id}
href={`/u/${u.username}`}
className="flex flex-col items-center gap-1.5 rounded-xl p-3 transition-all duration-200 hover:-translate-y-0.5"
style={{
background:
"color-mix(in srgb, var(--color-primary) 4%, transparent)",
}}
>
<UserAvatarThumbnail
figure={u.look}
alt={u.username}
options={{ direction: 2 }}
className="rounded-lg"
/>
<span
className="text-xs font-extrabold truncate max-w-full text-center"
style={{ color: "var(--color-text-readable)" }}
>
{u.username}
</span>
<span
className="text-[9px] font-semibold"
style={{ color: "var(--color-text-muted)" }}
>
{u.online === "1" ? "🟢 Online" : "Offline"}
</span>
</Link>
))}
</div>
</SurfaceCard>
)}
{rooms.length > 0 && (
<SurfaceCard className="p-5">
<h2
className="text-lg font-black tracking-tight mb-4"
style={{ color: "var(--color-text-readable)" }}
>
Rooms
</h2>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
{rooms.map((room) => (
<Link
key={room.id}
href={`/room/${room.id}`}
className="rounded-xl border p-3 transition-all duration-200 hover:-translate-y-0.5"
style={{
background:
"color-mix(in srgb, var(--color-primary) 4%, transparent)",
borderColor:
"color-mix(in srgb, var(--color-text-muted) 10%, transparent)",
}}
>
<p
className="text-sm font-extrabold truncate"
style={{ color: "var(--color-text-readable)" }}
>
{room.name}
</p>
<p
className="text-[10px] font-semibold mt-0.5"
style={{ color: "var(--color-text-muted)" }}
>
{room.ownerName} · {room.users}/{room.usersMax}
</p>
</Link>
))}
</div>
</SurfaceCard>
)}
{users.length === 0 && rooms.length === 0 && (
<SurfaceCard className="p-10 text-center">
<p
className="text-sm font-bold"
style={{ color: "var(--color-text-muted)" }}
>
No users or rooms found for &quot;{query}&quot;.
</p>
</SurfaceCard>
)}
</div>
);
}