From c61bca45dbfc3ee1a4b658018ece023cef627fa2 Mon Sep 17 00:00:00 2001 From: "yongen.loong" Date: Mon, 9 Sep 2024 16:30:24 +0800 Subject: [PATCH] refactor: common listing page components --- .../{tutorial => listing-page}/filter.tsx | 2 +- components/listing-page/list.tsx | 63 +++++++++++++++++++ components/listing-page/search.tsx | 41 ++++++++++++ components/tutorial/filter-lang.tsx | 4 +- components/tutorial/filter-level.tsx | 4 +- components/tutorial/list.tsx | 48 +++----------- components/tutorial/search.tsx | 40 +----------- 7 files changed, 118 insertions(+), 84 deletions(-) rename components/{tutorial => listing-page}/filter.tsx (99%) create mode 100644 components/listing-page/list.tsx create mode 100644 components/listing-page/search.tsx diff --git a/components/tutorial/filter.tsx b/components/listing-page/filter.tsx similarity index 99% rename from components/tutorial/filter.tsx rename to components/listing-page/filter.tsx index dab0c45..bebbbfc 100644 --- a/components/tutorial/filter.tsx +++ b/components/listing-page/filter.tsx @@ -20,7 +20,7 @@ const FormSchema = z.object({ items: z.array(z.string()), }); -export function TutorialFilter({ +export function Filter({ searchKey, title, options, diff --git a/components/listing-page/list.tsx b/components/listing-page/list.tsx new file mode 100644 index 0000000..fe48f36 --- /dev/null +++ b/components/listing-page/list.tsx @@ -0,0 +1,63 @@ +import Image from "next/image"; +import { + Card, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import Link from "next/link"; +import clsx from "clsx"; + +interface Item { + id: string; + img: string; + title: string; + description: string; + tags: string[]; + link: string; +} + +export function List({ list }: { list: Item[] }) { + + if (list.length === 0) { + return

No items for the current search / filter.

+ } + + return ( +
+ {list.map((i) => ( + + +
+ Image +
+ + {i.title} + {i.description} + + + {i.tags.map((tag, index) => ( + + {tag} + + ))} + +
+ + ))} +
+ ); +} diff --git a/components/listing-page/search.tsx b/components/listing-page/search.tsx new file mode 100644 index 0000000..f089a70 --- /dev/null +++ b/components/listing-page/search.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { useSearchParams, useRouter, usePathname } from "next/navigation"; +import { useCallback, useEffect, useState } from "react"; +import { Input } from "@/components/ui/input"; + +export function Search({placeholder}: {placeholder?: string}) { + const searchParams = useSearchParams(); + const pathname = usePathname(); + const router = useRouter(); + const [search, setSearch] = useState(searchParams.get("search") || undefined); + + const createQueryString = useCallback( + (value?: string) => { + const params = new URLSearchParams(searchParams.toString()); + params.delete("search"); + + if (value) params.set("search", value); + + const res = params.toString(); + + if (res) return "?" + res; + + return ""; + }, + [searchParams] + ); + + useEffect(() => { + router.push(pathname + createQueryString(search)); + }, [search]); + + return ( + setSearch(e.target.value)} + className="w-1/2 mb-8" + /> + ); +} diff --git a/components/tutorial/filter-lang.tsx b/components/tutorial/filter-lang.tsx index ae50620..a1777a6 100644 --- a/components/tutorial/filter-lang.tsx +++ b/components/tutorial/filter-lang.tsx @@ -1,4 +1,4 @@ -import { TutorialFilter } from "./filter"; +import { Filter } from "@/components/listing-page/filter"; const lang = [ { @@ -12,5 +12,5 @@ const lang = [ ]; export function TutorialLangFilter() { - return ; + return ; } diff --git a/components/tutorial/filter-level.tsx b/components/tutorial/filter-level.tsx index 81a014f..28f86a8 100644 --- a/components/tutorial/filter-level.tsx +++ b/components/tutorial/filter-level.tsx @@ -1,4 +1,4 @@ -import { TutorialFilter } from "./filter"; +import { Filter } from "@/components/listing-page/filter"; const level = [ { @@ -16,5 +16,5 @@ const level = [ ]; export function TutorialLevelFilter() { - return ; + return ; } diff --git a/components/tutorial/list.tsx b/components/tutorial/list.tsx index af9b2bc..39df4ef 100644 --- a/components/tutorial/list.tsx +++ b/components/tutorial/list.tsx @@ -1,17 +1,9 @@ "use client"; -import Image from "next/image"; import { useSearchParams } from "next/navigation"; import { useMemo } from "react"; -import { - Card, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import Link from "next/link"; import { useTutorialList } from "@/data/client"; +import { List } from "@/components/listing-page/list"; export function TutorialList() { const searchParams = useSearchParams(); @@ -37,38 +29,12 @@ export function TutorialList() { i.description.toLowerCase().includes(search) ); - return all; + return all.map((i) => ({ + ...i, + tags: [i.level, i.lang], + link: `/tutorials/${i.id}`, + })); }, [data, searchParams]); - return ( -
- {list.map((i) => ( - - -
- Image -
- - {i.title} - {i.description} - - - - {i.level} - - - {i.lang} - - -
- - ))} -
- ); + return ; } diff --git a/components/tutorial/search.tsx b/components/tutorial/search.tsx index 96e6f09..b5213ca 100644 --- a/components/tutorial/search.tsx +++ b/components/tutorial/search.tsx @@ -1,41 +1,5 @@ -"use client"; - -import { useSearchParams, useRouter, usePathname } from "next/navigation"; -import { useCallback, useEffect, useState } from "react"; -import { Input } from "@/components/ui/input"; +import { Search } from "../listing-page/search"; export function TutorialSearch() { - const searchParams = useSearchParams(); - const pathname = usePathname(); - const router = useRouter(); - const [search, setSearch] = useState(searchParams.get("search") || undefined); - - const createQueryString = useCallback( - (value?: string) => { - const params = new URLSearchParams(searchParams.toString()); - params.delete("search"); - - if (value) params.set("search", value); - - const res = params.toString(); - - if (res) return "?" + res; - - return ""; - }, - [searchParams] - ); - - useEffect(() => { - router.push(pathname + createQueryString(search)); - }, [search]); - - return ( - setSearch(e.target.value)} - className="w-1/2 mb-8" - /> - ); + return }