Skip to content
This repository was archived by the owner on Jun 2, 2025. It is now read-only.

Commit 7536448

Browse files
authored
Add a basic template with only search (#14)
* add basic search template * update readme, description, favicon, and fix type error
1 parent f08b351 commit 7536448

File tree

17 files changed

+5463
-0
lines changed

17 files changed

+5463
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["next/core-web-vitals", "next/typescript"]
3+
}

basic-search-frontend/.gitignore

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

basic-search-frontend/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Running Locally
2+
3+
To run this project locally, start by setting up your environment variables. Copy the definitions from [`.env.example`](https://github.com/hypermodeAI/hyper-commerce-frontend/blob/main/.env.example) into a new file named `.env.local` at the root of your project, and provide the values from your Hypermode dashboard.
4+
5+
Once your environment variables are configured, install the necessary dependencies with:
6+
7+
```
8+
npm install
9+
```
10+
11+
Then, start the development server using:
12+
13+
```
14+
npm run dev
15+
```
16+
17+
Your app should be up and running at [localhost:3000](http://localhost:3000/)
18+
19+
## Deploy with Vercel
20+
21+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/hypermodeAI/hyper-commerce-frontend)
22+
23+
_NOTES_: Make sure your environment variables are added to your Vercel project.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"use server";
2+
3+
type FetchQueryProps = {
4+
query: string;
5+
variables?: { [key: string]: unknown };
6+
};
7+
8+
const fetchQuery = async ({ query, variables }: FetchQueryProps) => {
9+
try {
10+
const res = await fetch(process.env.HYPERMODE_API_ENDPOINT as string, {
11+
method: "POST",
12+
headers: {
13+
"Content-Type": "application/json",
14+
Authorization: `Bearer ${process.env.HYPERMODE_API_TOKEN}`,
15+
},
16+
body: JSON.stringify({
17+
query,
18+
variables,
19+
}),
20+
cache: "no-store",
21+
});
22+
23+
if (res.status < 200 || res.status >= 300) {
24+
throw new Error(res.statusText);
25+
}
26+
27+
const { data, error, errors } = await res.json();
28+
return { data, error: error || errors };
29+
} catch (err) {
30+
console.error("error in fetchQuery:", err);
31+
return { data: null, error: err };
32+
}
33+
};
34+
35+
export async function searchProducts(
36+
query: string,
37+
maxItems: number,
38+
thresholdStars: number,
39+
inStockOnly: boolean = false
40+
) {
41+
const graphqlQuery = `
42+
query searchProducts($query: String!, $maxItems: Int!, $thresholdStars: Float!, $inStockOnly: Boolean!) {
43+
searchProducts(query: $query, maxItems: $maxItems, thresholdStars: $thresholdStars, inStockOnly: $inStockOnly) {
44+
searchObjs {
45+
product {
46+
name
47+
id
48+
image
49+
description
50+
stars
51+
price
52+
isStocked
53+
category
54+
}
55+
}
56+
}
57+
}
58+
`;
59+
60+
const { error, data } = await fetchQuery({
61+
query: graphqlQuery,
62+
variables: {
63+
query,
64+
maxItems,
65+
thresholdStars,
66+
inStockOnly,
67+
},
68+
});
69+
70+
if (error) {
71+
return { error: Array.isArray(error) ? error[0] : error };
72+
} else {
73+
return { data };
74+
}
75+
}
14.7 KB
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Search from "./search";
2+
import { Suspense } from "react";
3+
import Logo from "./logo";
4+
5+
export default function Header() {
6+
return (
7+
<>
8+
<div className="md:flex hidden items-center justify-between space-x-4 px-4">
9+
<Suspense fallback={<div>Loading</div>}>
10+
<Logo />
11+
</Suspense>
12+
<Suspense fallback={<SearchSkeleton />}>
13+
<Search />
14+
</Suspense>
15+
</div>
16+
<div className="md:hidden items-center justify-between px-4">
17+
<div className="flex items-center justify-between mb-2">
18+
<Suspense fallback={<div>Loading</div>}>
19+
<Logo />
20+
</Suspense>
21+
</div>
22+
<Suspense fallback={<SearchSkeleton />}>
23+
<Search />
24+
</Suspense>
25+
</div>
26+
</>
27+
);
28+
}
29+
30+
function SearchSkeleton() {
31+
return (
32+
<div className="relative flex flex-1 flex-shrink-0">
33+
<div className="peer block w-full h-11 rounded-md border border-stone-700 py-[9px] pl-14 text-sm outline-2 placeholder:text-gray-500 bg-black" />
34+
</div>
35+
);
36+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { Metadata } from "next";
2+
import "./globals.css";
3+
import Header from "./header";
4+
5+
export const metadata: Metadata = {
6+
title: "Hypermode Search",
7+
description: "A simple template with real-time search using Hypermode.",
8+
};
9+
10+
export default function RootLayout({
11+
children,
12+
}: Readonly<{
13+
children: React.ReactNode;
14+
}>) {
15+
return (
16+
<html lang="en">
17+
<body className="max-w-6xl mx-auto bg-stone-900 text-white my-8 space-y-8">
18+
<Header />
19+
<div>{children}</div>
20+
</body>
21+
</html>
22+
);
23+
}

basic-search-frontend/app/logo.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"use client";
2+
3+
import Link from "next/link";
4+
5+
export default function Logo() {
6+
return (
7+
<Link href="https://github.com/hypermodeAI/hyper-commerce?tab=readme-ov-file#hyper-commerce">
8+
<div className="text-white flex items-center font-semibold text-xl italic">
9+
<svg
10+
xmlns="http://www.w3.org/2000/svg"
11+
width="26"
12+
height="26"
13+
fill="none"
14+
className="fill-foreground"
15+
viewBox="0 0 130 124"
16+
>
17+
<g clipPath="url(#a)">
18+
<path
19+
fill="#fff"
20+
d="M90.2 0 76 55.5H39.5L53.7 0H14.2L0 55.5h32.3l-16.7 65.8 60.3-65.4-17.2 67.2h39.5L129.7 0z"
21+
/>
22+
</g>
23+
<defs>
24+
<clipPath id="a">
25+
<path fill="#fff" d="M0 0h130v123.2H0z" />
26+
</clipPath>
27+
</defs>
28+
</svg>
29+
</div>
30+
</Link>
31+
);
32+
}

basic-search-frontend/app/page.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Suspense } from "react";
2+
import { searchProducts } from "./actions";
3+
4+
export const metadata = {
5+
title: "Search",
6+
description: "Search for products in the store.",
7+
};
8+
9+
export default async function SearchPage({
10+
searchParams,
11+
}: {
12+
searchParams?: { [key: string]: string | string[] | undefined };
13+
}) {
14+
const { q: searchValue } = searchParams as {
15+
[key: string]: string;
16+
};
17+
18+
const response = await searchProducts(searchValue, 20, 0);
19+
20+
const products = response?.data?.searchProducts?.searchObjs || [];
21+
22+
return (
23+
<div className="px-4 flex md:flex-row flex-col md:space-x-4 space-y-4 md:space-y-0 w-full">
24+
<div className="w-full">
25+
<Suspense fallback={<div>Loading...</div>}>
26+
<div>
27+
{products?.length > 0 ? (
28+
<div className="">
29+
{products.map(
30+
(
31+
item: {
32+
product: {
33+
description: string;
34+
name: string;
35+
};
36+
},
37+
i: number
38+
) => (
39+
<div key={i} className="mb-8">
40+
<div>
41+
<div className="font-semibold text-xl mb-1">
42+
{item.product.name}
43+
</div>
44+
<div className="text-stone-400">
45+
{item.product.description}
46+
</div>
47+
</div>
48+
</div>
49+
)
50+
)}
51+
</div>
52+
) : (
53+
<div className="py-4 text-white/40 flex flex-col items-center justify-center w-full">
54+
<div className="font-semibold text-2xl">
55+
No items matching your search
56+
</div>
57+
<div>
58+
Try searching again, such as: &quot;Halloween
59+
decorations&quot;
60+
</div>
61+
</div>
62+
)}
63+
</div>
64+
</Suspense>
65+
</div>
66+
</div>
67+
);
68+
}

0 commit comments

Comments
 (0)