Skip to content

feat: #280 찻잎 사용량 기록 추가#282

Open
FLYLIKEB wants to merge 2 commits into
mainfrom
feature/issue-280-tea-leaf-weight
Open

feat: #280 찻잎 사용량 기록 추가#282
FLYLIKEB wants to merge 2 commits into
mainfrom
feature/issue-280-tea-leaf-weight

Conversation

@FLYLIKEB
Copy link
Copy Markdown
Owner

@FLYLIKEB FLYLIKEB commented Mar 18, 2026

Summary

  • Closes feat: 차록 기록 시 찻잎 사용량(g) 기록 기능 #280
  • Note 엔티티에 teaLeafWeight DECIMAL(5,1) nullable 컬럼 추가
  • TypeORM migration: AddTeaLeafWeightToNotes (notes 테이블만)
  • CreateNoteDto: @IsOptional + @Transform(null 보존) + @IsNumber 검증
  • useNoteForm: teaLeafWeight 상태 + localStorage 기본값 설정
  • NewNote: 찻잎 사용량 입력 필드 (step=0.1, min=0.1, max=999.9, 단위 g)
  • NoteDetail: teaLeafWeight 있을 때만 표시
  • Settings: 기본 찻잎 사용량 설정 UI (localStorage)

Test plan

  • Backend E2E: teaLeafWeight 3건 테스트 추가
  • npm run build
  • cd backend && npm run build
  • code-reviewer 에이전트 검토 완료 (HIGH 이슈 0건)

🤖 Generated with Claude Code

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 차 노트에 찻잎 사용량 기록 기능 추가 (0.1~999.9g, 0.1g 단위)
    • 노트 생성/편집 시 찻잎 사용량 입력 필드 추가
    • 노트 상세 페이지에서 찻잎 사용량 표시
    • 설정에서 기본 찻잎 사용량 설정 추가 (새 노트 생성 시 자동 적용)
  • 테스트

    • 찻잎 사용량 생성, 수정, 조회 관련 종합 테스트 추가

FLYLIKEB and others added 2 commits March 18, 2026 17:56
- Note 엔티티에 teaLeafWeight DECIMAL(5,1) nullable 컬럼 추가
- TypeORM migration: AddTeaLeafWeightToNotes
- CreateNoteDto에 @IsOptional @IsNumber teaLeafWeight 필드 추가
- Note 타입, useNoteForm, NewNote, NoteDetail 프론트엔드 구현
- Settings 페이지에 기본 찻잎 사용량 설정(localStorage) 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- teaLeafWeight DTO에 @Transform으로 null 보존 (enableImplicitConversion 대응)
- migration 파일을 notes 테이블 변경만 포함하도록 최소화

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 18, 2026

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

Project Deployment Actions Updated (UTC)
cha-log Ready Ready Preview, Comment Mar 18, 2026 9:13am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 18, 2026

Walkthrough

차록(차 기록) 시스템에 찻잎 사용량(g) 기록 기능을 추가하는 변경사항입니다. 데이터베이스 스키마에 새로운 컬럼을 추가하고, 백엔드 API 계층과 프론트엔드 폼, 상세 페이지 및 설정 화면에 필드 및 UI를 통합하여 사용자가 찻잎 무게를 입력·조회하고 기본값을 설정할 수 있도록 구성했습니다.

Changes

Cohort / File(s) Summary
데이터베이스 스키마
backend/migrations/1773820392689-AddTeaLeafWeightToNotes.ts, backend/src/notes/entities/note.entity.ts
Notes 테이블에 nullable decimal(5,1) 타입의 teaLeafWeight 컬럼 추가 및 마이그레이션 클래스 구현
백엔드 DTO 및 API 타입
backend/src/notes/dto/create-note.dto.ts, src/lib/api/notes.api.ts
CreateNoteDto에 선택사항 필드 추가 (소수점 1자리, 범위 0.1~999.9), CreateNoteRequest 인터페이스 확장
백엔드 테스트
backend/test/suites/notes-crud.e2e-spec.ts
POST/PATCH 엔드포인트에 대한 3개의 e2e 테스트 추가: 값 포함 생성, null 반환, 업데이트 검증
프론트엔드 상태 관리
src/hooks/useNoteForm.ts, src/types/index.ts
useNoteForm 훅에 teaLeafWeight 상태 추가 (localStorage 기본값 읽기), Note 인터페이스 필드 확장
프론트엔드 UI - 차록 작성
src/pages/NewNote.tsx
찻잎 사용량 입력 섹션 추가 (0.1~999.9g, 0.1 단계)
프론트엔드 UI - 차록 상세
src/pages/NoteDetail.tsx
찻잎 사용량이 있을 때 조건부 렌더링하는 상세 섹션 추가
프론트엔드 UI - 설정
src/pages/Settings.tsx
기본 찻잎 사용량 설정 카드 추가 (localStorage 연동)

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • PR #9: Note 엔티티 및 CreateNoteDto 초기 구현 — 현재 PR에서 동일한 파일들을 확장하여 teaLeafWeight 필드를 추가하므로 직접 관련됨
🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 'feat: #280 찻잎 사용량 기록 추가'는 변경 사항의 주요 내용을 명확하게 반영하며, 찻잎 사용량 기록 기능 추가라는 핵심 목표를 잘 요약하고 있습니다.
Linked Issues check ✅ Passed 모든 linked issue #280의 요구사항이 구현되었습니다: (1) 찻잎 사용량 입력 필드 추가 [#280], (2) 소수점 1자리 검증 [#280], (3) Settings에서 기본값 설정 [#280], (4) NewNote에서 자동 채우기 [#280], (5) NoteDetail에 표시 [#280], (6) 다회 세션 지원 [#280].
Out of Scope Changes check ✅ Passed PR의 모든 변경사항이 #280의 찻잎 사용량 기록 기능 추가 범위 내에 있으며, 범위를 벗어난 변경사항은 발견되지 않았습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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/issue-280-tea-leaf-weight
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/src/notes/dto/create-note.dto.ts`:
- Around line 104-105: The current `@Transform` uses parseFloat which accepts
strings like "7.5g"; update the transform on the CreateNote DTO property (the
`@Transform`(({ value }) => ... ) decorator) to only parse strings that fully
match a strict numeric pattern (e.g. use a regex like /^-?\d+(\.\d+)?$/) and
otherwise return the original value (or null/undefined) so the subsequent
`@IsNumber` validation can reject invalid input; in short, replace
parseFloat(value) with a guarded parse that first tests the entire string
against the strict numeric regex before calling parseFloat.

In `@src/hooks/useNoteForm.ts`:
- Around line 336-337: The payload currently uses "teaLeafWeight: teaLeafWeight
?? undefined", which drops the field from the serialized body when null and
prevents clearing the value on PATCH; update the payload construction in
useNoteForm (the spots where teaLeafWeight is set, including the occurrences at
the two locations flagged) to always include the teaLeafWeight property and send
explicit null when the user cleared the field (e.g., set the property to null if
the local value is null, otherwise the numeric value) so UpdateNoteDto will
receive a null and clear the stored value instead of leaving it unchanged.
- Around line 44-48: In useNoteForm, prevent storing NaN/Infinity by validating
parseFloat results for teaLeafWeight before calling setTeaLeafWeight (and when
initializing from localStorage) and when mapping API responses (the same
parseFloat usage around teaLeafWeight/state mapping); ensure the parsed value is
finite (Number.isFinite), within the allowed range (>= 0.1 && <= 999.9), and
only then set state, otherwise fall back to null (or a safe default) and log or
ignore invalid values.

In `@src/pages/NewNote.tsx`:
- Around line 181-184: The onChange handler for tea leaf weight uses parseFloat
and can set NaN into state; update the handler (the function that calls
setTeaLeafWeight) to parse the input safely (e.g., trim the value, skip empty
string) and then check the parsed number with Number.isFinite or isNaN — if the
parse yields NaN, call setTeaLeafWeight(null), otherwise set the parsed numeric
value; keep the existing empty-string branch intact so intermediate input never
stores NaN in teaLeafWeight.

In `@src/pages/Settings.tsx`:
- Around line 58-61: The state initializer for defaultTeaLeafWeight uses
parseFloat on localStorage value but doesn't guard against NaN; update the
initializer and any places that parse and save the value (including
setDefaultTeaLeafWeightState usage and the code around the other parseFloat at
lines mentioned) to check isNaN(parsed) and treat NaN as null (and avoid writing
"NaN" to localStorage), i.e., parse the stored string with parseFloat, if
Number.isNaN(result) then return null and when persisting only save valid
numeric values (or remove the key) so input-control and autofill logic won't
receive NaN.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0fe4402c-b7bb-4fa0-aacd-bf79d5434b9c

📥 Commits

Reviewing files that changed from the base of the PR and between c223d2b and 31b6e5d.

📒 Files selected for processing (10)
  • backend/migrations/1773820392689-AddTeaLeafWeightToNotes.ts
  • backend/src/notes/dto/create-note.dto.ts
  • backend/src/notes/entities/note.entity.ts
  • backend/test/suites/notes-crud.e2e-spec.ts
  • src/hooks/useNoteForm.ts
  • src/lib/api/notes.api.ts
  • src/pages/NewNote.tsx
  • src/pages/NoteDetail.tsx
  • src/pages/Settings.tsx
  • src/types/index.ts

Comment on lines +104 to +105
@Transform(({ value }) => value === null || value === undefined ? value : (typeof value === 'string' ? parseFloat(value) : value))
@IsNumber({ maxDecimalPlaces: 1 })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In JavaScript, what do parseFloat("7.5g"), parseFloat("1abc"), and Number("7.5g") return, and why?

💡 Result:

parseFloat("7.5g") returns 7.5, parseFloat("1abc") returns 1, and Number("7.5g") returns NaN. parseFloat parses from the start of the string until it encounters an invalid character (not +, -, 0-9, ., e/E), returning the numeric value up to that point. For "7.5g", it parses "7.5" (valid float) and stops at 'g'. For "1abc", it parses "1" and stops at 'a'. Number attempts to convert the entire string to a valid number literal; any invalid characters anywhere result in NaN. "7.5g" and "1abc" are invalid full literals.

Citations:


🏁 Script executed:

#!/bin/bash
# Check if the file exists and read the relevant section
if [ -f "backend/src/notes/dto/create-note.dto.ts" ]; then
  echo "=== File found, reading lines 100-115 ==="
  sed -n '100,115p' "backend/src/notes/dto/create-note.dto.ts" | cat -n
else
  echo "File not found at expected path"
  # Try to locate it
  find . -name "create-note.dto.ts" -type f
fi

Repository: FLYLIKEB/ChaLog

Length of output: 572


parseFloat 변환이 너무 관대해서 잘못된 입력이 통과할 수 있습니다.

Line 105의 parseFloat"7.5g" 같은 값을 7.5로 변환해 Line 106의 @IsNumber 검증을 통과시킬 수 있습니다. 숫자 형식을 엄격하게 검증하도록 변환 로직을 수정해주세요.

🔧 제안 수정
   `@IsOptional`()
-  `@Transform`(({ value }) => value === null || value === undefined ? value : (typeof value === 'string' ? parseFloat(value) : value))
+  `@Transform`(({ value }) => {
+    if (value === null || value === undefined) return value;
+    if (typeof value !== 'string') return value;
+    const trimmed = value.trim();
+    // 숫자 + 소수점 이하 1자리까지만 허용 (예: 5, 5.5, 7.5)
+    if (!/^\d+(\.\d)?$/.test(trimmed)) return value; // IsNumber가 실패하도록 원문 유지
+    return Number(trimmed);
+  })
   `@IsNumber`({ maxDecimalPlaces: 1 })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Transform(({ value }) => value === null || value === undefined ? value : (typeof value === 'string' ? parseFloat(value) : value))
@IsNumber({ maxDecimalPlaces: 1 })
`@IsOptional`()
`@Transform`(({ value }) => {
if (value === null || value === undefined) return value;
if (typeof value !== 'string') return value;
const trimmed = value.trim();
// 숫자 + 소수점 이하 1자리까지만 허용 (예: 5, 5.5, 7.5)
if (!/^\d+(\.\d)?$/.test(trimmed)) return value; // IsNumber가 실패하도록 원문 유지
return Number(trimmed);
})
`@IsNumber`({ maxDecimalPlaces: 1 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/notes/dto/create-note.dto.ts` around lines 104 - 105, The current
`@Transform` uses parseFloat which accepts strings like "7.5g"; update the
transform on the CreateNote DTO property (the `@Transform`(({ value }) => ... )
decorator) to only parse strings that fully match a strict numeric pattern (e.g.
use a regex like /^-?\d+(\.\d+)?$/) and otherwise return the original value (or
null/undefined) so the subsequent `@IsNumber` validation can reject invalid input;
in short, replace parseFloat(value) with a guarded parse that first tests the
entire string against the strict numeric regex before calling parseFloat.

