Skip to content

[] Add custom 404 page, language fallback banner, and feedback widget to docs site#263

Merged
zoetaka38 merged 4 commits into
mainfrom
feature/019f0d7d8813-019f0d7d8813
Jun 29, 2026
Merged

[] Add custom 404 page, language fallback banner, and feedback widget to docs site#263
zoetaka38 merged 4 commits into
mainfrom
feature/019f0d7d8813-019f0d7d8813

Conversation

@red-codens

@red-codens red-codens Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Type of Change

  • feat — new feature
  • fix — bug fix
  • chore — tooling / maintenance
  • docs — documentation only
  • refactor — code change that neither fixes a bug nor adds a feature
  • test — adding or updating tests
  • ci — CI/CD configuration

Testing

Checklist

  • Tests pass locally
  • Lint clean (task lint)
  • Commit messages follow Conventional Commits

Task Goal

Target Repository

Corevice/open-git
All work for this task belongs in this repository. Do the implementation inside this repository's directory in the workspace; do not search or edit other repositories unless this task explicitly requires a cross-repo change.

Context

The PRD requires: a 404 page with search link, a translation-missing banner for untranslated en pages that falls back to ja content, and an optional feedback widget (Was this helpful? Yes/No). These are self-contained UI additions that do not depend on a backend API — the feedback widget can POST to /api/docs/feedback but must degrade gracefully when that endpoint is absent.

Steps

  1. Create open-git/docs/pages/404.mdx with frontmatter title: ページが見つかりません; body: heading, explanation, link back to /docs homepage, and a search encouragement
  2. Create open-git/docs/components/UntranslatedBanner.tsx: a yellow info banner that reads 'このページはまだ日本語のみ提供されています。' with a dismiss button (useState); shown when locale === 'en' and page has lang: 'ja' frontmatter
  3. Update open-git/docs/theme.config.tsx to add a main wrapper component that conditionally renders UntranslatedBanner based on Nextra's useConfig() frontmatter + current locale
  4. Create open-git/docs/components/FeedbackWidget.tsx: renders two buttons '役に立った' and '役に立たなかった'; on click fetches POST /api/docs/feedback with { path, helpful, version }; shows thank-you message on 2xx; shows silent failure (no error UI) on non-2xx; entire widget is aria-labeled
  5. Update open-git/docs/theme.config.tsx main wrapper to also render FeedbackWidget at bottom of each doc page, reading path from usePathname() and version from useConfig().frontMatter.version ?? 'latest'
  6. Create open-git/docs/__tests__/components/UntranslatedBanner.test.tsx: render with locale='en'; assert banner text is visible; click dismiss button; assert banner disappears
  7. Create open-git/docs/__tests__/components/FeedbackWidget.test.tsx

Changes Made

I'll
inspect
the
existing
docs
setup
and
theme .config .tsx
so
the
new
components
match
current
patterns
.

Creating
the
docs
files
from
scratch
since
the
folder
doesn't
exist
yet
.

Implemented
the
docs
UI
additions
for
the

404
page
,
unt
rans
lated
banner
,
feedback
widget
,
theme
wiring
,
and
tests
.

Created
files

  1. docs/pages/404.mdx — 404 page with title: ページが見つかりません, explanation, link to /docs, and search
    encouragement.

  2. docs/components/UntranslatedBanner.tsx — Yellow info banner with dismiss state; shown when locale === 'en' and pageLang === 'ja '.

  3. docs/components/FeedbackWidget.tsx — Yes/No buttons (役に立った / 役に立たなかった); POSTs to /api/docs/ feedback; thank-you on 2xx; silent failure otherwise; aria-label on the widget.

  4. docs/theme.config.tsx
    DocsMain wrapper uses useConfig(), useRouter(), and usePathname() to render UntranslatedBanner and FeedbackWidget around page
    content.

  5. Tests — Component tests for banner dismiss and feedback submission/failure; Node test for 404 MD
    X frontmatter and /docs link.

[PURPLE-MODIFIED-FILES]
docs/pages/404.mdx
docs/components/UntranslatedBanner
.tsx
docs/components/FeedbackWidget.tsx
docs/theme.config.tsx
docs/tests/components/UntranslatedBanner.test.tsx
docs/tests/components/FeedbackWidget.test.tsx
docs/__
tests
__/
pages
/not
-found
.test
.ts

[/
P
UR
P
LE
-M
OD
IFIED
-F
ILES
]

Task ID: 019f0d7d-8813-773c-947c-6f895c67cd2b
Generated by Purple Codens

@orange-codens orange-codens Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍊 Orange Codens レビュー

