Skip to content

feat: ボトルを登録する(US-2) - #33

Merged
hduehgw0 merged 9 commits into
mainfrom
feature/#25/bottle-create
Jul 18, 2026
Merged

feat: ボトルを登録する(US-2)#33
hduehgw0 merged 9 commits into
mainfrom
feature/#25/bottle-create

Conversation

@hduehgw0

@hduehgw0 hduehgw0 commented Jul 18, 2026

Copy link
Copy Markdown
Owner

関連 issue

resolve #25

やったこと

概要

ボトル登録フォームと作成 API を実装した。最初の書き込み処理として、CRUD 共通のパターン(zod スキーマ共有・Route Handler での再検証+認可)をここで確立した。

変更点

  • src/lib/schemas/bottle.ts:共有 zod スキーマ(銘柄名のみ必須・国は固定リスト REGIONS・空文字→未入力の正規化・本数は既定 1)+単体テスト
  • POST /api/bottles:セッション確認 → 共有スキーマで再検証 → userId はセッションから付与して保存(ボディの userId は無視)+結合テスト
  • /bottles/new:登録フォーム(react-hook-form + zodResolver、エラーは role="alert" 表示、送信中は無効化)
  • /bottles:登録導線と反映確認用の最小表示(一覧の本実装は feat: ボトル一覧を表示する(US-3) #26
  • 依存導入:shadcn/ui(radix ベース・field 等)/react-hook-form/zod。vitest.config.ts@ エイリアス解決を追加

受け入れ条件(セルフチェック)

  • 「銘柄名」だけ入力すれば登録できる(他項目はすべて任意)
  • 任意項目:国(固定リスト選択式)/ 地域 / 年数(空欄=NAS 可)/ 樽 / 限定版(フラグ)/ 本数 / メモ
  • 本数は既定値 1、1 以上の整数
  • 銘柄名が未入力なら送信できず、分かるエラーが出る(zod バリデーション)
  • 登録後、一覧に反映され、自分のデータとして保存される
  • 同じ銘柄でも年数・樽・限定版が違うものは別レコードとして登録できる(同一物の重複本数は「本数」で表す)

動作確認

  • pnpm lint が通る
  • pnpm test が通る(17 本:スキーマ単体+Route Handler 結合)
  • pnpm build が通る
  • モバイル幅で表示が崩れない(実機で登録フローを確認済み)

Issue 要件外の対応(あれば)

  • fix: 銘柄名・地域がブラウザに氏名・住所と誤認され住所サジェストが出る不具合を、入力属性の変更(Controller 接続+認識されない name/id)で回避(実機確認で発覚)
  • refactor: 未使用の型 export(BottleInput/BottleData)を削除(利用側はスキーマから型推論)

備考

  • REGIONS の中身は父と確定するまでの暫定(5 大ウイスキー+主要新興産地。→ docs/data-model.md の TODO)
  • DB 書き込みの try/catch は意図的に置いていない(予期せぬ 500 は Next.js 任せでログに残し、フロントは !response.ok で一律表示。エラー形状の統一は書き込みルートが出揃う feat: ボトルを編集する(US-5) #28feat: ボトルを削除する(US-6) #29 で再評価)
  • テストは「zod 自体の保証は再テストせず、自作の振る舞い 1 つにつき代表 1 本」の基準で絞っている

Summary by CodeRabbit

  • New Features

    • Added an authenticated “register bottle” flow with a full form (region, age, cask type, quantity, limited, notes) and automatic navigation after saving.
    • Added shared, reusable UI controls (buttons, inputs, selects, checkbox, labels, form fields, separators, textareas).
    • Introduced refreshed app styling with full design tokens and dark-mode support.
  • Bug Fixes

    • Improved server-side validation and error handling (missing/invalid input returns clear client errors).
    • Ensured bottles are always saved to the signed-in account, ignoring any conflicting client-provided identity.
  • Tests

    • Added API and schema tests covering validation and authentication behaviors.

@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mycellar Ready Ready Preview, Comment Jul 18, 2026 9:43am

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5869b787-9887-4ea6-b849-a291808fd694

📥 Commits

Reviewing files that changed from the base of the PR and between 811c074 and 99adb3e.

📒 Files selected for processing (1)
  • src/lib/schemas/bottle.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/schemas/bottle.ts

📝 Walkthrough

Walkthrough

Adds shared Zod validation, Shadcn-based UI primitives, a bottle registration form, an authenticated POST API with tests, and database-backed bottle listing with registration navigation.

Changes

Bottle registration

Layer / File(s) Summary
Validation and project foundation
components.json, package.json, src/lib/schemas/*, src/lib/utils.ts, vitest.config.ts
Adds bottle field validation and normalization, schema tests, class merging, Vitest aliases, Shadcn configuration, and supporting packages.
Shared UI and theme foundation
src/components/ui/*, src/app/globals.css, src/app/layout.tsx
Adds reusable form controls and field primitives, button variants, theme tokens, dark-mode values, and Geist font wiring.
Bottle registration form
src/app/bottles/new/*
Adds an authenticated registration page and form with client validation, field errors, server errors, JSON submission, and post-submit navigation.
Authenticated bottle creation API
src/app/api/bottles/route.ts, src/app/api/bottles/route.test.ts
Adds authenticated POST handling, shared schema validation, session-owned persistence, and tests for authorization, invalid input, and client-supplied user ID handling.
Bottle list integration
src/app/bottles/page.tsx
Loads the current user’s bottles, orders them newest first, renders empty or populated states, and links to registration.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant BottleForm
  participant BottlesAPI
  participant Database
  User->>BottleForm: submit bottle fields
  BottleForm->>BottlesAPI: POST JSON payload
  BottlesAPI->>Database: create session-owned bottle
  Database-->>BottlesAPI: created bottle
  BottlesAPI-->>BottleForm: 201 response
  BottleForm-->>User: navigate to bottles list
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed It clearly summarizes the main change: adding bottle registration, and it matches the PR scope.
Description check ✅ Passed It includes the linked issue, summary, changes, self-check, verification, out-of-scope notes, and remarks.
Linked Issues check ✅ Passed The PR implements the form, API, shared schema, auth, validation, tests, and dependencies required by #25.
Out of Scope Changes check ✅ Passed The added shadcn setup, UI primitives, and autofill workaround support the requested bottle registration flow.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#25/bottle-create

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/ui/checkbox.tsx`:
- Around line 16-18: Replace the checkbox data-checked variants in
src/components/ui/checkbox.tsx lines 16-18 with data-[state=checked] variants.
Also update the select variants in src/components/ui/select.tsx lines 72-76 from
data-open/data-closed to data-[state=open]/data-[state=closed], and the
separator variants in src/components/ui/separator.tsx lines 19-22 from
data-horizontal/data-vertical to
data-[orientation=horizontal]/data-[orientation=vertical].
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1862f60e-288c-475f-be42-6b0124a0e1b1

📥 Commits

Reviewing files that changed from the base of the PR and between ea357c6 and 811c074.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • components.json
  • package.json
  • src/app/api/bottles/route.test.ts
  • src/app/api/bottles/route.ts
  • src/app/bottles/new/bottle-form.tsx
  • src/app/bottles/new/page.tsx
  • src/app/bottles/page.tsx
  • src/app/globals.css
  • src/app/layout.tsx
  • src/components/ui/button.tsx
  • src/components/ui/checkbox.tsx
  • src/components/ui/field.tsx
  • src/components/ui/input.tsx
  • src/components/ui/label.tsx
  • src/components/ui/select.tsx
  • src/components/ui/separator.tsx
  • src/components/ui/textarea.tsx
  • src/lib/schemas/bottle.test.ts
  • src/lib/schemas/bottle.ts
  • src/lib/utils.ts
  • vitest.config.ts

Comment on lines +16 to +18
className={cn(
"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
className,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## relevant files\n'
git ls-files 'src/components/ui/*' 'tailwind.config.*' 'postcss.config.*' 'src/**/tailwind*' 'src/**/theme*' 'src/**/styles*' | sed 's#^`#-` #'

printf '\n## search for these variants and any custom variant plugins\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'data-(checked|open|closed|horizontal|vertical)|data-\[state=|data-\[orientation=|addVariant|matchVariant|tailwindcss-radix|radix' \
  .

printf '\n## inspect the three target files\n'
sed -n '1,220p' src/components/ui/checkbox.tsx
printf '\n---\n'
sed -n '1,220p' src/components/ui/select.tsx
printf '\n---\n'
sed -n '1,220p' src/components/ui/separator.tsx

printf '\n## inspect tailwind config if present\n'
for f in tailwind.config.js tailwind.config.cjs tailwind.config.ts postcss.config.js postcss.config.cjs postcss.config.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    sed -n '1,260p' "$f"
  fi
done

Repository: hduehgw0/mycellar

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## postcss config\n'
sed -n '1,220p' postcss.config.mjs

printf '\n## search for custom variant definitions\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  '`@custom-variant`|custom-variant|theme\\(|`@theme`|`@plugin`|variant\\(|data-open|data-closed|data-checked|data-horizontal|data-vertical' \
  src . 

printf '\n## inspect field.tsx because it uses related variant syntax\n'
sed -n '1,220p' src/components/ui/field.tsx

Repository: hduehgw0/mycellar

Length of output: 556


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## search for custom Tailwind variant definitions\n'
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  '`@custom-variant`|`@theme`|`@plugin`|matchVariant|addVariant|data-checked|data-open|data-closed|data-horizontal|data-vertical|has-data-checked|group-has-data-horizontal' \
  .

printf '\n## inspect src/components/ui/field.tsx\n'
sed -n '1,220p' src/components/ui/field.tsx

printf '\n## inspect any global CSS files likely to hold Tailwind variants\n'
git ls-files '*.css' '*.scss' '*.mjs' '*.ts' | rg '(^|/)(globals|global|index|app|theme|tailwind|postcss)\.(css|scss|mjs|ts)$'

Repository: hduehgw0/mycellar

Length of output: 8371


🌐 Web query:

Tailwind CSS data-open variant data-checked selector Radix data-state data-orientation official docs

💡 Result:

Tailwind CSS provides built-in, first-class support for styling elements based on data attributes, making it the recommended and modern way to integrate with Radix UI states (such as data-state, data-orientation, and others) [1][2]. Using Data Attribute Selectors You can target any data attribute directly in your HTML using arbitrary variants [1][3]. This approach requires no external plugins: // Basic data attribute selector

...
// Multiple attributes
...
// Group and Peer variants
...
Configuring Shorthands If you prefer cleaner class names or require IntelliSense for common states, you can configure aliases in your tailwind.config.js (or equivalent theme configuration) [1][3]. This maps a short name to a specific data attribute value [3]. // tailwind.config.js module.exports = { theme: { extend: { data: { open: 'state="open"', closed: 'state="closed"', vertical: 'orientation="vertical"', horizontal: 'orientation="horizontal"', checked: 'state="checked"', }, }, }, }; With this configuration, you can use shorter class names:
...
Radix UI Integration Radix UI components automatically expose their state via data attributes (e.g., data-state="open", data-orientation="vertical") [4][5]. Because Tailwind CSS natively supports these attribute selectors, you do not need third-party plugins like tailwindcss-radix to achieve full styling control [2]. Using arbitrary variants or configured data shortcuts is considered the idiomatic approach for current Tailwind development [2]. Top results: [1][3][4][5][2]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## src/app/globals.css"
cat -n src/app/globals.css

echo
echo "## any theme data aliases in the repo"
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'data-(open|closed|checked|horizontal|vertical)|--data-|theme inline|`@custom-variant`' \
  src/app/globals.css src .

Repository: hduehgw0/mycellar

Length of output: 9838


🌐 Web query:

Tailwind CSS data-open data-checked data-horizontal built-in shortcut docs v4

💡 Result:

In Tailwind CSS v4, styling based on data attributes is primarily achieved using the built-in data-* variant [1][2]. There are no pre-built, hard-coded shortcuts for attributes like data-open, data-checked, or data-horizontal that work automatically out-of-the-box without configuration [1][3]. Instead, Tailwind v4 provides a flexible data-* variant that allows you to target any data attribute dynamically [1][2]. How to use data attributes in v4: 1. Direct Usage (Arbitrary Values): You can target any data attribute directly in your HTML using the syntax data-[key=value]: [1][2]. For example, to style an element when data-state="open" is present:

...
2. Boolean Attributes: If your attribute is a simple boolean (e.g., data-checked), you can often target it by just providing the key:
...
3. Custom Shortcuts: If you want to use shorthand like data-open: instead of data-[state=open]:, you can define custom variants in your CSS file using the @custom-variant directive [1][4]. This is the recommended way to create the "shortcuts" you are looking for [1][5]. Example CSS configuration: @import "tailwindcss"; @custom-variant data-open (&[data-state="open"]); @custom-variant data-checked (&[data-ui~="checked"]); @custom-variant data-horizontal (&[data-orientation="horizontal"]); Once defined in your CSS, you can use these shortcuts directly in your HTML classes:
...
This approach replaces the need for the theme.extend.data configuration found in older versions of Tailwind, aligning with the CSS-first configuration philosophy of v4 [6][3].

Citations:


Use arbitrary data variants for these states

  • src/components/ui/checkbox.tsx#L16-L18: replace data-checked:* with data-[state=checked]:*.
  • src/components/ui/select.tsx#L72-L76: replace data-open:* / data-closed:* with data-[state=open]:* / data-[state=closed]:*.
  • src/components/ui/separator.tsx#L19-L22: replace data-horizontal:* / data-vertical:* with data-[orientation=horizontal]:* / data-[orientation=vertical]:*.
📍 Affects 3 files
  • src/components/ui/checkbox.tsx#L16-L18 (this comment)
  • src/components/ui/select.tsx#L72-L76
  • src/components/ui/separator.tsx#L19-L22
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/checkbox.tsx` around lines 16 - 18, Replace the checkbox
data-checked variants in src/components/ui/checkbox.tsx lines 16-18 with
data-[state=checked] variants. Also update the select variants in
src/components/ui/select.tsx lines 72-76 from data-open/data-closed to
data-[state=open]/data-[state=closed], and the separator variants in
src/components/ui/separator.tsx lines 19-22 from data-horizontal/data-vertical
to data-[orientation=horizontal]/data-[orientation=vertical].

@hduehgw0
hduehgw0 requested a review from Copilot July 18, 2026 09:35

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

feat: ボトルを登録する(US-2)

2 participants