Comment thread src/hooks/useNoteForm.ts
Comment on lines +44 to +48
const [teaLeafWeight, setTeaLeafWeight] = useState<number | null>(() => {
if (mode === 'edit') return null;
const stored = localStorage.getItem('defaultTeaLeafWeight');
return stored ? parseFloat(stored) : null;
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n src/hooks/useNoteForm.ts | head -200

Repository: FLYLIKEB/ChaLog

Length of output: 8457


🏁 Script executed:

grep -n "parseFloat" src/hooks/useNoteForm.ts

Repository: FLYLIKEB/ChaLog

Length of output: 169


🏁 Script executed:

fd -t f "types\.(ts|tsx)$" src/ --exec cat -n {}

Repository: FLYLIKEB/ChaLog

Length of output: 41


🏁 Script executed:

rg "interface Note|type Note" src/types --type ts -A 20

Repository: FLYLIKEB/ChaLog

Length of output: 966


🏁 Script executed:

rg "teaLeafWeight" src/types --type ts -B 2 -A 2

Repository: FLYLIKEB/ChaLog

Length of output: 281


🏁 Script executed:

rg "teaLeafWeight|WEIGHT|weight" src/ --type ts -B 2 -A 2 | grep -v "node_modules"

Repository: FLYLIKEB/ChaLog

Length of output: 22302


🏁 Script executed:

rg "teaLeafWeight" src/pages/NewNote.tsx -B 5 -A 5

Repository: FLYLIKEB/ChaLog

Length of output: 691


🏁 Script executed:

rg "teaLeafWeight" src/lib/api --type ts -B 3 -A 3

Repository: FLYLIKEB/ChaLog

Length of output: 375


🏁 Script executed:

rg "0\.1|999\.9|teaLeafWeight|TEA.*WEIGHT" src/ --type ts -B 2 -A 2

Repository: FLYLIKEB/ChaLog

Length of output: 8817


parseFloat 기반 초기화/매핑은 NaN 및 느슨한 파싱을 유입시킬 수 있습니다.

Line 47, Line 179에서 parseFloat 결과를 검증 없이 상태로 넣고 있어, 비정상 문자열(예: "abc", "Infinity")이 들어오면 NaN이나 Infinity가 저장될 수 있습니다. HTML 입력 제약(min="0.1", max="999.9", step="0.1")은 UI 레벨에서만 작동하며, localStorage와 API 응답 경로에서는 보호되지 않습니다. 상태 설정 전 유효 범위와 유한수 검증이 필요합니다.

🔧 제안 수정
+  const parseTeaLeafWeight = (raw: unknown): number | null => {
+    if (raw == null || raw === '') return null;
+    const n = typeof raw === 'string' ? Number(raw.trim()) : Number(raw);
+    if (!Number.isFinite(n)) return null;
+    if (n < 0.1 || n > 999.9) return null;
+    if (!Number.isInteger(n * 10)) return null; // 소수 첫째 자리까지만 허용
+    return n;
+  };
+
   const [teaLeafWeight, setTeaLeafWeight] = useState<number | null>(() => {
     if (mode === 'edit') return null;
     const stored = localStorage.getItem('defaultTeaLeafWeight');
-    return stored ? parseFloat(stored) : null;
+    return parseTeaLeafWeight(stored);
   });
...
         if (normalizedNote.teaLeafWeight != null) {
-          setTeaLeafWeight(
-            typeof normalizedNote.teaLeafWeight === 'string'
-              ? parseFloat(normalizedNote.teaLeafWeight)
-              : normalizedNote.teaLeafWeight,
-          );
+          setTeaLeafWeight(parseTeaLeafWeight(normalizedNote.teaLeafWeight));
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useNoteForm.ts` around lines 44 - 48, In useNoteForm, prevent
storing NaN/Infinity by validating parseFloat results for teaLeafWeight before
calling setTeaLeafWeight (and when initializing from localStorage) and when
mapping API responses (the same parseFloat usage around teaLeafWeight/state
mapping); ensure the parsed value is finite (Number.isFinite), within the
allowed range (>= 0.1 && <= 999.9), and only then set state, otherwise fall back
to null (or a safe default) and log or ignore invalid values.

Comment thread src/hooks/useNoteForm.ts
Comment on lines +336 to 337
teaLeafWeight: teaLeafWeight ?? undefined,
isPublic,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e

# 1) Update DTO가 PartialType인지 확인 (누락 필드 미갱신 근거)
sed -n '1,80p' backend/src/notes/dto/update-note.dto.ts

# 2) useNoteForm 저장 payload에서 null -> undefined 변환 사용 위치 확인
rg -n -C2 'teaLeafWeight:\s*teaLeafWeight\s*\?\?\s*undefined' src/hooks/useNoteForm.ts

Repository: FLYLIKEB/ChaLog

Length of output: 635


🏁 Script executed:

# 1. teaLeafWeight 상태 초기화 및 타입 확인
rg -n "teaLeafWeight" src/hooks/useNoteForm.ts | head -20

# 2. 페이로드가 어떻게 구성되는지 확인 (전체 save 함수 구조)
sed -n '320,380p' src/hooks/useNoteForm.ts

# 3. 페이로드가 API로 전송될 때 undefined 처리 방식 확인
sed -n '380,420p' src/hooks/useNoteForm.ts

Repository: FLYLIKEB/ChaLog

Length of output: 3476


🏁 Script executed:

# 1. notesApi 구현 위치 찾기
find src -name "*api*" -o -name "*notes*" | grep -E "\.(ts|tsx)$"

# 2. notesApi.create/update 구현 확인
rg -n "notesApi\s*=" src/hooks/useNoteForm.ts

# 3. API 클라이언트 구현
rg -n "export.*notesApi" src -A 5

Repository: FLYLIKEB/ChaLog

Length of output: 1372


🏁 Script executed:

# notes.api.ts에서 create/update 메서드 확인
cat src/lib/api/notes.api.ts | head -100

Repository: FLYLIKEB/ChaLog

Length of output: 3785


🏁 Script executed:

# apiClient 구현 확인
cat src/lib/api/client.ts | head -150

Repository: FLYLIKEB/ChaLog

Length of output: 4610


🏁 Script executed:

# apiClient의 post/patch 메서드 구현 확인
tail -n +150 src/lib/api/client.ts | head -200

Repository: FLYLIKEB/ChaLog

Length of output: 5511


🏁 Script executed:

# POST/PATCH 메서드 구현 찾기
rg -n "^\s*post:|^\s*patch:" src/lib/api/client.ts -A 5

Repository: FLYLIKEB/ChaLog

Length of output: 41


🏁 Script executed:

# apiClient 객체 정의 찾기
rg -n "export.*apiClient" src/lib/api/client.ts -A 30

Repository: FLYLIKEB/ChaLog

Length of output: 117


🏁 Script executed:

# ApiClient 클래스 정의 찾기
rg -n "class ApiClient" src/lib/api/client.ts -A 50

Repository: FLYLIKEB/ChaLog

Length of output: 2154


🏁 Script executed:

# ApiClient 클래스 내 post/patch 메서드 찾기
rg -n "^\s*(post|patch)\s*<" src/lib/api/client.ts -A 8

Repository: FLYLIKEB/ChaLog

Length of output: 41


🏁 Script executed:

# post와 patch 메서드 찾기 (더 간단한 패턴)
sed -n '384,900p' src/lib/api/client.ts | grep -n -A 10 "post\|patch"

Repository: FLYLIKEB/ChaLog

Length of output: 1027


수정(PATCH) 시 teaLeafWeight 필드를 삭제할 수 없습니다.

Line 336과 362에서 teaLeafWeight ?? undefined를 사용하면, teaLeafWeight가 null일 때 undefined로 변환됩니다. JavaScript의 JSON.stringify()는 undefined 속성을 직렬화에서 제외하므로 해당 필드가 요청 바디에 포함되지 않습니다. NestJS의 PartialType을 사용하는 UpdateNoteDto에서는 요청에 없는 필드를 갱신하지 않으므로 기존 값이 유지됩니다. 따라서 사용자가 한 번 설정한 값을 이후 수정 시 지울 수 없습니다.

제안 수정
-          teaLeafWeight: teaLeafWeight ?? undefined,
+          teaLeafWeight,

(Line 362도 동일하게 수정 필요)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
teaLeafWeight: teaLeafWeight ?? undefined,
isPublic,
teaLeafWeight,
isPublic,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useNoteForm.ts` around lines 336 - 337, The payload currently uses
"teaLeafWeight: teaLeafWeight ?? undefined", which drops the field from the
serialized body when null and prevents clearing the value on PATCH; update the
payload construction in useNoteForm (the spots where teaLeafWeight is set,
including the occurrences at the two locations flagged) to always include the
teaLeafWeight property and send explicit null when the user cleared the field
(e.g., set the property to null if the local value is null, otherwise the
numeric value) so UpdateNoteDto will receive a null and clear the stored value
instead of leaving it unchanged.

Comment thread src/pages/NewNote.tsx
Comment on lines +181 to +184
onChange={(e) => {
const val = e.target.value;
setTeaLeafWeight(val === '' ? null : parseFloat(val));
}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

찻잎 사용량 입력에서 NaN이 상태로 들어갈 수 있습니다.

Line 183의 parseFloat는 입력 중간 상태에서 NaN을 만들 수 있어, 저장 시 잘못된 payload가 전송될 수 있습니다.

🔧 제안 수정안
-                    onChange={(e) => {
-                      const val = e.target.value;
-                      setTeaLeafWeight(val === '' ? null : parseFloat(val));
-                    }}
+                    onChange={(e) => {
+                      const raw = e.currentTarget.value;
+                      const next = e.currentTarget.valueAsNumber;
+                      if (raw === '' || Number.isNaN(next)) {
+                        setTeaLeafWeight(null);
+                        return;
+                      }
+                      setTeaLeafWeight(Math.round(next * 10) / 10);
+                    }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onChange={(e) => {
const val = e.target.value;
setTeaLeafWeight(val === '' ? null : parseFloat(val));
}}
onChange={(e) => {
const raw = e.currentTarget.value;
const next = e.currentTarget.valueAsNumber;
if (raw === '' || Number.isNaN(next)) {
setTeaLeafWeight(null);
return;
}
setTeaLeafWeight(Math.round(next * 10) / 10);
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/NewNote.tsx` around lines 181 - 184, The onChange handler for tea
leaf weight uses parseFloat and can set NaN into state; update the handler (the
function that calls setTeaLeafWeight) to parse the input safely (e.g., trim the
value, skip empty string) and then check the parsed number with Number.isFinite
or isNaN — if the parse yields NaN, call setTeaLeafWeight(null), otherwise set
the parsed numeric value; keep the existing empty-string branch intact so
intermediate input never stores NaN in teaLeafWeight.

Comment thread src/pages/Settings.tsx
Comment on lines +58 to +61
const [defaultTeaLeafWeight, setDefaultTeaLeafWeightState] = useState<number | null>(() => {
const stored = localStorage.getItem('defaultTeaLeafWeight');
return stored ? parseFloat(stored) : null;
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

NaN 값이 기본값 상태와 localStorage에 저장될 수 있습니다.

Line 60/Line 596의 parseFloat 결과가 NaN일 때도 그대로 저장되어, 입력값 제어와 기본값 자동 채움이 깨질 수 있습니다.

🔧 제안 수정안
-  const [defaultTeaLeafWeight, setDefaultTeaLeafWeightState] = useState<number | null>(() => {
-    const stored = localStorage.getItem('defaultTeaLeafWeight');
-    return stored ? parseFloat(stored) : null;
-  });
+  const parseTeaLeafWeight = (raw: string | null): number | null => {
+    if (raw == null || raw.trim() === '') return null;
+    const n = Number(raw);
+    if (!Number.isFinite(n)) return null;
+    if (n < 0.1 || n > 999.9) return null;
+    return Math.round(n * 10) / 10;
+  };
+
+  const [defaultTeaLeafWeight, setDefaultTeaLeafWeightState] = useState<number | null>(() =>
+    parseTeaLeafWeight(localStorage.getItem('defaultTeaLeafWeight')),
+  );
@@
-                onChange={(e) => {
-                  const val = e.target.value;
-                  const parsed = val === '' ? null : parseFloat(val);
-                  setDefaultTeaLeafWeightState(parsed);
-                  if (parsed === null) {
+                onChange={(e) => {
+                  const raw = e.currentTarget.value;
+                  const parsed = raw === '' ? null : parseTeaLeafWeight(raw);
+                  setDefaultTeaLeafWeightState(parsed);
+                  if (parsed == null) {
                     localStorage.removeItem('defaultTeaLeafWeight');
                   } else {
                     localStorage.setItem('defaultTeaLeafWeight', String(parsed));
                   }
                 }}

Also applies to: 594-602

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Settings.tsx` around lines 58 - 61, The state initializer for
defaultTeaLeafWeight uses parseFloat on localStorage value but doesn't guard
against NaN; update the initializer and any places that parse and save the value
(including setDefaultTeaLeafWeightState usage and the code around the other
parseFloat at lines mentioned) to check isNaN(parsed) and treat NaN as null (and
avoid writing "NaN" to localStorage), i.e., parse the stored string with
parseFloat, if Number.isNaN(result) then return null and when persisting only
save valid numeric values (or remove the key) so input-control and autofill
logic won't receive NaN.

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: 차록 기록 시 찻잎 사용량(g) 기록 기능

1 participant