全体的に PRD の要件を満たした実装になっています。最も注意すべき問題は「ごフィードバックありがとうございます。」という誤った敬語表現で、実装とテスト両方に存在します。また next/navigation(App Router)と next/router(Pages Router)の API 混在は、Nextra の Pages Router 環境では usePathname() が常に null を返す可能性があり、フィードバックの path が空文字になるリスクがあります。テスト面では非 2xx レスポンスのサイレント失敗ケースと UntranslatedBanner の非表示条件が未検証です。

🔒 セキュリティ: 今回の diff はドキュメント UI のみの追加であり、直接的な認証 bypass・SQL injection・SSRF 等の重大脆弱性は検出されませんでした。主なリスクは FeedbackWidget が未検証の path / version をサーバーに送信する点と、CSRF トークン不在による偽フィードバック送信の懸念です。いずれも現時点では中〜低リスクですが、/api/docs/feedback エンドポイント実装時にレート制限・入力検証・CSRF 対策を確実に組み込むことを強く推奨します。

🟡 medium: 4 / 🔵 low: 5 / ⚪ info: 1

その他の指摘 (10 件)
  • 🟡 [medium] docs/components/FeedbackWidget.tsx:47 — 「ごフィードバック」は誤った敬語表現 (confidence: 0.95, code.typo.honorific_prefix)
  • 🟡 [medium] docs/__tests__/components/FeedbackWidget.test.tsx:35 — テストの期待値も誤った敬語「ごフィードバック」を使用 (confidence: 0.95, code.typo.honorific_prefix_test)
  • 🟡 [medium] docs/components/FeedbackWidget.tsx:41 — 非 2xx レスポンス時にボタンが disabled のまま復帰しない (confidence: 0.65, code.logic.feedback_silent_failure_ux)
  • 🟡 [medium] docs/components/FeedbackWidget.tsx:29 — ユーザー制御の path 値が未検証のまま POST ボディに送信される (confidence: 0.55, sec.sast.injection.path_traversal)
  • 🔵 [low] docs/__tests__/components/FeedbackWidget.test.tsx:40 — 非 2xx レスポンス(fetch 成功・ok=false)のケースがテストされていない (confidence: 0.90, maintainability.test.missing_non2xx_case)
  • 🔵 [low] docs/__tests__/components/UntranslatedBanner.test.tsx:1UntranslatedBanner のテストに「表示されないべきケース」の検証がない (confidence: 0.90, maintainability.test.untranslated_banner_no_show_condition)
  • 🔵 [low] docs/components/UntranslatedBanner.tsx:22role="status" のバナーに dismiss ボタンがあると支援技術で混乱する可能性 (confidence: 0.70, maintainability.a11y.role_status_dismiss)
  • 🔵 [low] docs/components/FeedbackWidget.tsx:28 — フィードバック POST にフルパスと version が含まれ、情報漏洩になり得る (confidence: 0.60, sec.sast.info.verbose_path_exposure)
  • 🔵 [low] docs/components/UntranslatedBanner.tsx:18 — locale / pageLang が文字列比較のみで使われており、型チェック外の値が将来的に XSS につながる可能性 (confidence: 0.45, sec.sast.xss.unsafe_locale)
  • [info] docs/theme.config.tsx:11 — Next.js Pages Router と App Router の API を混在使用 (confidence: 0.80, maintainability.routing.mixed_router_apis)

head: c8ad4a6 | Orange Codens (P1)

@orange-codens orange-codens Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍊 Orange Codens レビュー

全体として PRD の要件を概ね満たした実装ですが、「ごフィードバック」という敬語の誤字が本番コードとテスト両方に存在しており(blocker 相当のユーザー可視バグ)、早期修正が必要です。また usePathname(App Router)と useRouter(Pages Router)の混在は Nextra v2 環境では path が常に空文字になりうる実動作上の問題です。その他は低優先度の改善点です。

🔒 セキュリティ: このPRはフロントエンドのみのUI追加(404ページ・翻訳バナー・フィードバックウィジェット)であり、バックエンドロジックを含まないため重大な脆弱性は見当たりません。主な懸念は FeedbackWidgetpath および version をクライアント側で検証せずPOSTボディに含める点(CWE-20)と、CSRF トークン未付与(CWE-352)です。いずれも /api/docs/feedback エンドポイントの実装次第で影響度が変わるため、サーバー側でも入力を検証・サニタイズする実装を合わせて確認することを推奨します。

🟡 medium: 2 / 🔵 low: 6

その他の指摘 (8 件)
  • 🟡 [medium] docs/__tests__/components/FeedbackWidget.test.tsx:36 — テスト期待値も「ごフィードバック」の誤字を引き継いでいる (confidence: 0.95, code.typo.honorific_prefix_test)
  • 🟡 [medium] docs/components/FeedbackWidget.tsx:38 — fetch 失敗後もボタンが無効化されたままになる可能性がある (confidence: 0.70, code.logic.fetch_failure_no_ui_update)
  • 🔵 [low] docs/__tests__/components/UntranslatedBanner.test.tsx:1 — バナーが表示されないケース(locale が en 以外、pageLang が ja 以外)のテストが欠落 (confidence: 0.88, maintainability.test.missing_case)
  • 🔵 [low] docs/theme.config.tsx:3usePathname(App Router)と useRouter(Pages Router)を混在させている (confidence: 0.82, maintainability.router_mixing)
  • 🔵 [low] docs/components/UntranslatedBanner.tsx:23role="status" のバナーに dismiss ボタンがある組み合わせは role="alert" または role="region" の方が適切 (confidence: 0.75, code.accessibility.role_status_dismiss)
  • 🔵 [low] docs/components/FeedbackWidget.tsx:55 — 全スタイルがインラインで記述されており、テーマとの一貫性・保守性が低い (confidence: 0.65, maintainability.inline_style)
  • 🔵 [low] docs/components/FeedbackWidget.tsx:26 — フィードバック送信 fetch に CSRF 対策トークンが含まれていない (confidence: 0.50, sec.sast.authz.csrf_missing)
  • 🔵 [low] docs/pages/404.mdx:9 — 404 ページのリンク先が絶対パス /docs のみでオープンリダイレクトリスクは低いが、クエリパラメータ由来リダイレクトへの拡張に注意 (confidence: 0.30, sec.sast.xss.open_redirect)

前回 run からの未解決 finding: 2 件

head: ca59c9e | Orange Codens (P1)

@orange-codens orange-codens Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍊 Orange Codens レビュー

全体的に実装は PRD の仕様に沿っており、コンポーネント構造も適切です。ただし ごフィードバック(外来語への誤った「ご」付け) がコンポーネントとテスト両方に存在しており修正が必要です。また docs/theme.config.tsx で App Router 用の usePathnamenext/navigation)と Pages Router 用の useRouternext/router)を混用しており、Nextra 環境によっては pathname が正しく取れないリスクがあります。テストは dismiss フローと silent failure を押さえている一方、バナー非表示条件や 役に立たなかった ボタンのカバレッジが不足しています。

🔒 セキュリティ: 今回の diff はドキュメントサイトへの UI 追加が中心であり、重篤な認証 bypass や SQL injection は存在しません。主なリスクは FeedbackWidget が受け取る path および version の入力値を検証・サニタイズせずにバックエンドへ送信している点(CWE-20)と、POST リクエストに CSRF 対策が施されていない点(CWE-352)です。/api/docs/feedback バックエンドの実装がこのリポジトリの diff には含まれないため、サーバー側の影響度は確定できませんが、フロントエンド側での入力検証を追加することで防御層を設けることを推奨します。

🟡 medium: 3 / 🔵 low: 7

その他の指摘 (10 件)
  • 🟡 [medium] docs/components/FeedbackWidget.tsx:48 — 謝辞メッセージの誤字:「ごフィードバック」→「フィードバック」 (confidence: 0.95, code.typo.japanese_honorific)
  • 🟡 [medium] docs/components/FeedbackWidget.tsx:18 — fetch 失敗時にボタンが押せたままになり、ユーザーが何度も送信を試みられる (confidence: 0.75, code.logic.feedback_silent_failure_ux)
  • 🟡 [medium] docs/components/FeedbackWidget.tsx:28 — ユーザー制御の path 値をサニタイズせずにリクエストボディに含めている (confidence: 0.70, sec.sast.injection.path_traversal)
  • 🔵 [low] docs/__tests__/components/UntranslatedBanner.test.tsx:1UntranslatedBanner テストに「英語以外のロケールでは表示しない」ケースが欠落 (confidence: 0.90, maintainability.test.missing_negative_case_banner)
  • 🔵 [low] docs/__tests__/components/FeedbackWidget.test.tsx:1FeedbackWidget テストに「役に立たなかった」ボタンのケースが欠落 (confidence: 0.90, maintainability.test.feedback_unhelpful_button)
  • 🔵 [low] docs/theme.config.tsx:3usePathname(App Router)と useRouter(Pages Router)を同一コンポーネントで混用 (confidence: 0.80, code.router.mixed_navigation_hooks)
  • 🔵 [low] docs/components/UntranslatedBanner.tsx:22role="status" はライブリージョンであり、バナーには role="alert"role="region" の方が適切 (confidence: 0.70, code.a11y.role_status_banner)
  • 🔵 [low] docs/components/FeedbackWidget.tsx:26 — フィードバック POST に CSRF 対策トークンが含まれていない (confidence: 0.60, sec.sast.authz.missing_csrf)
  • 🔵 [low] .github/workflows/helm.yml:31--generate-name フラグ削除によりリリース名が固定化 (confidence: 0.55, maintainability.helm.generate_name_removed)
  • 🔵 [low] docs/theme.config.tsx:14 — frontMatter から取得した version 値が未検証のまま子コンポーネントに渡される (confidence: 0.55, sec.sast.injection.log_injection)

head: 9695e63 | Orange Codens (P1)

# Conflicts:
#	.github/workflows/helm.yml
#	docs/theme.config.tsx
@zoetaka38
zoetaka38 merged commit bbeedbd into main Jun 29, 2026
11 of 13 checks passed

@orange-codens orange-codens Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍊 Orange Codens レビュー

404・未翻訳バナー・フィードバックウィジェットの PRD 要件は概ね満たされていますが、theme.config.tsxnext/routernext/navigation のフック混在は実行時リスクが高く、修正を推奨します。コンポーネントテストは主要 happy path はあるものの、非 2xx HTTP 応答や negative case のカバレッジが不足しています。

🔒 セキュリティ: 本 PR の diff は静的 404 ページ、翻訳未対応バナー、フィードバックウィジェット、およびテーマ設定の配線のみで、バックエンド API 実装は含まれていません。FeedbackWidgetfetch は固定の相対 URL /api/docs/feedback へ JSON POST するクライアント側処理であり、SSRF・SQL/コマンドインジェクション・認可漏れ・暗号誤用・認証 bypass 等の論理的脆弱性は diff 上確認できませんでした。pathusePathname() 由来で DOM に描画されず、JSON.stringify 経由のため XSS 面も限定的です。

🟠 high: 1 / 🟡 medium: 1 / 🔵 low: 2 / ⚪ info: 1

その他の指摘 (4 件)
  • 🟡 [medium] docs/__tests__/components/FeedbackWidget.test.tsx:41 — HTTP 非 2xx(reject 以外)のサイレント失敗が未テスト (confidence: 0.88, maintainability.test.missing_non2xx)
  • 🔵 [low] docs/__tests__/components/FeedbackWidget.test.tsx:10 — 「役に立たなかった」送信パスが未テスト (confidence: 0.85, maintainability.test.missing_helpful_false)
  • 🔵 [low] docs/__tests__/components/UntranslatedBanner.test.tsx:5 — バナー非表示条件のテストが不足 (confidence: 0.80, maintainability.test.missing_negative_case)
  • [info] docs/pages/404.mdx:9 — 404 ページに検索への導線リンクがない (confidence: 0.55, maintainability.prd.search_link)

head: 86f5b5e | Orange Codens (P1)

Comment thread docs/theme.config.tsx

function DocsMain({ children }: { children: ReactNode }) {
const { frontMatter } = useConfig();
const pathname = usePathname();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [high] Pages Router と App Router のフックを混在させている

locale 取得に next/routeruseRouter を使いながら、パス取得に next/navigationusePathname を使っており、Next.js 標準のルーティングモデルでは同一ツリー内で併用できません。localenext/router から取っている時点で Pages Router 前提と読め、usePathname() は実行時エラーになるか、常に null となり path が空文字になる恐れがあります。

該当箇所:

import { usePathname } from 'next/navigation';
import { useRouter } from 'next/router';
...
  const pathname = usePathname();
  const { locale } = useRouter();

フィードバック POST の path が空になると、分析用途のデータ品質も損なわれます。

@@ -3,2 +3,1 @@
-import { usePathname } from 'next/navigation';
 import { useRouter } from 'next/router';
@@ -12,3 +11,3 @@
   const { frontMatter } = useConfig();
-  const pathname = usePathname();
-  const { locale } = useRouter();
+  const { asPath, locale } = useRouter();
@@ -25,1 +24,1 @@
-      <FeedbackWidget path={pathname ?? ''} version={version} />
+      <FeedbackWidget path={asPath} version={version} />

code.router.mixed_hooks | confidence: 0.82

@zoetaka38
zoetaka38 deleted the feature/019f0d7d8813-019f0d7d8813 branch July 2, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant