Skip to content
Snippets Groups Projects
Commit 2887a678 authored by Yusuf Akgül's avatar Yusuf Akgül :hatching_chick:
Browse files

search functionality wortking

parent 30033916
No related branches found
No related tags found
1 merge request!7search functionality working
Pipeline #34669 passed
import { InfiniteScrollGames } from "@/components/InfiniteScroll";
import Search from "@/components/search-games";
import SearchInput from "@/components/search-input";
import Sort from "@/components/sort-games";
// renders a list of games infinitely (presumably)
......@@ -8,7 +8,7 @@ export default async function GamesPage() {
<main className="relative lg:gap-10 xl:grid xl:grid-cols-[1fr_240px]">
<div className="grid">
<div className="flex flex-col gap-10 items-center w-full">
<Search className="p-3 lg:w-2/3 2xl:w-1/3" />
<SearchInput className="p-3 lg:w-2/3 2xl:w-1/3" />
</div>
<InfiniteScrollGames />
</div>
......
......@@ -11,7 +11,7 @@ export default async function ContentLayout({
}: DashboardLayoutProps) {
return (
<div className="flex min-h-screen flex-col">
<div className="mx-6 my-6 flex-1 md:grid md:grid-cols-[220px_1fr] md:gap-6 lg:grid-cols-[240px_1fr] lg:gap-10">
<div className="flex-1 md:grid md:grid-cols-[220px_1fr] md:gap-6 lg:grid-cols-[240px_1fr] lg:gap-10">
<aside className="hidden w-[200px] flex-col md:flex">
<div className="sticky top-0">
<DashboardNav items={dashboardConfig.sidebarNav} />
......
......@@ -4,7 +4,10 @@ import { NextRequest, NextResponse } from "next/server";
export async function GET(req: NextRequest) {
const p = req.nextUrl.searchParams;
try {
const games = await getGames(p.get('page') ? parseInt(p.get('page') as string) : undefined);
const page = parseInt(p.get('page') as string)
const search = p.get('search')
const games = await getGames(page, search ? search : '');
return NextResponse.json(games);
} catch (error) {
return NextResponse.json(error, { status: 500 });
......
......@@ -27,7 +27,9 @@ export default function RootLayout({
<Suspense fallback={<SiteLoad />}>
<ClerkProvider>
<Providers>
{children}
<div className="mx-32 my-6">
{children}
</div>
</Providers>
</ClerkProvider>
</Suspense>
......
......@@ -4,10 +4,16 @@ import Game from "@/components/Game";
import { Card } from "@/components/ui/card";
import { IGame } from "@/types/igdb-types";
import { useInfiniteQuery } from "@tanstack/react-query";
import { useSearchParams } from "next/navigation";
import { Fragment } from "react";
import InfiniteScroll from "react-infinite-scroll-component";
export function InfiniteScrollGames() {
const search = useSearchParams()
const searchQuery = search.get('search');
const searchURL = searchQuery ? `&search=${searchQuery}` : "";
const {
status,
data,
......@@ -15,10 +21,10 @@ export function InfiniteScrollGames() {
fetchNextPage,
hasNextPage,
} = useInfiniteQuery(
['infiniteGames'],
['infiniteGames', searchQuery],
async ({ pageParam = 1 }) =>
await fetch(`/api/games/?page=${pageParam}`,
{ cache: 'force-cache' }
await fetch(`/api/games/?page=${pageParam}` + searchURL,
{ cache: 'no-cache' }
).then((result) => result.json() as Promise<IGame[]>),
{
getNextPageParam: (lastPage, pages) => {
......@@ -46,7 +52,7 @@ export function InfiniteScrollGames() {
}
>
<div className="">
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-4 lg:gap-8">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4 lg:gap-8">
{data.pages.map((page, i) => (
<Fragment key={i}>
{page.map((game: IGame) => (
......
......@@ -42,7 +42,7 @@ export default function DashboardNav({ items }: DashboardNavProps) {
<Link key={index} href={item.disabled ? "/" : item.href} className={index == 6 ? "mt-10" : ""}>
<span
className={cn(
"group flex items-center rounded-md px-3 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground",
"group flex items-center rounded-md px-3 py-2 font-medium hover:bg-accent hover:text-accent-foreground",
path === item.href ? "bg-accent" : "transparent",
item.disabled && "cursor-not-allowed opacity-80"
)}
......
"use client"
import { cn } from '@/lib/utils';
import { usePathname, useRouter } from 'next/navigation';
import { useState } from 'react';
import { Button } from './ui/button';
import { Icons } from './ui/icons';
import { Input } from './ui/input';
import { toast } from './ui/use-toast';
interface DocsSearchProps extends React.HTMLAttributes<HTMLFormElement> { }
export default function Search({ className, ...props }: DocsSearchProps) {
const [searchText, setSearchText] = useState('');
export default function SearchInput({ className, ...props }: DocsSearchProps) {
const [searchQuery, setSearchQuery] = useState("");
const router = useRouter();
const pathname = usePathname();
function onSubmit(event: React.SyntheticEvent) {
function onSearch(event: React.FormEvent) {
event.preventDefault()
return toast({
title: "Not implemented",
description: "We're still working on the search.",
})
const encoededQuery = encodeURIComponent(searchQuery)
router.push(`${pathname}?search=${encoededQuery}`)
};
return (
<form
onSubmit={onSubmit}
onSubmit={onSearch}
className={cn("relative w-full flex justify-end items-center", className)}
{...props}
>
......@@ -31,6 +31,8 @@ export default function Search({ className, ...props }: DocsSearchProps) {
type="search"
placeholder="Search..."
className="rounded-full pr-12"
value={searchQuery}
onChange={(event) => setSearchQuery(event.target.value)}
/>
<Button variant="outline" size="lg" className="absolute align-middle h-8 px-2.5 mr-1">
<Icons.arrowRight className="h-3 w-3" />
......
......@@ -28,39 +28,35 @@ async function getToken(): Promise<IAuth> {
}
// fetches the top 200 games with a rating of 96 or higher
export async function getGames(page = 1): Promise<IGame[]> {
try {
const auth = await getToken();
const url = new URL(`${IGDB_BASE_URL}/games`);
let offset = calculateOffset(page, limit);
const response = await fetch(url, {
method: 'POST',
headers: {
'Client-ID': CLIENT_ID,
'Authorization': `Bearer ${auth.access_token}`
},
body: `fields name, cover.*; limit ${limit}; offset ${offset};
export async function getGames(page: number, search: string): Promise<IGame[]> {
const auth = await getToken();
const url = new URL(`${IGDB_BASE_URL}/games`);
let offset = calculateOffset(page, limit);
const response = await fetch(url, {
method: 'POST',
headers: {
'Client-ID': CLIENT_ID,
'Authorization': `Bearer ${auth.access_token}`
},
body: `fields name, cover.*; limit ${limit}; offset ${offset};
sort total_rating desc; where total_rating_count > 2
& cover != null & total_rating != null & rating != null;`
});
& cover != null & total_rating != null & rating != null
& name ~ *"${search}"*;`
});
if (!response.ok) {
throw new Error(`Error fetching games: ${response.statusText}`);
}
if (!response.ok) {
throw new Error(`Error fetching games: ${response.statusText}`);
}
const games = await response.json() as IGame[];
const games: IGame[] = await response.json() as IGame[];
games.forEach(game => {
game.cover.url = getImageURL(game.cover.image_id, 'cover_big');
});
games.forEach(game => {
game.cover.url = getImageURL(game.cover.image_id, 'cover_big');
});
return games;
} catch (error) {
console.error('Error in getGames:', error);
throw error;
}
return games;
}
// fetches a single game by id
......
......@@ -10,7 +10,7 @@ const IGDB_IMG_BASE_URL = process.env.IGDB_IMG_BASE_URL ?? ''
// changes the default size of the image to be fetched
export function getImageURL(hashId: string, size: string): string {
return `${IGDB_IMG_BASE_URL}/t_${size}_2x/${hashId}.jpg`
return `${IGDB_IMG_BASE_URL}/t_${size}/${hashId}.jpg`
}
// calculates the offset for the query
......
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment