Skip to content

Commit c32efae

Browse files
committed
Set up Next.js project with Tailwind CSS, add guides data and search functionality
1 parent 286627d commit c32efae

File tree

12 files changed

+265
-10
lines changed

12 files changed

+265
-10
lines changed

packages/guides/data/guides.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
[
2+
{
3+
"title": "Building Subgraphs 101",
4+
"slug": "building-subgraphs-101",
5+
"description": "Learn the basics of creating a subgraph on The Graph network.",
6+
"categories": ["Subgraphs", "Getting Started"],
7+
"date": "2024-05-18",
8+
"author": "Graph Docs Team",
9+
"types": ["blog"],
10+
"url": "https://thegraph.com/blog/building-subgraphs-101"
11+
},
12+
{
13+
"title": "Indexing Smart Contracts with The Graph",
14+
"slug": "indexing-smart-contracts",
15+
"description": "Step-by-step video tutorial for indexing contracts using Graph CLI.",
16+
"categories": ["Indexing", "Video"],
17+
"date": "2024-03-02",
18+
"author": "Graph Protocol",
19+
"types": ["video"],
20+
"url": "https://youtube.com/watch?v=dQw4w9WgXcQ"
21+
},
22+
{
23+
"title": "Using Graph Client in React",
24+
"slug": "using-graph-client-react",
25+
"description": "How to query subgraphs efficiently in a React front-end.",
26+
"categories": ["Client", "React"],
27+
"date": "2023-12-10",
28+
"author": "Edge & Node",
29+
"types": ["blog", "repo"],
30+
"url": "https://github.com/graphprotocol/example"
31+
}
32+
]

packages/guides/next-env.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

packages/guides/next.config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @ts-check
2+
3+
/** @type {import('next').NextConfig} */
4+
const nextConfig = {
5+
reactStrictMode: true,
6+
trailingSlash: true,
7+
};
8+
9+
export default nextConfig;

packages/guides/package.json

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@
66
"types": "dist/index.d.ts",
77
"files": ["dist"],
88
"scripts": {
9-
"build": "tsc -p tsconfig.json",
10-
"dev": "tsc -w -p tsconfig.json",
11-
"typecheck": "tsc --noEmit -p tsconfig.json"
9+
"dev": "next dev -p 3100",
10+
"build": "next build",
11+
"preview": "next start -p 3100",
12+
"typecheck": "tsc --noEmit"
13+
},
14+
"dependencies": {
15+
"next": "^14.2.28",
16+
"react": "^18.3.1",
17+
"react-dom": "^18.3.1",
18+
"fuse.js": "^7.0.0"
1219
},
13-
"dependencies": {},
1420
"devDependencies": {
15-
"typescript": "^5.8.3"
21+
"typescript": "^5.8.3",
22+
"tailwindcss": "^3.4.17",
23+
"postcss": "^8.5.3",
24+
"autoprefixer": "^10.4.21"
1625
}
1726
}

packages/guides/pages/_app.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import '@/styles/globals.css';
2+
import type { AppProps } from 'next/app';
3+
4+
export default function App({ Component, pageProps }: AppProps) {
5+
return <Component {...pageProps} />;
6+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { GetServerSideProps } from 'next';
2+
import guides from '@/data/guides.json';
3+
4+
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
5+
const guide = (guides as any[]).find((g) => g.slug === params?.slug);
6+
if (guide) {
7+
return {
8+
redirect: {
9+
destination: guide.url,
10+
permanent: false,
11+
},
12+
};
13+
}
14+
return { notFound: true };
15+
};
16+
17+
export default function Guide() {
18+
return null;
19+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { useMemo, useState } from 'react';
2+
import Fuse from 'fuse.js';
3+
import guidesData from '@/data/guides.json';
4+
import Link from 'next/link';
5+
6+
interface Guide {
7+
title: string;
8+
slug: string;
9+
description: string;
10+
categories: string[];
11+
date: string;
12+
author: string;
13+
types: string[];
14+
url: string;
15+
}
16+
17+
const fuse = new Fuse(guidesData as Guide[], {
18+
includeScore: true,
19+
threshold: 0.4,
20+
keys: ['title', 'description', 'categories', 'types'],
21+
});
22+
23+
const allCategories = Array.from(
24+
new Set((guidesData as Guide[]).flatMap((g) => g.categories)),
25+
).sort();
26+
27+
export default function GuidesPage() {
28+
const [query, setQuery] = useState('');
29+
const [selected, setSelected] = useState<Set<string>>(new Set());
30+
31+
const guides = useMemo(() => {
32+
let filtered: Guide[] = guidesData as Guide[];
33+
if (selected.size) {
34+
filtered = filtered.filter((g) => g.categories.some((c) => selected.has(c)));
35+
}
36+
if (query.trim()) {
37+
return fuse.search(query).map((r) => r.item).filter((g) => filtered.includes(g));
38+
}
39+
return filtered;
40+
}, [query, selected]);
41+
42+
const toggleCategory = (cat: string) => {
43+
const next = new Set(selected);
44+
if (next.has(cat)) next.delete(cat);
45+
else next.add(cat);
46+
setSelected(next);
47+
};
48+
49+
return (
50+
<div className="flex min-h-screen p-6 gap-8 font-sans">
51+
<aside className="w-64 border-r pr-4">
52+
<h2 className="text-xl font-semibold mb-4">Categories</h2>
53+
<ul className="space-y-2">
54+
{allCategories.map((cat) => (
55+
<li key={cat} className="flex items-center space-x-2">
56+
<input
57+
type="checkbox"
58+
checked={selected.has(cat)}
59+
onChange={() => toggleCategory(cat)}
60+
className="h-4 w-4 rounded border-gray-400"
61+
/>
62+
<span className="text-sm">{cat}</span>
63+
</li>
64+
))}
65+
</ul>
66+
</aside>
67+
<main className="flex-1">
68+
<div className="mb-6">
69+
<input
70+
type="text"
71+
placeholder="Search guides..."
72+
value={query}
73+
onChange={(e) => setQuery(e.target.value)}
74+
className="w-full md:w-1/2 border rounded px-3 py-2"
75+
/>
76+
</div>
77+
<ul className="space-y-6">
78+
{guides.map((g) => (
79+
<li key={g.slug} className="border rounded p-4 hover:shadow">
80+
<Link href={`/guides/${g.slug}`} target="_blank">
81+
<h3 className="text-lg font-semibold mb-1">{g.title}</h3>
82+
</Link>
83+
<p className="text-sm text-gray-600 mb-2">{g.description}</p>
84+
<div className="text-xs text-gray-500 flex flex-wrap gap-2">
85+
{g.categories.map((c) => (
86+
<span key={c} className="bg-gray-200 px-2 py-0.5 rounded">
87+
{c}
88+
</span>
89+
))}
90+
{g.types.map((t) => (
91+
<span key={t} className="bg-purple-200 px-2 py-0.5 rounded">
92+
{t}
93+
</span>
94+
))}
95+
<span>{new Date(g.date).toLocaleDateString()}</span>
96+
</div>
97+
</li>
98+
))}
99+
</ul>
100+
</main>
101+
</div>
102+
);
103+
}

packages/guides/postcss.config.mjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
};

packages/guides/styles/globals.css

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;

packages/guides/tailwind.config.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import defaultTheme from 'tailwindcss/defaultTheme';
2+
3+
/** @type {import('tailwindcss').Config} */
4+
export default {
5+
content: [
6+
'./pages/**/*.{js,ts,jsx,tsx}',
7+
'./components/**/*.{js,ts,jsx,tsx}',
8+
'./app/**/*.{js,ts,jsx,tsx}',
9+
],
10+
theme: {
11+
extend: {
12+
fontFamily: {
13+
sans: ['Inter', ...defaultTheme.fontFamily.sans],
14+
},
15+
},
16+
},
17+
plugins: [],
18+
};

packages/guides/tsconfig.json

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,18 @@
55
"outDir": "dist",
66
"declaration": true,
77
"declarationMap": true,
8-
"sourceMap": true
8+
"sourceMap": true,
9+
"baseUrl": ".",
10+
"paths": {
11+
"@/*": [
12+
"*"
13+
]
14+
},
15+
"resolveJsonModule": true,
16+
"jsx": "preserve",
17+
"module": "ESNext"
918
},
10-
"include": ["src"]
11-
}
19+
"include": [
20+
"src"
21+
]
22+
}

pnpm-lock.yaml

Lines changed: 36 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)