diff --git a/apps/website/_next-typesafe-url_.d.ts b/apps/website/_next-typesafe-url_.d.ts new file mode 100644 index 0000000..7aee75a --- /dev/null +++ b/apps/website/_next-typesafe-url_.d.ts @@ -0,0 +1,19 @@ +// This file is generated by next-typesafe-url +// Do not edit this file directly. + +// @generated +// prettier-ignore +/* eslint-disable */ + + +declare module "@@@next-typesafe-url" { + import type { InferRoute, StaticRoute } from "next-typesafe-url"; + + interface DynamicRouter { + "/item/[...item]": InferRoute; + } + + interface StaticRouter { + + } +} diff --git a/apps/website/package.json b/apps/website/package.json index 56159e3..a03c2ab 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -3,9 +3,9 @@ "version": "0.0.0", "private": true, "scripts": { - "dev": "next dev --turbo", + "dev": "concurrently 'next dev --turbo' 'next-typesafe-url --watch'", "start": "NODE_ENV=production next start", - "build": "DEBUG=true NODE_ENV=production bunx next build", + "build": "next-typesafe-url && DEBUG=true NODE_ENV=production bunx next build", "postbuild": "next-sitemap", "analyze": "ANALYZE=true npm run build", "typecheck": "tsc --noEmit" @@ -29,6 +29,7 @@ "drizzle-zod": "^0.5.1", "lucide-react": "^0.353.0", "next": "^14.1.3", + "next-typesafe-url": "^4.0.6", "ofetch": "^1.3.3", "pg": "^8.11.3", "postgres": "^3.4.3", @@ -37,7 +38,8 @@ "tailwind-merge": "^2.2.1", "tailwindcss-animate": "^1.0.7", "use-debounce": "^10.0.0", - "vaul": "^0.9.0" + "vaul": "^0.9.0", + "zod": "^3.22.4" }, "devDependencies": { "@next/bundle-analyzer": "^14.1.3", @@ -47,6 +49,7 @@ "@types/react": "^18.2.64", "@types/react-dom": "^18.2.21", "autoprefixer": "^10.4.18", + "concurrently": "^8.2.2", "drizzle-kit": "^0.20.14", "next-sitemap": "^4.2.3", "postcss": "^8.4.35", diff --git a/apps/website/src/app/item/[...item]/page.tsx b/apps/website/src/app/item/[...item]/page.tsx index ced0805..0661b82 100644 --- a/apps/website/src/app/item/[...item]/page.tsx +++ b/apps/website/src/app/item/[...item]/page.tsx @@ -5,15 +5,15 @@ import { and, eq, asc } from 'drizzle-orm'; import { notFound } from 'next/navigation'; import { ActivityIcon } from 'lucide-react'; import dayjs from 'dayjs'; +import Link from 'next/link'; +import { $path } from 'next-typesafe-url'; import { db } from 'db'; import { items, itemsMetadata } from 'db/schema'; import { Card, CardHeader, CardTitle, CardContent } from 'shadcn-ui/card'; import { CurvedlineChart } from 'modules/item-detail/charts'; import { getAuctionHouseIds, seasonalRealmsEU, seasonalRealmsUS } from 'services/realms'; -import { getActiveFaction } from 'services/search-params'; import { Button } from 'shadcn-ui/button'; -import Link from 'next/link'; type Props = i.NextPageProps<{ params: { @@ -88,10 +88,24 @@ const Page = async ({ params, searchParams }: Props) => {
diff --git a/apps/website/src/app/item/[...item]/routeType.ts b/apps/website/src/app/item/[...item]/routeType.ts new file mode 100644 index 0000000..cd8716a --- /dev/null +++ b/apps/website/src/app/item/[...item]/routeType.ts @@ -0,0 +1,10 @@ +import { type DynamicRoute } from 'next-typesafe-url'; +import { z } from 'zod'; + +export const Route = { + routeParams: z.object({ + item: z.array(z.string()), + }), +} satisfies DynamicRoute; + +export type RouteType = typeof Route; diff --git a/apps/website/src/components/common/item-search-input.tsx b/apps/website/src/components/common/item-search-input.tsx index 495ba73..bd66540 100644 --- a/apps/website/src/components/common/item-search-input.tsx +++ b/apps/website/src/components/common/item-search-input.tsx @@ -5,6 +5,7 @@ import { useDebounce } from 'use-debounce'; import Image from 'next/image'; import Link from 'next/link'; import { useParams, useRouter } from 'next/navigation'; +import { $path } from 'next-typesafe-url'; import { ItemParam } from 'src/app/item/[...item]/page'; import { Command, CommandGroup, CommandInput, CommandItem, CommandList } from 'shadcn-ui/command'; @@ -23,6 +24,10 @@ export const ItemSearchInput = () => { const inputRef = React.useRef(null); const isResultsOpen = isFocused && inputValue; + function getItemUrl(slug: string) { + return `/item/${realmSlug}/${region}/${faction}/${slug}`; + } + return ( { key={result.id} asChild onSelect={() => { - router.push(`/${realmSlug}/${region}/${faction}/${result.slug}`); + router.push( + $path({ + route: '/item/[...item]', + routeParams: { item: [realmSlug, region, faction, result.slug] }, + }), + ); + inputRef.current?.blur(); setValue(''); }} > { setValue(''); diff --git a/apps/website/src/components/common/realm-dropdown.tsx b/apps/website/src/components/common/realm-dropdown.tsx index 34c9688..816594a 100644 --- a/apps/website/src/components/common/realm-dropdown.tsx +++ b/apps/website/src/components/common/realm-dropdown.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { useParams, useRouter } from 'next/navigation'; +import { $path } from 'next-typesafe-url'; import { useMediaQuery } from 'hooks/use-media-query'; import { Button } from 'shadcn-ui/button'; @@ -73,7 +74,12 @@ function RealmList(props: { setOpen: (open: boolean) => void }) { startTransition(() => { props.setOpen(false); const [realm, region] = value.split('_'); - router.push(`/${realm}/${region}/${params.item[2]}/${params.item[3]}`); + router.push( + $path({ + route: '/item/[...item]', + routeParams: { item: [realm!, region!, params.item[2], params.item[3]] }, + }), + ); }); }} > diff --git a/apps/website/src/components/common/responsive-combobox.tsx b/apps/website/src/components/common/responsive-combobox.tsx index b893184..8dd6c66 100644 --- a/apps/website/src/components/common/responsive-combobox.tsx +++ b/apps/website/src/components/common/responsive-combobox.tsx @@ -1,6 +1,8 @@ 'use client'; import * as React from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { $path } from 'next-typesafe-url'; import { useMediaQuery } from 'hooks/use-media-query'; import { Button } from 'shadcn-ui/button'; @@ -14,7 +16,7 @@ import { } from 'shadcn-ui/command'; import { Drawer, DrawerContent, DrawerTrigger } from 'shadcn-ui/drawer'; import { Popover, PopoverContent, PopoverTrigger } from 'shadcn-ui/popover'; -import { useRouter } from 'next/navigation'; +import { ItemParam } from 'src/app/item/[...item]/page'; type Props = { options: Option[]; @@ -72,7 +74,7 @@ function StatusList(props: { options: Option[]; }) { const router = useRouter(); - const [, startTransition] = React.useTransition(); + const params = useParams() as { item: ItemParam }; return ( @@ -85,18 +87,16 @@ function StatusList(props: { key={option.value} value={option.value} onSelect={(value) => { - // props.setSelectedRealm( - // props.options.find((priority) => priority.value === value) || null, - // ); + props.setOpen(false); - startTransition(() => { - props.setOpen(false); + const [realm, region] = value.split('_'); - console.log('go'); - - const [realm, region] = value.split('_'); - router.push(`/${realm}/${region}/alliance/grime-encrusted-salvage`); - }); + router.push( + $path({ + route: '/item/[...item]', + routeParams: { item: [realm!, region!, params.item[2], params.item[3]] }, + }), + ); }} > {option.label} diff --git a/bun.lockb b/bun.lockb index 359fdca..b3ac27e 100755 Binary files a/bun.lockb and b/bun.lockb differ