Feature/policy api#60
Conversation
📝 WalkthroughWalkthrough데이터 포맷팅 유틸리티를 도입하여 바이트-기가바이트 변환 로직을 통합하고, 알람 메시지 상수 키에 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
src/components/Onboarding/OnboardingModal.tsx (1)
320-338:⚠️ Potential issue | 🟠 Major하단 설명 문구의 대비가 부족합니다.
배경을 밝게 바꾼 상태에서 설명 문구와 커서를 밝은 계열로 두면 가독성이 크게 떨어집니다. 온보딩 핵심 안내 문구라서 더 어두운 전경색으로 맞추는 편이 안전합니다.
수정 예시
- <p className="text-white text-[13px] text-center leading-relaxed min-h-[38px] mt-1"> + <p className="text-sky-900 text-[13px] text-center leading-relaxed min-h-[38px] mt-1"> {typedDesc} {titleDone && typedDesc.length < descText.length && ( - <Cursor height="h-[13px]" color="bg-sky-300" /> + <Cursor height="h-[13px]" color="bg-sky-700" /> )} </p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/Onboarding/OnboardingModal.tsx` around lines 320 - 338, The description paragraph and its typing Cursor are using light foreground colors against a light gradient, reducing contrast; update the <p> that renders {typedDesc} (currently className="text-white ...") to a darker, high-contrast color (e.g. text-slate-800 or a hex like `#0f172a`) and change the Cursor color prop used with the description (currently color="bg-sky-300") to a darker color class (e.g. "bg-slate-800"); also check the title Cursor (Cursor with color="bg-white") and switch it to a darker cursor color when the slide background is the light gradient so both typedTitle and typedDesc have sufficient contrast. Ensure you modify the <p> rendering typedDesc and the two Cursor components in OnboardingModal to implement these color changes.src/page/Log/LogPage.tsx (1)
31-34:⚠️ Potential issue | 🟡 Minor로그 항목 금액도
formatDataLabel로 통일해 주세요.요약 카드만 새 formatter를 쓰고 있어서, 여기서는 1GB 미만 값이 여전히
0.xGB로 보입니다. 이번 PR의 표시 정책과 어긋납니다.💡 제안된 수정
function formatAmount(entry: HistoryEntry): string { - const gb = formatData(Math.abs(entry.amount)); - return entry.eventType === "USAGE" ? `- ${gb}GB` : `+ ${gb}GB`; + const amount = formatDataLabel(Math.abs(entry.amount)); + return entry.eventType === "USAGE" ? `- ${amount}` : `+ ${amount}`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/Log/LogPage.tsx` around lines 31 - 34, The formatAmount function currently uses formatData and appends "GB", causing sub-1GB values to display as "0.xGB"; change it to call formatDataLabel(Math.abs(entry.amount)) instead and use that returned label (which already includes proper unit formatting), preserving the existing +/- prefix logic in formatAmount for HistoryEntry eventType comparisons.src/components/common/Slider.tsx (1)
9-13:⚠️ Potential issue | 🟠 Major
gbprop 계약과 formatter 입력 단위가 맞지 않습니다.
SliderProps는 여전히 GB 값을 받는다고 설명하고 있는데,formatDataLabel은 bytes를 받습니다. 호출부를 그대로 두면 툴팁 값이 거의 항상 잘못 표시됩니다. GB 계약을 유지할 거면 여기서 bytes로 변환하고, bytes가 맞다면 prop 이름/주석도 같이 정리해 주세요.💡 `gb` 계약을 유지하는 경우의 수정 예시
- {formatDataLabel(item.gb)} + {formatDataLabel(item.gb * 1e9)}Also applies to: 161-170
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/Slider.tsx` around lines 9 - 13, The gb prop in SliderProps is documented/used as gigabytes but formatDataLabel expects bytes, causing incorrect tooltip values; update the code that builds the data passed to Slider (or inside Slider's render) to convert gb to bytes before calling formatDataLabel (multiply gb by 1024**3) if you intend to keep the gb contract, or alternatively rename the prop to bytes (and update its comment) and pass bytes directly to formatDataLabel; ensure you update all usages of gb (including the data array and any code paths around formatDataLabel and isCurrent) so the units are consistent.src/api/services/sharedPoolService.ts (1)
32-37:⚠️ Potential issue | 🟠 Major
getMainRemainingAmount와getMainRemainingAmountByLine간의 단위 불일치.두 메서드 모두 동일한 엔드포인트(
/shared-pools/main/remaining-amount)를 호출하고SharedPoolMainData타입을 반환하지만, 이제 서로 다른 단위를 반환합니다:
getMainRemainingAmount: 바이트(raw bytes) 반환getMainRemainingAmountByLine: GB로 변환하여 반환동일한 타입이 서로 다른 단위를 나타내면 혼란을 야기하고, 소비자가 일관된 단위를 기대할 경우 버그로 이어질 수 있습니다. UI 레이어에서 바이트 포맷팅으로 전환하는 것이라면,
getMainRemainingAmountByLine에서도bytesToGb변환을 제거하여 일관성을 유지해야 합니다.🔧 일관성을 위해 `getMainRemainingAmountByLine`에서도 변환 제거 제안
// [어드민] lineId로 공유풀 메인 데이터 조회 getMainRemainingAmountByLine: async (lineId: number) => { const response = await apiClient.get<SharedPoolMainData>( "/shared-pools/main/remaining-amount", { params: { lineId } }, ); - const data = response.data; - - // Bytes를 GB로 변환 - return { - sharedPoolBaseData: bytesToGb(data.sharedPoolBaseData), - sharedPoolAdditionalData: bytesToGb(data.sharedPoolAdditionalData), - sharedPoolRemainingData: bytesToGb(data.sharedPoolRemainingData), - sharedPoolTotalData: bytesToGb(data.sharedPoolTotalData), - }; + return response.data; // 변환 없이 바이트 그대로 },Also applies to: 84-98
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/api/services/sharedPoolService.ts` around lines 32 - 37, The two methods getMainRemainingAmount and getMainRemainingAmountByLine call the same endpoint but return different units (raw bytes vs GB); update getMainRemainingAmountByLine to stop converting with bytesToGb so both methods return the same raw-byte values matching SharedPoolMainData (or, if you prefer conversion, apply bytesToGb in both), ensuring the unit is consistent across getMainRemainingAmount and getMainRemainingAmountByLine and update any related comments/types accordingly.
🧹 Nitpick comments (3)
src/page/Policy/Policy.tsx (1)
264-270: 필터 범위에 대한 선택적 개선 사항.현재 로직은 전체
familyMembers배열을 기준으로 중복 여부를 확인합니다. 현재 사용자가 제외된 상태에서도 전체 배열을 검사하므로, 표시 목록에는 동일 이름이 하나뿐이어도 전화번호 뒷자리가 표시될 수 있습니다.예: 현재 사용자 "김철수"가 제외되고, 다른 회원 "김철수" 한 명만 표시되는 경우에도 뒷자리가 표시됨.
기능적으로 문제가 되지는 않지만, 더 정확한 표시를 원한다면 필터링된 목록을 기준으로 검사할 수 있습니다.
♻️ 선택적 개선 제안
// 컴포넌트 상단에서 필터링된 목록을 미리 계산 const displayedMembers = familyMembers.filter( (member) => member.lineId !== userData?.lineId ); // JSX에서 사용 {displayedMembers.filter( (m) => m.userName === member.userName, ).length > 1 && ( <span className="text-xs font-normal text-gray-400 ml-1"> ({member.phone.slice(-4)}) </span> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/Policy/Policy.tsx` around lines 264 - 270, The current duplicate-name check uses the full familyMembers array which can include the current user; compute a filtered list of visible members (e.g., displayedMembers = familyMembers.filter(m => m.lineId !== userData?.lineId)) and use that list when checking duplicates (replace familyMembers.filter(...).length > 1 with displayedMembers.filter(m => m.userName === member.userName).length > 1) so the phone tail shown by member.phone.slice(-4) is based only on displayed members.src/page/SharedData/SharedData.tsx (1)
30-32: 디버그 로그는 머지 전에 제거하는 편이 좋겠습니다.
myData가 갱신될 때마다 콘솔이 찍혀서 refetch 시 로그가 계속 쌓이고, 사용자별 공유 데이터 상태도 브라우저 콘솔에 남습니다. 의도된 관측용 코드가 아니라면 빼는 게 안전합니다.정리 예시
- useEffect(() => { - console.log("myData:", myData); - }, [myData]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/page/SharedData/SharedData.tsx` around lines 30 - 32, Remove the debug console logging in the useEffect that watches myData in SharedData.tsx: locate the useEffect referencing myData and delete the console.log("myData:", myData) (or guard it behind a development-only flag if you need runtime debugging), so browser consoles no longer receive user data on every refetch; keep the effect only if it performs necessary side-effects beyond logging.src/api/services/sharedPoolService.ts (1)
40-58: 서비스 전체의 변환 전략 일관성 검토 권장.현재 서비스 내 메서드들이 혼재된 전략을 사용합니다:
- 바이트 반환:
getMainRemainingAmount,getMySharedPool- GB 변환:
getSharedPools,getSharedPoolsByFamilyId,getMainRemainingAmountByLinePR 목표가 UI 레이어에서
formatDataLabel을 통한 포맷팅으로 전환하는 것이라면, 모든 메서드에서 일관되게 바이트를 반환하거나, 명확한 네이밍 컨벤션(예:getRaw...,getFormatted...)을 사용하는 것이 좋습니다.Also applies to: 61-81
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/api/services/sharedPoolService.ts` around lines 40 - 58, The current service mixes return units: getMainRemainingAmount and getMySharedPool return raw bytes while getSharedPools, getSharedPoolsByFamilyId, and getMainRemainingAmountByLine convert bytes to GB; to make behavior consistent with the UI-driven formatting approach, change getSharedPools (and similarly getSharedPoolsByFamilyId and getMainRemainingAmountByLine) to return raw byte values (remove bytesToGb conversions) so all methods return bytes, or alternatively rename these methods to explicitly indicate GB output (e.g., getSharedPoolsInGb) if you prefer keeping GB conversion—update only the service methods (getSharedPools, getSharedPoolsByFamilyId, getMainRemainingAmountByLine) and ensure their return shapes match the existing byte-based methods (getMainRemainingAmount, getMySharedPool).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/Onboarding/OnboardingModal.tsx`:
- Around line 6-23: The imports for screenshot2 through screenshot8 all point to
screenshot1.png, causing all onboarding slides to show the same image; update
the import statements (screenshot2, screenshot3, ..., screenshot8) to import
their respective asset files (e.g., screenshot2.png, screenshot3.png, etc.) and
ensure the SCREENSHOT_PATHS mapping still references those variables (keys 2–9
-> screenshot1..screenshot8) so each slide renders its correct image.
In `@src/constants/alarmMessages.ts`:
- Around line 23-42: ALARM_MESSAGE_MAP uses keys with a POLICY_ prefix (e.g.,
POLICY_UPDATE_SHAREDATA_LIMIT, POLICY_CREATE_IMMEDIATE_BLOCK) that do not match
the unprefixed keys produced/used by TYPE_TO_ALARM_CODE and read by
getAlarmMessage (which does ALARM_MESSAGE_MAP[value.type]); update
ALARM_MESSAGE_MAP to use the same unprefixed keys (e.g., UPDATE_SHAREDATA_LIMIT,
CREATE_IMMEDIATE_BLOCK, DELETE_DAYDATA_LIMIT, UPDATE_REPEAT_BLOCK,
UPDATE_IMMEDIATE_BLOCK, CREATE_REPEAT_BLOCK, DELETE_REPEAT_BLOCK, etc.) or
alternatively add duplicate unprefixed entries that map to the same messages,
and make the naming consistent for UPDATE/CREATE/DELETE of
repeat/immediate/data-limit entries so getAlarmMessage(value.type) finds the
correct message.
In `@src/page/Log/LogPage.tsx`:
- Around line 162-165: The total data label is rendered with an extra "GB"
because formatDataLabel(total) already returns a unit-suffixed string; update
the Render in LogPage (around formatDataLabel(remaining) /
formatDataLabel(total)) to stop appending the literal "GB" — either remove the
trailing "GB" after formatDataLabel(total) or call a variant of formatDataLabel
that returns a unit-less string if available (reference symbols:
formatDataLabel, remaining, total, LogPage).
In `@src/page/SharedData/components/DataTransferCard.tsx`:
- Around line 165-181: The limit value (limitData → limitDisplay) is rendered
without units while contributedGB includes units, causing inconsistent UI;
update the DataTransferCard rendering so limitDisplay includes the unit string
(e.g., "GB" or "MB") before passing to DataStatItem or set showUnit=true and
pass a formatted string; specifically modify how limitData is converted to
limitDisplay in DataTransferCard (where limitDisplay is computed) so the value
passed into the DataStatItem for label "한도 데이터" matches the format used for
contributedGB.
In `@src/utils/dataFormat.ts`:
- Around line 20-24: The function formatDataLabel currently rounds the GB value
before checking the 1GB threshold which can cause values just under 1GB to be
labeled "1GB"; change formatDataLabel to first compute the exact GB value (e.g.,
gbExact = bytes / 1e9) and use gbExact >= 1 for the branch decision, and only
then format/round the displayed GB value (e.g., round to two decimals) when
returning `${...}GB`; otherwise fall back to MB formatting (keep existing mb
rounding logic). Ensure you update the logic in the formatDataLabel function
accordingly.
---
Outside diff comments:
In `@src/api/services/sharedPoolService.ts`:
- Around line 32-37: The two methods getMainRemainingAmount and
getMainRemainingAmountByLine call the same endpoint but return different units
(raw bytes vs GB); update getMainRemainingAmountByLine to stop converting with
bytesToGb so both methods return the same raw-byte values matching
SharedPoolMainData (or, if you prefer conversion, apply bytesToGb in both),
ensuring the unit is consistent across getMainRemainingAmount and
getMainRemainingAmountByLine and update any related comments/types accordingly.
In `@src/components/common/Slider.tsx`:
- Around line 9-13: The gb prop in SliderProps is documented/used as gigabytes
but formatDataLabel expects bytes, causing incorrect tooltip values; update the
code that builds the data passed to Slider (or inside Slider's render) to
convert gb to bytes before calling formatDataLabel (multiply gb by 1024**3) if
you intend to keep the gb contract, or alternatively rename the prop to bytes
(and update its comment) and pass bytes directly to formatDataLabel; ensure you
update all usages of gb (including the data array and any code paths around
formatDataLabel and isCurrent) so the units are consistent.
In `@src/components/Onboarding/OnboardingModal.tsx`:
- Around line 320-338: The description paragraph and its typing Cursor are using
light foreground colors against a light gradient, reducing contrast; update the
<p> that renders {typedDesc} (currently className="text-white ...") to a darker,
high-contrast color (e.g. text-slate-800 or a hex like `#0f172a`) and change the
Cursor color prop used with the description (currently color="bg-sky-300") to a
darker color class (e.g. "bg-slate-800"); also check the title Cursor (Cursor
with color="bg-white") and switch it to a darker cursor color when the slide
background is the light gradient so both typedTitle and typedDesc have
sufficient contrast. Ensure you modify the <p> rendering typedDesc and the two
Cursor components in OnboardingModal to implement these color changes.
In `@src/page/Log/LogPage.tsx`:
- Around line 31-34: The formatAmount function currently uses formatData and
appends "GB", causing sub-1GB values to display as "0.xGB"; change it to call
formatDataLabel(Math.abs(entry.amount)) instead and use that returned label
(which already includes proper unit formatting), preserving the existing +/-
prefix logic in formatAmount for HistoryEntry eventType comparisons.
---
Nitpick comments:
In `@src/api/services/sharedPoolService.ts`:
- Around line 40-58: The current service mixes return units:
getMainRemainingAmount and getMySharedPool return raw bytes while
getSharedPools, getSharedPoolsByFamilyId, and getMainRemainingAmountByLine
convert bytes to GB; to make behavior consistent with the UI-driven formatting
approach, change getSharedPools (and similarly getSharedPoolsByFamilyId and
getMainRemainingAmountByLine) to return raw byte values (remove bytesToGb
conversions) so all methods return bytes, or alternatively rename these methods
to explicitly indicate GB output (e.g., getSharedPoolsInGb) if you prefer
keeping GB conversion—update only the service methods (getSharedPools,
getSharedPoolsByFamilyId, getMainRemainingAmountByLine) and ensure their return
shapes match the existing byte-based methods (getMainRemainingAmount,
getMySharedPool).
In `@src/page/Policy/Policy.tsx`:
- Around line 264-270: The current duplicate-name check uses the full
familyMembers array which can include the current user; compute a filtered list
of visible members (e.g., displayedMembers = familyMembers.filter(m => m.lineId
!== userData?.lineId)) and use that list when checking duplicates (replace
familyMembers.filter(...).length > 1 with displayedMembers.filter(m =>
m.userName === member.userName).length > 1) so the phone tail shown by
member.phone.slice(-4) is based only on displayed members.
In `@src/page/SharedData/SharedData.tsx`:
- Around line 30-32: Remove the debug console logging in the useEffect that
watches myData in SharedData.tsx: locate the useEffect referencing myData and
delete the console.log("myData:", myData) (or guard it behind a development-only
flag if you need runtime debugging), so browser consoles no longer receive user
data on every refetch; keep the effect only if it performs necessary
side-effects beyond logging.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1cf71afb-1385-4aea-a456-e976aefc70d9
📒 Files selected for processing (22)
src/api/services/sharedPoolService.tssrc/components/Onboarding/OnboardingModal.tsxsrc/components/common/Slider.tsxsrc/constants/alarmMessages.tssrc/layout/Layout.tsxsrc/page/Detail/components/AppUsageChart.tsxsrc/page/Detail/components/DataBalance.tsxsrc/page/Log/LogPage.tsxsrc/page/Main/MainPage.tsxsrc/page/Main/components/DataBar.tsxsrc/page/Main/components/PieChart.tsxsrc/page/Policy/Policy.tsxsrc/page/Policy/components/DataRemainingCard.tsxsrc/page/Policy/components/DataThresholdSlider.tsxsrc/page/Policy/components/UserInfoCard.tsxsrc/page/PolicyDetail/components/SliderCard.tsxsrc/page/SharedData/SharedData.tsxsrc/page/SharedData/components/DataTransferCard.tsxsrc/page/SharedData/components/SharedPoolCard.tsxsrc/types/alarm.tssrc/utils/alarmUtils.tssrc/utils/dataFormat.ts
| import screenshot1 from "@/assets/img/screenshot1.png"; | ||
| import screenshot2 from "@/assets/img/screenshot1.png"; | ||
| import screenshot3 from "@/assets/img/screenshot1.png"; | ||
| import screenshot4 from "@/assets/img/screenshot1.png"; | ||
| import screenshot5 from "@/assets/img/screenshot1.png"; | ||
| import screenshot6 from "@/assets/img/screenshot1.png"; | ||
| import screenshot7 from "@/assets/img/screenshot1.png"; | ||
| import screenshot8 from "@/assets/img/screenshot1.png"; | ||
|
|
||
| const SCREENSHOT_PATHS: Record<number, string> = { | ||
| 2: "/src/assets/img/screenshot1.png", | ||
| 3: "/src/assets/img/screenshot2.png", | ||
| 4: "/src/assets/img/screenshot3.png", | ||
| 5: "/src/assets/img/screenshot4.png", | ||
| 6: "/src/assets/img/screenshot5.png", | ||
| 7: "/src/assets/img/screenshot6.png", | ||
| 8: "/src/assets/img/screenshot7.png", | ||
| 9: "/src/assets/img/screenshot8.png", | ||
| 2: screenshot1, | ||
| 3: screenshot2, | ||
| 4: screenshot3, | ||
| 5: screenshot4, | ||
| 6: screenshot5, | ||
| 7: screenshot6, | ||
| 8: screenshot7, | ||
| 9: screenshot8, |
There was a problem hiding this comment.
모든 온보딩 스크린샷이 같은 이미지로 표시됩니다.
screenshot29번 슬라이드가 서로 다른 화면이 아니라 동일한 이미지를 렌더링합니다. 각 변수에 맞는 자산으로 연결해야 합니다.screenshot8도 전부 screenshot1.png를 import하고 있어서, 2
수정 예시
import screenshot1 from "@/assets/img/screenshot1.png";
-import screenshot2 from "@/assets/img/screenshot1.png";
-import screenshot3 from "@/assets/img/screenshot1.png";
-import screenshot4 from "@/assets/img/screenshot1.png";
-import screenshot5 from "@/assets/img/screenshot1.png";
-import screenshot6 from "@/assets/img/screenshot1.png";
-import screenshot7 from "@/assets/img/screenshot1.png";
-import screenshot8 from "@/assets/img/screenshot1.png";
+import screenshot2 from "@/assets/img/screenshot2.png";
+import screenshot3 from "@/assets/img/screenshot3.png";
+import screenshot4 from "@/assets/img/screenshot4.png";
+import screenshot5 from "@/assets/img/screenshot5.png";
+import screenshot6 from "@/assets/img/screenshot6.png";
+import screenshot7 from "@/assets/img/screenshot7.png";
+import screenshot8 from "@/assets/img/screenshot8.png";📝 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.
| import screenshot1 from "@/assets/img/screenshot1.png"; | |
| import screenshot2 from "@/assets/img/screenshot1.png"; | |
| import screenshot3 from "@/assets/img/screenshot1.png"; | |
| import screenshot4 from "@/assets/img/screenshot1.png"; | |
| import screenshot5 from "@/assets/img/screenshot1.png"; | |
| import screenshot6 from "@/assets/img/screenshot1.png"; | |
| import screenshot7 from "@/assets/img/screenshot1.png"; | |
| import screenshot8 from "@/assets/img/screenshot1.png"; | |
| const SCREENSHOT_PATHS: Record<number, string> = { | |
| 2: "/src/assets/img/screenshot1.png", | |
| 3: "/src/assets/img/screenshot2.png", | |
| 4: "/src/assets/img/screenshot3.png", | |
| 5: "/src/assets/img/screenshot4.png", | |
| 6: "/src/assets/img/screenshot5.png", | |
| 7: "/src/assets/img/screenshot6.png", | |
| 8: "/src/assets/img/screenshot7.png", | |
| 9: "/src/assets/img/screenshot8.png", | |
| 2: screenshot1, | |
| 3: screenshot2, | |
| 4: screenshot3, | |
| 5: screenshot4, | |
| 6: screenshot5, | |
| 7: screenshot6, | |
| 8: screenshot7, | |
| 9: screenshot8, | |
| import screenshot1 from "@/assets/img/screenshot1.png"; | |
| import screenshot2 from "@/assets/img/screenshot2.png"; | |
| import screenshot3 from "@/assets/img/screenshot3.png"; | |
| import screenshot4 from "@/assets/img/screenshot4.png"; | |
| import screenshot5 from "@/assets/img/screenshot5.png"; | |
| import screenshot6 from "@/assets/img/screenshot6.png"; | |
| import screenshot7 from "@/assets/img/screenshot7.png"; | |
| import screenshot8 from "@/assets/img/screenshot8.png"; | |
| const SCREENSHOT_PATHS: Record<number, string> = { | |
| 2: screenshot1, | |
| 3: screenshot2, | |
| 4: screenshot3, | |
| 5: screenshot4, | |
| 6: screenshot5, | |
| 7: screenshot6, | |
| 8: screenshot7, | |
| 9: screenshot8, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/Onboarding/OnboardingModal.tsx` around lines 6 - 23, The
imports for screenshot2 through screenshot8 all point to screenshot1.png,
causing all onboarding slides to show the same image; update the import
statements (screenshot2, screenshot3, ..., screenshot8) to import their
respective asset files (e.g., screenshot2.png, screenshot3.png, etc.) and ensure
the SCREENSHOT_PATHS mapping still references those variables (keys 2–9 ->
screenshot1..screenshot8) so each slide renders its correct image.
| POLICY_UPDATE_SHAREDATA_LIMIT: "월 공유 데이터 사용량 제한이 수정되었습니다.", | ||
| POLICY_UPDATE_DAYDATA_LIMIT: "일 개인 데이터 사용량 제한이 수정되었습니다.", | ||
| POLICY_UPDATE_APP_USAGE_LIMIT: "앱 데이터의 사용량 제한이 수정되었습니다.", | ||
| POLICY_UPDATE_DATA_SPEED_LIMIT: "앱 데이터의 속도 제한이 수정되었습니다.", | ||
| ACTIVATE_POLICY: "새로운 정책이 활성화되었습니다.", | ||
| DEACTIVATE_POLICY: "새로운 정책이 비활성화되었습니다.", | ||
|
|
||
| // Policy_Limit | ||
| CREATE_REPEAT_BLOCK: "반복적 차단 정책이 생성되었습니다.", | ||
| DELETE_REPEAT_BLOCK: "반복적 차단 정책이 삭제되었습니다.", | ||
| CREATE_IMMEDIATE_BLOCK: "즉시 차단 정책이 생성되었습니다.", | ||
| DELETE_IMMEDIATE_BLOCK: "즉시 차단 정책이 삭제되었습니다.", | ||
| CREATE_DAYDATA_LIMIT: "일 개인 데이터 사용량 제한이 생성되었습니다.", | ||
| DELETE_DAYDATA_LIMIT: "일 개인 데이터 사용량 제한이 삭제되었습니다.", | ||
| CREATE_SHAREDATA_LIMIT: "월 공유 데이터 사용량 제한이 생성되었습니다.", | ||
| DELETE_SHAREDATA_LIMIT: "월 공유 데이터 사용량 제한이 삭제되었습니다.", | ||
| CREATE_APP_USAGE_LIMIT: "앱 데이터의 사용량 제한이 생성되었습니다.", | ||
| DELETE_APP_USAGE_LIMIT: "앱 데이터의 사용량 제한이 삭제되었습니다.", | ||
| CREATE_DATA_SPEED_LIMIT: "앱 데이터의 속도 제한이 생성되었습니다.", | ||
| DELETE_DATA_SPEED_LIMIT: "앱 데이터의 속도 제한이 삭제되었습니다.", | ||
| POLICY_CREATE_IMMEDIATE_BLOCK: "즉시 차단 정책이 생성되었습니다.", | ||
| POLICY_DELETE_IMMEDIATE_BLOCK: "즉시 차단 정책이 삭제되었습니다.", | ||
| POLICY_CREATE_DAYDATA_LIMIT: "일 개인 데이터 사용량 제한이 생성되었습니다.", | ||
| POLICY_DELETE_DAYDATA_LIMIT: "일 개인 데이터 사용량 제한이 삭제되었습니다.", | ||
| POLICY_CREATE_SHAREDATA_LIMIT: "월 공유 데이터 사용량 제한이 생성되었습니다.", | ||
| POLICY_DELETE_SHAREDATA_LIMIT: "월 공유 데이터 사용량 제한이 삭제되었습니다.", | ||
| POLICY_CREATE_APP_USAGE_LIMIT: "앱 데이터의 사용량 제한이 생성되었습니다.", | ||
| POLICY_DELETE_APP_USAGE_LIMIT: "앱 데이터의 사용량 제한이 삭제되었습니다.", | ||
| POLICY_CREATE_DATA_SPEED_LIMIT: "앱 데이터의 속도 제한이 생성되었습니다.", | ||
| POLICY_DELETE_DATA_SPEED_LIMIT: "앱 데이터의 속도 제한이 삭제되었습니다.", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: ALARM_MESSAGE_MAP과 TYPE_TO_ALARM_CODE 간 키 불일치 확인
echo "=== ALARM_MESSAGE_MAP keys ==="
rg -n 'POLICY_UPDATE_|POLICY_CREATE_|POLICY_DELETE_' src/constants/alarmMessages.ts
echo ""
echo "=== TYPE_TO_ALARM_CODE keys (expecting same prefix) ==="
rg -n 'UPDATE_SHAREDATA_LIMIT|UPDATE_DAYDATA_LIMIT|UPDATE_APP_USAGE_LIMIT|UPDATE_DATA_SPEED_LIMIT|CREATE_IMMEDIATE_BLOCK|DELETE_IMMEDIATE_BLOCK|CREATE_DAYDATA_LIMIT|DELETE_DAYDATA_LIMIT|CREATE_SHAREDATA_LIMIT|DELETE_SHAREDATA_LIMIT|CREATE_APP_USAGE_LIMIT|DELETE_APP_USAGE_LIMIT|CREATE_DATA_SPEED_LIMIT|DELETE_DATA_SPEED_LIMIT' src/constants/alarmMessages.tsRepository: pooli-dev/pooli-fe
Length of output: 1787
🏁 Script executed:
cat -n src/constants/alarmMessages.ts | head -100Repository: pooli-dev/pooli-fe
Length of output: 4406
🏁 Script executed:
rg -n "getAlarmMessage|ALARM_MESSAGE_MAP\[" --type ts --type tsx | head -30Repository: pooli-dev/pooli-fe
Length of output: 89
🏁 Script executed:
rg -n "getAlarmMessage|ALARM_MESSAGE_MAP\[" --type tsRepository: pooli-dev/pooli-fe
Length of output: 518
🏁 Script executed:
cat -n src/utils/alarmUtils.tsRepository: pooli-dev/pooli-fe
Length of output: 807
ALARM_MESSAGE_MAP의 POLICY_ 접두사가 TYPE_TO_ALARM_CODE의 키와 불일치하여 알림 메시지가 표시되지 않습니다.
TYPE_TO_ALARM_CODE는 UPDATE_SHAREDATA_LIMIT, CREATE_IMMEDIATE_BLOCK, DELETE_DAYDATA_LIMIT 등 접두사 없는 키를 사용하지만, ALARM_MESSAGE_MAP은 동일한 개념의 키에 POLICY_ 접두사를 붙였습니다(POLICY_UPDATE_SHAREDATA_LIMIT, POLICY_CREATE_IMMEDIATE_BLOCK, POLICY_DELETE_DAYDATA_LIMIT 등).
getAlarmMessage(src/utils/alarmUtils.ts:15)는 ALARM_MESSAGE_MAP[value.type]으로 직접 조회하기 때문에, 백엔드에서 UPDATE_SHAREDATA_LIMIT 같은 unprefixed 키를 전송하면 매칭되지 않아 폴백 메시지("알림이 도착했습니다.")만 표시됩니다. 이는 UPDATE 계열 4개, CREATE/DELETE IMMEDIATE_BLOCK 2개, CREATE/DELETE 데이터 제한 8개 등 총 14개 이상의 알림 메시지에 영향을 미칩니다.
추가로 UPDATE_REPEAT_BLOCK, UPDATE_IMMEDIATE_BLOCK, CREATE_REPEAT_BLOCK, DELETE_REPEAT_BLOCK은 접두사가 없어 명명 규칙이 일관되지 않습니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/constants/alarmMessages.ts` around lines 23 - 42, ALARM_MESSAGE_MAP uses
keys with a POLICY_ prefix (e.g., POLICY_UPDATE_SHAREDATA_LIMIT,
POLICY_CREATE_IMMEDIATE_BLOCK) that do not match the unprefixed keys
produced/used by TYPE_TO_ALARM_CODE and read by getAlarmMessage (which does
ALARM_MESSAGE_MAP[value.type]); update ALARM_MESSAGE_MAP to use the same
unprefixed keys (e.g., UPDATE_SHAREDATA_LIMIT, CREATE_IMMEDIATE_BLOCK,
DELETE_DAYDATA_LIMIT, UPDATE_REPEAT_BLOCK, UPDATE_IMMEDIATE_BLOCK,
CREATE_REPEAT_BLOCK, DELETE_REPEAT_BLOCK, etc.) or alternatively add duplicate
unprefixed entries that map to the same messages, and make the naming consistent
for UPDATE/CREATE/DELETE of repeat/immediate/data-limit entries so
getAlarmMessage(value.type) finds the correct message.
| {formatDataLabel(remaining)} | ||
| <span className="text-base font-normal text-gray-400 ml-1"> | ||
| / {total}GB | ||
| / {formatDataLabel(total)}GB | ||
| </span> |
There was a problem hiding this comment.
총량 라벨에 단위가 한 번 더 붙습니다.
formatDataLabel(total)가 이미 단위를 포함하므로 현재 문자열은 500MBGB나 1.2GBGB처럼 렌더링됩니다.
💡 제안된 수정
<span className="text-base font-normal text-gray-400 ml-1">
- / {formatDataLabel(total)}GB
+ / {formatDataLabel(total)}
</span>📝 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.
| {formatDataLabel(remaining)} | |
| <span className="text-base font-normal text-gray-400 ml-1"> | |
| / {total}GB | |
| / {formatDataLabel(total)}GB | |
| </span> | |
| {formatDataLabel(remaining)} | |
| <span className="text-base font-normal text-gray-400 ml-1"> | |
| / {formatDataLabel(total)} | |
| </span> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/page/Log/LogPage.tsx` around lines 162 - 165, The total data label is
rendered with an extra "GB" because formatDataLabel(total) already returns a
unit-suffixed string; update the Render in LogPage (around
formatDataLabel(remaining) / formatDataLabel(total)) to stop appending the
literal "GB" — either remove the trailing "GB" after formatDataLabel(total) or
call a variant of formatDataLabel that returns a unit-less string if available
(reference symbols: formatDataLabel, remaining, total, LogPage).
| const limitDisplay = limitData.toString(); | ||
|
|
||
| return ( | ||
| <div className="mb-8"> | ||
| <div className="flex justify-between items-start mb-3"> | ||
| <DataStatItem label="공유한 데이터" value={contributedGB} color={COLORS.primary} showUnit={true} /> | ||
| <DataStatItem label="한도 데이터" value={limitDisplay} color={COLORS.textGray} showUnit={true} /> | ||
| <DataStatItem | ||
| label="공유한 데이터" | ||
| value={contributedGB} | ||
| color={COLORS.primary} | ||
| showUnit={true} | ||
| /> | ||
| <DataStatItem | ||
| label="한도 데이터" | ||
| value={limitDisplay} | ||
| color={COLORS.textGray} | ||
| showUnit={true} | ||
| /> |
There was a problem hiding this comment.
한도 데이터가 단위 없이 렌더링됩니다.
공유한 데이터는 GB/MB가 포함된 문자열인데, 여기서는 limitData를 숫자만 넘겨서 현재 UI가 50처럼 보입니다. 같은 카드 안에서 표기 규칙이 달라져서 헷갈리니 이 값도 단위를 포함해 주세요.
수정 예시
function DataStats({ contributedGB, limitData }: DataStatsProps) {
- const limitDisplay = limitData.toString();
+ const limitDisplay = `${limitData}GB`;
return (
<div className="mb-8">
<div className="flex justify-between items-start mb-3">
- <DataStatItem
- label="공유한 데이터"
- value={contributedGB}
- color={COLORS.primary}
- showUnit={true}
- />
- <DataStatItem
- label="한도 데이터"
- value={limitDisplay}
- color={COLORS.textGray}
- showUnit={true}
- />
+ <DataStatItem label="공유한 데이터" value={contributedGB} color={COLORS.primary} />
+ <DataStatItem label="한도 데이터" value={limitDisplay} color={COLORS.textGray} />
</div>
</div>
);
}📝 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.
| const limitDisplay = limitData.toString(); | |
| return ( | |
| <div className="mb-8"> | |
| <div className="flex justify-between items-start mb-3"> | |
| <DataStatItem label="공유한 데이터" value={contributedGB} color={COLORS.primary} showUnit={true} /> | |
| <DataStatItem label="한도 데이터" value={limitDisplay} color={COLORS.textGray} showUnit={true} /> | |
| <DataStatItem | |
| label="공유한 데이터" | |
| value={contributedGB} | |
| color={COLORS.primary} | |
| showUnit={true} | |
| /> | |
| <DataStatItem | |
| label="한도 데이터" | |
| value={limitDisplay} | |
| color={COLORS.textGray} | |
| showUnit={true} | |
| /> | |
| const limitDisplay = `${limitData}GB`; | |
| return ( | |
| <div className="mb-8"> | |
| <div className="flex justify-between items-start mb-3"> | |
| <DataStatItem label="공유한 데이터" value={contributedGB} color={COLORS.primary} /> | |
| <DataStatItem label="한도 데이터" value={limitDisplay} color={COLORS.textGray} /> | |
| </div> | |
| </div> | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/page/SharedData/components/DataTransferCard.tsx` around lines 165 - 181,
The limit value (limitData → limitDisplay) is rendered without units while
contributedGB includes units, causing inconsistent UI; update the
DataTransferCard rendering so limitDisplay includes the unit string (e.g., "GB"
or "MB") before passing to DataStatItem or set showUnit=true and pass a
formatted string; specifically modify how limitData is converted to limitDisplay
in DataTransferCard (where limitDisplay is computed) so the value passed into
the DataStatItem for label "한도 데이터" matches the format used for contributedGB.
| export const formatDataLabel = (bytes: number): string => { | ||
| const gb = Math.round((bytes / 1e9) * 100) / 100; | ||
| if (gb >= 1) return `${gb}GB`; | ||
| const mb = Math.round(bytes / 1e6); | ||
| return `${mb}MB`; |
There was a problem hiding this comment.
1GB 경계값 판정이 반올림 때문에 뒤집힙니다.
지금은 GB 값을 먼저 반올림한 뒤 >= 1을 검사해서, 실제로는 1GB 미만인 값도 "1GB"로 표시될 수 있습니다. PR 설명대로라면 경계 판정은 반올림 전 값으로 해야 합니다.
수정 예시
export const formatDataLabel = (bytes: number): string => {
- const gb = Math.round((bytes / 1e9) * 100) / 100;
- if (gb >= 1) return `${gb}GB`;
+ const rawGb = bytes / 1e9;
+ if (rawGb >= 1) {
+ return `${Math.round(rawGb * 100) / 100}GB`;
+ }
const mb = Math.round(bytes / 1e6);
return `${mb}MB`;
};📝 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.
| export const formatDataLabel = (bytes: number): string => { | |
| const gb = Math.round((bytes / 1e9) * 100) / 100; | |
| if (gb >= 1) return `${gb}GB`; | |
| const mb = Math.round(bytes / 1e6); | |
| return `${mb}MB`; | |
| export const formatDataLabel = (bytes: number): string => { | |
| const rawGb = bytes / 1e9; | |
| if (rawGb >= 1) { | |
| return `${Math.round(rawGb * 100) / 100}GB`; | |
| } | |
| const mb = Math.round(bytes / 1e6); | |
| return `${mb}MB`; | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/utils/dataFormat.ts` around lines 20 - 24, The function formatDataLabel
currently rounds the GB value before checking the 1GB threshold which can cause
values just under 1GB to be labeled "1GB"; change formatDataLabel to first
compute the exact GB value (e.g., gbExact = bytes / 1e9) and use gbExact >= 1
for the branch decision, and only then format/round the displayed GB value
(e.g., round to two decimals) when returning `${...}GB`; otherwise fall back to
MB formatting (keep existing mb rounding logic). Ensure you update the logic in
the formatDataLabel function accordingly.
이슈
✔️ 체크리스트
🔍 작업 내용
Summary by CodeRabbit
릴리스 노트
스타일
버그 수정
기능 개선