From 6aa5bbec820dd2d2de4f66e48872c0c1e0c92347 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Wed, 22 Oct 2025 17:28:52 +0800 Subject: [PATCH 1/4] feat(app-navbar): redirect "feedback" to our Discord community --- components/app-navbar.tsx | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/components/app-navbar.tsx b/components/app-navbar.tsx index 4409f36..742dd11 100644 --- a/components/app-navbar.tsx +++ b/components/app-navbar.tsx @@ -27,7 +27,7 @@ function NavItem({ icon, label, active = false }: NavItemProps) { active && "bg-primary text-primary-foreground", )} > -
{icon}
+
{icon}
{label} ); @@ -45,7 +45,7 @@ function UserMenu() { > {user?.name} - + @@ -86,7 +86,7 @@ export default function AppNavbar({ path }: { path: string }) { `} > {navItems.map((item) => ( - + ( setIsMobileMenuOpen(false)} > @@ -164,12 +164,21 @@ export default function AppNavbar({ path }: { path: string }) { ); } -interface NavItem { +interface BaseNavItem { icon: React.ReactNode; label: string; +} + +interface InternalNavItem extends BaseNavItem { pathPrefix: string; } +interface ExternalNavItem extends BaseNavItem { + externalLink: string; +} + +type NavItem = InternalNavItem | ExternalNavItem; + const navItems: NavItem[] = [ { icon: , @@ -181,21 +190,21 @@ const navItems: NavItem[] = [ label: "挑戰題目", pathPrefix: "/challenges", }, - { - icon: , - label: "經驗分享", - pathPrefix: "/comments", - }, { icon: , label: "補充資料", pathPrefix: "/materials", }, + { + icon: , + label: "意見分享", + externalLink: "https://community.dbplay.app/discord", + }, ]; function getActiveNavItemLabel(path: string): string | null { for (const item of navItems) { - if (path.startsWith(item.pathPrefix)) { + if ("pathPrefix" in item && path.startsWith(item.pathPrefix)) { return item.label; } } From 87a2c37ab4e88b2053e68aeb113e2cf92b864637 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Wed, 22 Oct 2025 17:34:51 +0800 Subject: [PATCH 2/4] feat(not-found): implement 404 page --- app/(app)/comments/page.tsx | 27 --------- app/not-found.tsx | 5 ++ .../not-found-layout/current-page.lazy.tsx | 8 +++ components/not-found-layout/current-page.tsx | 9 +++ components/not-found-layout/index.tsx | 55 +++++++++++++++++++ 5 files changed, 77 insertions(+), 27 deletions(-) delete mode 100644 app/(app)/comments/page.tsx create mode 100644 app/not-found.tsx create mode 100644 components/not-found-layout/current-page.lazy.tsx create mode 100644 components/not-found-layout/current-page.tsx create mode 100644 components/not-found-layout/index.tsx diff --git a/app/(app)/comments/page.tsx b/app/(app)/comments/page.tsx deleted file mode 100644 index bcf9d94..0000000 --- a/app/(app)/comments/page.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import PageHeader from "@/components/page-header"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Button } from "@/components/ui/button"; -import { AlertCircle } from "lucide-react"; -import type { Metadata } from "next"; - -export const metadata: Metadata = { - title: "經驗分享", -}; - -export default function CommentsPage() { - return ( -
- - - - - 功能正在開發 - - 目前尚未上線分享學習經驗的功能。在服務上線之前,歡迎提供您對這個功能的意見! - - - - -
- ); -} diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..cf7cf77 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,5 @@ +import NotFoundLayout from "@/components/not-found-layout"; + +export default async function NotFoundPage() { + return ; +} diff --git a/components/not-found-layout/current-page.lazy.tsx b/components/not-found-layout/current-page.lazy.tsx new file mode 100644 index 0000000..6bb25eb --- /dev/null +++ b/components/not-found-layout/current-page.lazy.tsx @@ -0,0 +1,8 @@ +"use client"; + +import dynamic from "next/dynamic"; +import CurrentPage from "./current-page"; + +export const CurrentPageLazy = dynamic(() => Promise.resolve(CurrentPage), { + ssr: false, +}); diff --git a/components/not-found-layout/current-page.tsx b/components/not-found-layout/current-page.tsx new file mode 100644 index 0000000..fe339c5 --- /dev/null +++ b/components/not-found-layout/current-page.tsx @@ -0,0 +1,9 @@ +/** + * Get the current page URL. + * + * Note that this function is not SSR-friendly. + * You should import it with `current-page.lazy.tsx`, which disables SSR. + */ +export default function CurrentPage() { + return

網址:{window.location.href}

; +} diff --git a/components/not-found-layout/index.tsx b/components/not-found-layout/index.tsx new file mode 100644 index 0000000..9c1cc31 --- /dev/null +++ b/components/not-found-layout/index.tsx @@ -0,0 +1,55 @@ +import { Logo } from "@/components/logo"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { AlertTriangle } from "lucide-react"; +import Link from "next/link"; +import { Suspense } from "react"; +import { CurrentPageLazy } from "./current-page.lazy"; + +export default async function NotFoundLayout() { + return ( +
+ +
+ +
+ 資料庫練功坊 + + + + + 找不到此頁面 + + 此頁面不存在或已經被移除。
請確認網址是否正確,或回到首頁。 +
+
+ + + + + + + + +
+
+ ); +} From 7863faaf7f155b75a7c36b53ce8dd81aee3086a0 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Wed, 22 Oct 2025 17:41:45 +0800 Subject: [PATCH 3/4] chore: upgrade dependencies --- package.json | 4 +- pnpm-lock.yaml | 120 ++++++++++++++++++++++++------------------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index 545aaa1..ba7696e 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "foxact": "^0.2.49", "graphql": "^16.11.0", "lucide-react": "^0.546.0", - "next": "16.0.0-canary.17", + "next": "16.0.0", "next-themes": "^0.4.6", "posthog-js": "1.278.0", "posthog-node": "^5.10.2", @@ -81,7 +81,7 @@ "@typescript-eslint/parser": "^8.46.2", "dprint": "^0.50.2", "eslint": "^9.38.0", - "eslint-config-next": "16.0.0-canary.17", + "eslint-config-next": "16.0.0", "eslint-plugin-better-tailwindcss": "^3.7.10", "tailwindcss": "^4.1.15", "tw-animate-css": "^1.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b1d74e..a450780 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,10 +24,10 @@ importers: version: 4.0.7(graphql-ws@6.0.6(graphql@16.11.0)(ws@8.18.3))(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2) '@apollo/client-integration-nextjs': specifier: ^0.14.0 - version: 0.14.0(@apollo/client@4.0.7(graphql-ws@6.0.6(graphql@16.11.0)(ws@8.18.3))(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2))(@types/react@19.2.2)(graphql@16.11.0)(next@16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2) + version: 0.14.0(@apollo/client@4.0.7(graphql-ws@6.0.6(graphql@16.11.0)(ws@8.18.3))(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2))(@types/react@19.2.2)(graphql@16.11.0)(next@16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2) '@bprogress/next': specifier: ^3.2.12 - version: 3.2.12(next@16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) + version: 3.2.12(next@16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) '@codemirror/lang-sql': specifier: ^6.10.0 version: 6.10.0 @@ -119,8 +119,8 @@ importers: specifier: ^0.546.0 version: 0.546.0(react@19.3.0-canary-71b3a03c-20251021) next: - specifier: 16.0.0-canary.17 - version: 16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) + specifier: 16.0.0 + version: 16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) @@ -210,8 +210,8 @@ importers: specifier: ^9.38.0 version: 9.38.0(jiti@2.6.1) eslint-config-next: - specifier: 16.0.0-canary.17 - version: 16.0.0-canary.17(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 16.0.0 + version: 16.0.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-better-tailwindcss: specifier: ^3.7.10 version: 3.7.10(eslint@9.38.0(jiti@2.6.1))(tailwindcss@4.1.15) @@ -1208,56 +1208,56 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/env@16.0.0-canary.17': - resolution: {integrity: sha512-Yc5OzgY6t/2q2/K8hy6EIQDiJhzS3datftC2b3ik2lMhfG7CukyaBGWLGdgT+IQRuzJc27X1prnU6+5bmpZHcA==} + '@next/env@16.0.0': + resolution: {integrity: sha512-s5j2iFGp38QsG1LWRQaE2iUY3h1jc014/melHFfLdrsMJPqxqDQwWNwyQTcNoUSGZlCVZuM7t7JDMmSyRilsnA==} - '@next/eslint-plugin-next@16.0.0-canary.17': - resolution: {integrity: sha512-xI+l8lrme99o1mlu87XSjU/6kF+R92k7Ogcs/MGMK9OYzYd59X4fjl0Cgdp0NeMaDmxJfR9c9w8ePTAMwGYZQA==} + '@next/eslint-plugin-next@16.0.0': + resolution: {integrity: sha512-IB7RzmmtrPOrpAgEBR1PIQPD0yea5lggh5cq54m51jHjjljU80Ia+czfxJYMlSDl1DPvpzb8S9TalCc0VMo9Hw==} - '@next/swc-darwin-arm64@16.0.0-canary.17': - resolution: {integrity: sha512-hUTH48puJ2ogyRixTXIae2nOzzsGWZL/zPNcGOnocRnpr/0H97b6KF/DX950mhBbiWmxRJR+Wemdd8ky8pL7KA==} + '@next/swc-darwin-arm64@16.0.0': + resolution: {integrity: sha512-/CntqDCnk5w2qIwMiF0a9r6+9qunZzFmU0cBX4T82LOflE72zzH6gnOjCwUXYKOBlQi8OpP/rMj8cBIr18x4TA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.0.0-canary.17': - resolution: {integrity: sha512-maxLu+coYb8PDuCbSYJh6HHXzSQ30LwnEwQ6Ibzmf9jRKvlNXzw0aBhrPfSvwu7BpuSlQN9qKRK6GeVf2onVzw==} + '@next/swc-darwin-x64@16.0.0': + resolution: {integrity: sha512-hB4GZnJGKa8m4efvTGNyii6qs76vTNl+3dKHTCAUaksN6KjYy4iEO3Q5ira405NW2PKb3EcqWiRaL9DrYJfMHg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.0.0-canary.17': - resolution: {integrity: sha512-Qsg7GmKm85fj+pomUgbKe23ILCF/mpQuR//cycYCE8p5ujWeJrbwte6hrBrq3JWt7xbcf4efdW2owlOcgmXTpQ==} + '@next/swc-linux-arm64-gnu@16.0.0': + resolution: {integrity: sha512-E2IHMdE+C1k+nUgndM13/BY/iJY9KGCphCftMh7SXWcaQqExq/pJU/1Hgn8n/tFwSoLoYC/yUghOv97tAsIxqg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@16.0.0-canary.17': - resolution: {integrity: sha512-fZoiYKqyEN22dizdbuGAzC77qB7qWtB9j/0moyxujhxsEssdU8VYQ8Vjao070+Y8s7x/OucJN2nalrs0BPhaeQ==} + '@next/swc-linux-arm64-musl@16.0.0': + resolution: {integrity: sha512-xzgl7c7BVk4+7PDWldU+On2nlwnGgFqJ1siWp3/8S0KBBLCjonB6zwJYPtl4MUY7YZJrzzumdUpUoquu5zk8vg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@16.0.0-canary.17': - resolution: {integrity: sha512-ZKmPaet/BoHMrwvjzbDkaSFi4VC5olqV/etmJfsJ4jzMnG/jF0GZ+cZWFqM3UWksuDxaKDnld0d3MfBrcxjqeQ==} + '@next/swc-linux-x64-gnu@16.0.0': + resolution: {integrity: sha512-sdyOg4cbiCw7YUr0F/7ya42oiVBXLD21EYkSwN+PhE4csJH4MSXUsYyslliiiBwkM+KsuQH/y9wuxVz6s7Nstg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@16.0.0-canary.17': - resolution: {integrity: sha512-nCVg0XnDPhnPofioedixGhOJHhieYsFthMzQa2dflp2cHsIB/ugZ50OIaieLbfUeeHYLGxVXgvzX8IbTHQfwUg==} + '@next/swc-linux-x64-musl@16.0.0': + resolution: {integrity: sha512-IAXv3OBYqVaNOgyd3kxR4L3msuhmSy1bcchPHxDOjypG33i2yDWvGBwFD94OuuTjjTt/7cuIKtAmoOOml6kfbg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@16.0.0-canary.17': - resolution: {integrity: sha512-hgGV6GU9P5KS08p7oVlJoiwjsOB/SBa9aTRiOfQ8pkJ0v/IUjBbEbLHF96c7V38pxPRPvB+mg0vpmZ+nVxq0gA==} + '@next/swc-win32-arm64-msvc@16.0.0': + resolution: {integrity: sha512-bmo3ncIJKUS9PWK1JD9pEVv0yuvp1KPuOsyJTHXTv8KDrEmgV/K+U0C75rl9rhIaODcS7JEb6/7eJhdwXI0XmA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.0.0-canary.17': - resolution: {integrity: sha512-TuJ1E5IUfd15qo/8yqxawuwpl/dN+AJ2RVXmh3dtQaLEBP9jOzf3dE+kfIfVGX2dzg36bSck2RonHPaxk2c0zQ==} + '@next/swc-win32-x64-msvc@16.0.0': + resolution: {integrity: sha512-O1cJbT+lZp+cTjYyZGiDwsOjO3UHHzSqobkPNipdlnnuPb1swfcuY6r3p8dsKU4hAIEO4cO67ZCfVVH/M1ETXA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -3102,8 +3102,8 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - eslint-config-next@16.0.0-canary.17: - resolution: {integrity: sha512-wNdHwK3Nu1yRmdfs1ZFon6RbSrrxx+4+VETdXXP5pbq9tX6zahnXf8ujrgLV1IJmfERIAK4HQIPoCR0qJiGdKA==} + eslint-config-next@16.0.0: + resolution: {integrity: sha512-DWKT1YAO9ex2rK0/EeiPpKU++ghTiG59z6m08/ReLRECOYIaEv17maSCYT8zmFQLwIrY5lhJ+iaJPQdT4sJd4g==} peerDependencies: eslint: '>=9.0.0' typescript: '>=3.3.1' @@ -3374,8 +3374,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.12.0: - resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -4364,8 +4364,8 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@16.0.0-canary.17: - resolution: {integrity: sha512-BuYWU/IAIK7znfCnrGEMaktyELOAvtQwrfYb7QArpyQ9Ph0TAsKOe8fv7pA0aHMKsULOZhlbH4ZZw6okosM0bQ==} + next@16.0.0: + resolution: {integrity: sha512-nYohiNdxGu4OmBzggxy9rczmjIGI+TpR5vbKTsE1HqYwNm1B+YSiugSrFguX6omMOKnDHAmBPY4+8TNJk0Idyg==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -5629,11 +5629,11 @@ snapshots: optionalDependencies: zod: 4.1.12 - '@apollo/client-integration-nextjs@0.14.0(@apollo/client@4.0.7(graphql-ws@6.0.6(graphql@16.11.0)(ws@8.18.3))(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2))(@types/react@19.2.2)(graphql@16.11.0)(next@16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2)': + '@apollo/client-integration-nextjs@0.14.0(@apollo/client@4.0.7(graphql-ws@6.0.6(graphql@16.11.0)(ws@8.18.3))(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2))(@types/react@19.2.2)(graphql@16.11.0)(next@16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2)': dependencies: '@apollo/client': 4.0.7(graphql-ws@6.0.6(graphql@16.11.0)(ws@8.18.3))(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2) '@apollo/client-react-streaming': 0.14.0(@apollo/client@4.0.7(graphql-ws@6.0.6(graphql@16.11.0)(ws@8.18.3))(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2))(@types/react@19.2.2)(graphql@16.11.0)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)(rxjs@7.8.2) - next: 16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) + next: 16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) react: 19.3.0-canary-71b3a03c-20251021 rxjs: 7.8.2 transitivePeerDependencies: @@ -5796,11 +5796,11 @@ snapshots: '@bprogress/core@1.3.4': {} - '@bprogress/next@3.2.12(next@16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)': + '@bprogress/next@3.2.12(next@16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021))(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021)': dependencies: '@bprogress/core': 1.3.4 '@bprogress/react': 1.2.7(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) - next: 16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) + next: 16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021) react: 19.3.0-canary-71b3a03c-20251021 react-dom: 19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021) @@ -6802,34 +6802,34 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@16.0.0-canary.17': {} + '@next/env@16.0.0': {} - '@next/eslint-plugin-next@16.0.0-canary.17': + '@next/eslint-plugin-next@16.0.0': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.0.0-canary.17': + '@next/swc-darwin-arm64@16.0.0': optional: true - '@next/swc-darwin-x64@16.0.0-canary.17': + '@next/swc-darwin-x64@16.0.0': optional: true - '@next/swc-linux-arm64-gnu@16.0.0-canary.17': + '@next/swc-linux-arm64-gnu@16.0.0': optional: true - '@next/swc-linux-arm64-musl@16.0.0-canary.17': + '@next/swc-linux-arm64-musl@16.0.0': optional: true - '@next/swc-linux-x64-gnu@16.0.0-canary.17': + '@next/swc-linux-x64-gnu@16.0.0': optional: true - '@next/swc-linux-x64-musl@16.0.0-canary.17': + '@next/swc-linux-x64-musl@16.0.0': optional: true - '@next/swc-win32-arm64-msvc@16.0.0-canary.17': + '@next/swc-win32-arm64-msvc@16.0.0': optional: true - '@next/swc-win32-x64-msvc@16.0.0-canary.17': + '@next/swc-win32-x64-msvc@16.0.0': optional: true '@nodelib/fs.scandir@2.1.5': @@ -8792,9 +8792,9 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-next@16.0.0-canary.17(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3): + eslint-config-next@16.0.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@next/eslint-plugin-next': 16.0.0-canary.17 + '@next/eslint-plugin-next': 16.0.0 eslint: 9.38.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.38.0(jiti@2.6.1)) @@ -8825,7 +8825,7 @@ snapshots: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 eslint: 9.38.0(jiti@2.6.1) - get-tsconfig: 4.12.0 + get-tsconfig: 4.13.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 @@ -9177,7 +9177,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.12.0: + get-tsconfig@4.13.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -10436,9 +10436,9 @@ snapshots: react: 19.3.0-canary-71b3a03c-20251021 react-dom: 19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021) - next@16.0.0-canary.17(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021): + next@16.0.0(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@19.1.0-rc.3)(react-dom@19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021))(react@19.3.0-canary-71b3a03c-20251021): dependencies: - '@next/env': 16.0.0-canary.17 + '@next/env': 16.0.0 '@swc/helpers': 0.5.15 caniuse-lite: 1.0.30001751 postcss: 8.4.31 @@ -10446,14 +10446,14 @@ snapshots: react-dom: 19.3.0-canary-71b3a03c-20251021(react@19.3.0-canary-71b3a03c-20251021) styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.3.0-canary-71b3a03c-20251021) optionalDependencies: - '@next/swc-darwin-arm64': 16.0.0-canary.17 - '@next/swc-darwin-x64': 16.0.0-canary.17 - '@next/swc-linux-arm64-gnu': 16.0.0-canary.17 - '@next/swc-linux-arm64-musl': 16.0.0-canary.17 - '@next/swc-linux-x64-gnu': 16.0.0-canary.17 - '@next/swc-linux-x64-musl': 16.0.0-canary.17 - '@next/swc-win32-arm64-msvc': 16.0.0-canary.17 - '@next/swc-win32-x64-msvc': 16.0.0-canary.17 + '@next/swc-darwin-arm64': 16.0.0 + '@next/swc-darwin-x64': 16.0.0 + '@next/swc-linux-arm64-gnu': 16.0.0 + '@next/swc-linux-arm64-musl': 16.0.0 + '@next/swc-linux-x64-gnu': 16.0.0 + '@next/swc-linux-x64-musl': 16.0.0 + '@next/swc-win32-arm64-msvc': 16.0.0 + '@next/swc-win32-x64-msvc': 16.0.0 '@opentelemetry/api': 1.9.0 babel-plugin-react-compiler: 19.1.0-rc.3 sharp: 0.34.4 From 2b1cf3025e930f3df190007b5aed15599ea313f2 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Wed, 22 Oct 2025 17:42:07 +0800 Subject: [PATCH 4/4] fix(app-navbar): secure external link --- components/app-navbar.tsx | 51 +++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/components/app-navbar.tsx b/components/app-navbar.tsx index 742dd11..4c2ddd8 100644 --- a/components/app-navbar.tsx +++ b/components/app-navbar.tsx @@ -86,14 +86,11 @@ export default function AppNavbar({ path }: { path: string }) { `} > {navItems.map((item) => ( - - - + ))} @@ -136,18 +133,12 @@ export default function AppNavbar({ path }: { path: string }) {
{/* Mobile Navigation Items */} {navItems.map((item) => ( - setIsMobileMenuOpen(false)} - > - - + /> ))} {/* Mobile User Menu */} @@ -211,3 +202,27 @@ function getActiveNavItemLabel(path: string): string | null { return null; } + +function NavItemLink({ item, active, onClick }: { item: NavItem; active?: boolean; onClick?: () => void }) { + if ("pathPrefix" in item) { + return ( + + + + ); + } + + return ( + + + + ); +}