8000 Feature/ux 3 by guesung · Pull Request #109 · guesung/Web-Memo · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Feature/ux 3 #109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Nov 29, 2024
Merged

Feature/ux 3 #109

merged 12 commits into from
Nov 29, 2024

Conversation

guesung
Copy link
Owner
@guesung guesung commented Nov 29, 2024

Summary by CodeRabbit

  • 새로운 기능

    • 메모 수를 표시하는 기능 추가: "totalMemos" 항목이 영어 및 한국어 번역 파일에 추가되었습니다.
    • 내비게이션을 위한 새로운 Breadcrumb 컴포넌트 세트 추가.
  • 버그 수정

    • 메모 삭제 및 카테고리 변경 처리 로직 개선, 사용자 경험 향상.
  • UI 개선

    • 여러 컴포넌트의 스타일 업데이트 및 접근성 향상.
    • Textarea의 시각적 표현 개선.
  • 문서화

    • 번역 파일에서 설명 텍스트 수정으로 사용자 지침 개선.

Summary by CodeRabbit

  • 새로운 기능

    • 메모 수를 표시하는 기능 추가: "totalMemos" 항목이 영어 및 한국어 번역 파일에 추가되었습니다.
    • 내비게이션을 위한 새로운 Breadcrumb 컴포넌트 세트 추가.
    • MemoDialog에서 메모 ID를 prop으로 받아 처리하도록 변경.
  • 버그 수정

    • 메모 삭제 및 카테고리 변경 처리 로직 개선, 사용자 경험 향상.
    • useMemosQuery에서 memosundefined가 되지 않도록 수정.
  • UI 개선

    • 여러 컴포넌트의 스타일 업데이트 및 접근성 향상.
    • Textarea의 시각적 표현 개선.
    • MemoOption 컴포넌트의 버튼 스타일과 이벤트 처리 개선.
  • 문서화

    • 번역 파일에서 설명 텍스트 수정으로 사용자 지침 개선.

Copy link
coderabbitai bot commented Nov 29, 2024

Walkthrough

이 풀 리퀘스트는 여러 파일에서의 변경 사항을 포함하고 있으며, 주요 변경 사항은 useMemosQuery 함수의 반환값을 수정하여 memosundefined가 아닌 빈 배열로 기본값을 갖도록 하고, TypeScript 구성 파일에 새로운 컴파일러 옵션을 추가하는 것입니다. 또한 여러 UI 구성 요소의 스타일과 이벤트 처리 로직이 개선되었으며, 새로운 브레드크럼 내비게이션 컴포넌트가 추가되었습니다. 이 외에도 여러 컴포넌트의 인터페이스가 업데이트되고, 번역 파일에 새로운 항목이 추가되었습니다.

Changes

파일 경로 변경 요약
packages/shared/src/hooks/supabase/useMemosQuery.ts memos 반환값을 query.data?.data ?? []로 수정하여 기본값을 빈 배열로 설정.
packages/ui/tsconfig.json declarationMap 옵션을 true로 추가하여 선언 파일의 소스 맵 생성.
packages/web/src/app/[lng]/login/components/LoginSection/index.tsx section 요소의 클래스 속성을 w-96에서 px-8로 변경, 버튼 텍스트를 새 줄로 이동.
packages/web/src/app/[lng]/memos/components/Header/index.tsx PATHS의 import 문을 파일 상단으로 이동.
packages/web/src/app/[lng]/memos/components/MemoCardFooter/MemoOption.tsx 버튼의 변형 및 크기를 변경하고, 삭제 메모에 대한 더 나은 오류 처리 로직 추가.
packages/web/src/app/[lng]/memos/components/MemoCardFooter/index.tsx handleIsWishClick 함수의 매개변수 타입을 변경하고, HeartIconButton으로 감싸서 클릭 가능하게 변경.
packages/web/src/app/[lng]/memos/components/MemoDialog/index.tsx id 속성을 추가하고, 상태 관리를 위한 open 변수를 도입하여 다이얼로그의 열림 상태를 관리.
packages/web/src/app/[lng]/memos/components/MemoSidebar/SidebarMenuItemAddCategory.tsx <p> 요소에 role="button" 속성을 추가하여 접근성 향상.
packages/web/src/app/[lng]/memos/components/MemoSidebar/index.tsx SettingsIcon을 감싸는 Link 컴포넌트에서 cursor-pointer 클래스를 제거.
packages/web/src/app/[lng]/memos/components/MemoSidebarTrigger/index.tsx TooltipProvider 주위의 불필요한 <div> 래퍼 제거.
packages/web/src/app/[lng]/memos/components/MemoView/MemoItem.tsx 클릭 핸들러의 서명을 변경하고, 이벤트 핸들러를 motion.article로 이동하여 접근성 향상.
packages/web/src/app/[lng]/memos/components/MemoView/index.tsx MemoViewProps 인터페이스에 isWishcategory 속성을 추가하고, 검색 매개변수에서 직접 가져옴.
packages/web/src/app/[lng]/memos/layout.tsx MemoSidebarTrigger 컴포넌트를 렌더링에서 제거하여 사이드바 기능 단순화.
packages/web/src/app/[lng]/memos/page.tsx PageProps 인터페이스 추가 및 searchParams를 통해 MemoView에 전달.
packages/web/src/components/ui/breadcrumb.tsx 새로운 브레드크럼 내비게이션 컴포넌트 세트 추가.
packages/web/src/components/ui/textarea.tsx <textarea> 요소에서 특정 클래스 제거, 나머지 클래스는 유지.
packages/web/src/modules/i18n/locales/en/translation.json guide 섹션의 텍스트 수정 및 새로운 항목 "totalMemos" 추가.
packages/web/src/modules/i18n/locales/ko/translation.json "totalMemos" 항목 추가하여 총 메모 수를 표시.
pages/side-panel/src/components/MemoForm.tsx Textarea 컴포넌트의 스타일 수정 및 isSaved 상태 처리 로직 업데이트.

Possibly related PRs

  • Feature: 최적화 #97: useMemosQuery 함수의 변경 사항이 관련되어 있으며, 메모 데이터 처리 및 쿼리 방식에 대한 수정이 포함됨.
  • Feature: 세팅 페이지 구현 #100: 메모 처리와 관련된 전반적인 구조 및 기능에 영향을 줄 수 있는 새로운 설정 페이지가 포함됨.
  • Feature/ux #101: 사용자 경험 향상에 대한 제안이 있으며, 메모 처리 및 쿼리 프로세스의 개선과 관련이 있을 수 있음.

🐰 새로운 기능이 가득한 이곳,
메모가 더 똑똑해졌어요,
버튼도 더 예쁘게 바뀌고,
다이얼로그는 열리고 닫히죠.
함께 즐겨요, 우리 메모를! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
@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: 6

🧹 Outside diff range and nitpick comments (16)
packages/web/src/app/[lng]/memos/components/MemoSidebarTrigger/index.tsx (1)

17-17: 접근성 개선을 위한 제안

사이드바 트리거 버튼에 aria-label을 추가하여 스크린 리더 사용자를 위한 접근성을 개선하는 것이 좋습니다.

다음과 같이 수정하는 것을 제안합니다:

-          <SidebarTrigger />
+          <SidebarTrigger aria-label={t('tooltip.sideBar')} />
packages/web/src/app/[lng]/memos/components/MemoView/index.tsx (2)

27-27: 필터링 로직을 더 명확하게 표현하는 것이 좋겠습니다.

이중 부정(!!)을 사용한 비교는 동작하지만, 코드의 의도를 파악하기 어려울 수 있습니다.

다음과 같이 더 명시적으로 표현하는 것을 고려해보세요:

-?.filter(memo => !!isWish === !!memo.isWish)
+?.filter(memo => Boolean(isWish) === Boolean(memo.isWish))

또는:

-?.filter(memo => !!isWish === !!memo.isWish)
+?.filter(memo => {
+  const isWishMemo = Boolean(memo.isWish);
+  const isWishFilter = Boolean(isWish);
+  return isWishFilter === isWishMemo;
+})

31-37: UI 구조가 깔끔하게 구성되었습니다!

레이아웃과 조건부 렌더링이 잘 구현되어 있습니다. 다만, 카테고리 표시 부분에 대한 국제화(i18n)를 고려해보면 좋겠습니다.

카테고리 표시 부분을 다음과 같이 수정하는 것을 제안드립니다:

-{category && `${category} | `}
+{category && `${t('common.category', { category })} | `}
packages/web/src/app/[lng]/memos/layout.tsx (1)

Line range hint 1-40: 전반적인 구현이 잘 되어있습니다.

다음과 같은 우수한 구현 사항들이 확인됩니다:

  • 서버 컴포넌트 지시자 사용
  • 적절한 사용자 인증 확인 및 리다이렉션 처리
  • 쿠키를 통한 사이드바 상태 관리
  • HydrationBoundaryWrapper를 통한 클라이언트 사이드 데이터 페칭

하나의 개선 제안사항이 있습니다:

사이드바의 기본 상태를 위한 상수를 추가하는 것이 좋을 것 같습니다:

+ const SIDEBAR_DEFAULT_STATE = 'true';
  const cookieStore = cookies();
- const defaultOpen = cookieStore.get(COOKIE_KEY.sideBarState)?.value === 'true';
+ const defaultOpen = cookieStore.get(COOKIE_KEY.sideBarState)?.value === SIDEBAR_DEFAULT_STATE;
packages/web/src/app/[lng]/login/components/LoginSection/index.tsx (1)

15-15: 반응형 레이아웃으로의 변경이 적절해 보입니다.

고정 너비에서 패딩 기반 레이아웃으로의 변경은 다양한 화면 크기에 더 잘 대응할 수 있게 해줍니다. 다만, 매우 큰 화면에서의 최대 너비 제한을 고려해보시면 좋을 것 같습니다.

필요한 경우 다음과 같이 최대 너비를 제한하는 것을 고려해보세요:

-    <section className="relative flex flex-col items-center justify-center rounded-md bg-zinc-100 px-8 py-12 opacity-80 shadow-xl dark:bg-zinc-900">
+    <section className="relative flex flex-col items-center justify-center rounded-md bg-zinc-100 px-8 py-12 opacity-80 shadow-xl dark:bg-zinc-900 max-w-md mx-auto">
packages/web/src/app/[lng]/memos/components/MemoView/MemoItem.tsx (1)

42-42: 접근성 개선을 위한 추가 제안

키보드 상호작용과 tabIndex 추가는 좋은 접근성 개선입니다. 하지만 더 나은 스크린 리더 지원을 위해 role 속성을 추가하는 것을 고려해보세요.

다음과 같이 role 속성을 추가하는 것을 제안합니다:

 <motion.article
   id={String(memo.id)}
   initial={{ opacity: 0, y: 10 }}
   animate={{ opacity: 1, y: 0 }}
   transition={{ duration: 0.3 }}
   
    => e.key === 'Enter' && handleItemClick(e)}
   className="transition-all"
   tabIndex={0}
+  role="article"
   {...props}>

Also applies to: 46-47, 49-49

packages/web/src/modules/i18n/locales/ko/translation.json (1)

83-84: 번역이 자연스럽고 적절합니다!

문법과 어순이 자연스럽고, 카운터 접미사 "개"의 사용이 적절합니다. 다만, 사용자 경험을 더욱 향상시키기 위해 다음과 같은 개선을 고려해보시는 건 어떨까요?

더 친근한 표현으로 수정하는 것을 제안드립니다:

-    "totalMemos": "총 {{total}}개의 메모"
+    "totalMemos": "지금까지 {{total}}개의 메모를 작성하셨어요"
packages/web/src/components/ui/breadcrumb.tsx (3)

1-5: import 구문 정리가 필요합니다

import 구문을 다음과 같은 순서로 정리하면 코드의 가독성이 향상될 것 같습니다:

  1. React 관련
  2. 외부 라이브러리
  3. 내부 유틸리티
-import * as React from "react"
-import { Slot } from "@radix-ui/react-slot"
-import { ChevronRight, MoreHorizontal } from "lucide-react"
-
-import { cn } from "@src/utils"
+import * as React from 'react';
+
+import { Slot } from '@radix-ui/react-slot';
+import { ChevronRight, MoreHorizontal } from 'lucide-react';
+
+import { cn } from '@src/utils';
🧰 Tools
🪛 eslint

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 1-1: Replace "react" with 'react';

(prettier/prettier)


[error] 2-2: Replace "@radix-ui/react-slot" with '@radix-ui/react-slot';

(prettier/prettier)


[error] 3-3: Replace "lucide-react" with 'lucide-react';

(prettier/prettier)


[error] 5-5: Replace "@src/utils" with '@src/utils';

(prettier/prettier)


7-13: 구현이 깔끔하고 접근성이 잘 고려되었습니다

nav 요소에 aria-label을 추가한 것이 좋습니다. 다만, aria-label을 props로 받아 커스터마이즈할 수 있게 하면 더 좋을 것 같습니다.

 const Breadcrumb = React.forwardRef<
   HTMLElement,
   React.ComponentPropsWithoutRef<"nav"> & {
     separator?: React.ReactNode
+    ariaLabel?: string;
   }
->(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
+>(({ ariaLabel = 'breadcrumb', ...props }, ref) => (
+  <nav ref={ref} aria-label={ariaLabel} {...props} />
+))
🧰 Tools
🪛 eslint

[error] 9-9: Replace "nav" with 'nav'

(prettier/prettier)


[error] 10-10: Insert ;

(prettier/prettier)


[error] 12-12: Insert ;

(prettier/prettier)


[error] 13-13: Replace "Breadcrumb" with 'Breadcrumb';

(prettier/prettier)


42-58: 컴포넌트 합성이 잘 구현되었습니다

Radix UI의 Slot을 활용한 컴포넌트 합성이 잘 되어있습니다. 하지만 hover 상태의 색상을 theme 시스템과 연동하면 좋을 것 같습니다.

-      className={cn("transition-colors hover:text-foreground", className)}
+      className={cn("transition-colors hover:text-primary", className)}
🧰 Tools
🪛 eslint

[error] 44-44: Replace "a" with 'a'

(prettier/prettier)


[error] 45-45: Insert ;

(prettier/prettier)


[error] 48-48: Replace "a" with 'a';

(prettier/prettier)


[error] 50-56: Replace (⏎····<Comp⏎······ref={ref}⏎······className={cn("transition-colors·hover:text-foreground",·className)}⏎······{...props}⏎····/>⏎··) with <Comp·ref={ref}·className={cn('hover:text-foreground·transition-colors',·className)}·{...props}·/>;

(prettier/prettier)


[error] 57-57: Insert ;

(prettier/prettier)


[error] 58-58: Replace "BreadcrumbLink" with 'BreadcrumbLink';

(prettier/prettier)

packages/web/src/app/[lng]/memos/components/MemoCardFooter/index.tsx (1)

41-43: 이벤트 처리 개선이 필요합니다.

현재 구현은 좋지만, 다음과 같은 개선사항을 고려해보세요:

  • 버튼 클릭 시 사용자 피드백을 위한 loading 상태 추가
  • 에러 처리 로직 추가
  const handleIsWishClick = (event: MouseEvent<HTMLButtonElement>) => {
    event.stopPropagation();
+   const prevIsWish = memo.isWish;
+   try {
      mutateMemoPatch(
        {
          id: memo.id,
          memoRequest: {
            isWish: !memo.isWish,
          },
        },
        {
+         onError: () => {
+           toast({
+             title: t('error.wishUpdateFailed'),
+             variant: 'destructive',
+           });
+         },
          onSuccess: () => {
            const toastTitle = prevIsWish ? t('toastTitle.memoWishListDeleted') : t('toastTitle.memoWishListAdded');
            // ... rest of the success handler
          },
        },
      );
+   } catch (error) {
+     console.error('Failed to update wish status:', error);
+   }
  };
packages/web/src/app/[lng]/memos/components/MemoDialog/index.tsx (1)

25-25: 상태 관리 로직이 개선되었습니다만, 초기값 처리를 검토해주세요.

open 상태의 초기값이 false로 설정되어 있어 컴포넌트 마운트 시 깜빡임이 발생할 수 있습니다.

다음과 같이 초기값을 설정하는 것을 고려해보세요:

-const [open, setOpen] = useState(false);
+const [open, setOpen] = useState(!!id);

Also applies to: 32-32

pages/side-panel/src/components/MemoForm.tsx (2)

128-129: 스타일링 개선이 잘 이루어졌습니다!

테마 컬러를 활용한 스타일링으로 변경하여 디자인 시스템의 일관성이 향상되었습니다. 다만, 사용자 경험을 더욱 개선하기 위해 다음 사항을 고려해보시면 좋을 것 같습니다.

저장 중임을 나타내는 시각적 표시를 추가하는 것은 어떨까요? 다음과 같이 구현할 수 있습니다:

 <Textarea
   {...register('memo', {
     onChange: handleMemoTextAreaChange,
   })}
   className={cn('h-full resize-none text-sm outline-none', {
     'border-primary focus:border-primary': !isSaved,
+    'after:content-["저장중..."] after:absolute after:bottom-2 after:right-2 after:text-xs after:text-gray-400': !isSaved,
   })}
   id="memo-textarea"
   placeholder={I18n.get('memo')}
   
 />

Line range hint 1-146: 컴포넌트 구조에 대한 제안사항

전반적으로 잘 구현되어 있으나, 다음과 같은 개선사항을 고려해보시면 좋을 것 같습니다:

  1. 로딩 상태 처리
  2. 타입 안정성 강화

다음과 같은 개선을 제안드립니다:

// 메모 상태를 위한 타입 정의
type MemoState = {
  isLoading: boolean;
  isSaving: boolean;
  isSaved: boolean;
};

// 컴포넌트 내부에서 상태 관리
const [memoState, setMemoState] = useState<MemoState>({
  isLoading: false,
  isSaving: false,
  isSaved: true
});

// 로딩 상태에 따른 UI 처리
{memoState.isLoading && <LoadingSpinner />}
packages/web/src/app/[lng]/memos/components/MemoCardFooter/MemoOption.tsx (2)

Line range hint 50-69: 메모 삭제 처리 로직이 개선되었습니다만, 에러 처리를 보완하면 좋겠습니다.

삭제된 메모를 복구할 수 있는 실행 취소 기능이 잘 구현되었습니다. 하지만 다음 사항들을 고려해보시기 바랍니다:

  1. 삭제 실패 시의 에러 처리
  2. 복구 작업 실패 시의 에러 처리

다음과 같이 에러 처리를 추가하는 것을 제안드립니다:

 mutateDeleteMemo(memoId, {
   onSuccess: ({ data }) => {
     if (!data) return;

     const deletedMemo = data[0];
-    const handlePostMemo = () => mutatePostMemo(deletedMemo);
+    const handlePostMemo = () => {
+      mutatePostMemo(deletedMemo, {
+        onError: () => {
+          toast({
+            variant: 'destructive',
+            title: t('toastTitle.error'),
+            description: t('toastDescription.restoreFailed'),
+          });
+        },
+      });
+    };

     toast({
       title: t('toastTitle.memoDeleted'),
       action: (
         <ToastAction altText={t('toastActionMessage.undo')} 
           {t('toastActionMessage.undo')}
         </ToastAction>
       ),
     });
     requestRefetchTheMemos();
   },
+  onError: () => {
+    toast({
+      variant: 'destructive',
+      title: t('toastTitle.error'),
+      description: t('toastDescription.deleteFailed'),
+    });
+  },
 });

122-126: 카테고리 선택 UI 개선사항에 대한 제안

cursor-pointer 클래스 추가로 사용자 상호작용이 개선되었습니다만, 다음과 같은 개선사항을 고려해보시기 바랍니다:

  1. category.id를 문자열로 변환하는 로직을 별도의 유틸리티 함수로 분리
  2. SelectItem의 props 타입 안전성 강화

다음과 같은 리팩토링을 제안드립니다:

+const getCategoryIdString = (id: number) => String(id);
+
 {categories?.map(category => (
   <SelectItem
     key={category.id}
-    value={String(category.id)}
-    id={String(category.id)}
+    value={getCategoryIdString(category.id)}
+    id={getCategoryIdString(category.id)}
     className="cursor-pointer">
     {category.name}
   </SelectItem>
 ))}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 54fa20d and 077f5be.

📒 Files selected for processing (19)
  • packages/shared/src/hooks/supabase/useMemosQuery.ts (1 hunks)
  • packages/ui/tsconfig.json (1 hunks)
  • packages/web/src/app/[lng]/login/components/LoginSection/index.tsx (2 hunks)
  • packages/web/src/app/[lng]/memos/components/Header/index.tsx (1 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoCardFooter/MemoOption.tsx (3 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoCardFooter/index.tsx (4 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoDialog/index.tsx (4 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoSidebar/SidebarMenuItemAddCategory.tsx (1 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoSidebar/index.tsx (1 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoSidebarTrigger/index.tsx (1 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoView/MemoItem.tsx (2 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoView/index.tsx (2 hunks)
  • packages/web/src/app/[lng]/memos/layout.tsx (1 hunks)
  • packages/web/src/app/[lng]/memos/page.tsx (1 hunks)
  • packages/web/src/components/ui/breadcrumb.tsx (1 hunks)
  • packages/web/src/components/ui/textarea.tsx (1 hunks)
  • packages/web/src/modules/i18n/locales/en/translation.json (2 hunks)
  • packages/web/src/modules/i18n/locales/ko/translation.json (1 hunks)
  • pages/side-panel/src/components/MemoForm.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/web/src/app/[lng]/memos/components/Header/index.tsx
  • packages/web/src/app/[lng]/memos/components/MemoSidebar/index.tsx
🧰 Additional context used
🪛 eslint
packages/web/src/app/[lng]/memos/components/MemoSidebar/SidebarMenuItemAddCategory.tsx

[error] 38-38: Non-interactive elements should not be assigned interactive roles.

(jsx-a11y/no-noninteractive-element-to-interactive-role)

packages/web/src/components/ui/breadcrumb.tsx

[error] 1-5: Run autofix to sort these imports!

(simple-import-sort/imports)


[error] 1-1: Replace "react" with 'react';

(prettier/prettier)


[error] 2-2: Replace "@radix-ui/react-slot" with '@radix-ui/react-slot';

(prettier/prettier)


[error] 3-3: Replace "lucide-react" with 'lucide-react';

(prettier/prettier)


[error] 5-5: Replace "@src/utils" with '@src/utils';

(prettier/prettier)


[error] 9-9: Replace "nav" with 'nav'

(prettier/prettier)


[error] 10-10: Insert ;

(prettier/prettier)


[error] 12-12: Insert ;

(prettier/prettier)


[error] 13-13: Replace "Breadcrumb" with 'Breadcrumb';

(prettier/prettier)


[error] 15-18: Replace ⏎··HTMLOListElement,⏎··React.ComponentPropsWithoutRef<"ol">⏎>( with HTMLOListElement,·React.ComponentPropsWithoutRef<'ol'>>(⏎··

(prettier/prettier)


[error] 19-19: Insert ··

(prettier/prettier)


[error] 20-20: Insert ··

(prettier/prettier)


[error] 21-21: Replace ···· with ······

(prettier/prettier)


[error] 22-22: Replace "flex·flex-wrap·items-center·gap-1.5·break-words·text-sm·text-muted-foreground·sm:gap-2.5" with ··'text-muted-foreground·flex·flex-wrap·items-center·gap-1.5·break-words·text-sm·sm:gap-2.5'

(prettier/prettier)


[error] 23-23: Replace ······className with ········className,

(prettier/prettier)


[error] 24-24: Replace ···· with ······

(prettier/prettier)


[error] 25-25: Insert ··

(prettier/prettier)


[error] 26-26: Insert ··

(prettier/prettier)


[error] 27-27: Replace )) with ··),⏎);

(prettier/prettier)


[error] 28-28: Replace "BreadcrumbList" with 'BreadcrumbList';

(prettier/prettier)


[error] 30-33: Replace ⏎··HTMLLIElement,⏎··React.ComponentPropsWithoutRef<"li">⏎>( with HTMLLIElement,·React.ComponentPropsWithoutRef<'li'>>(⏎··

(prettier/prettier)


[error] 34-38: Replace ··<li⏎····ref={ref}⏎····className={cn("inline-flex·items-center·gap-1.5",·className)}⏎····{...props}⏎· with ····<li·ref={ref}·className={cn('inline-flex·items-center·gap-1.5',·className)}·{...props}

(prettier/prettier)


[error] 39-39: Replace )) with ··),⏎);

(prettier/prettier)


[error] 40-40: Replace "BreadcrumbItem" with 'BreadcrumbItem';

(prettier/prettier)


[error] 44-44: Replace "a" with 'a'

(prettier/prettier)


[error] 45-45: Insert ;

(prettier/prettier)


[error] 48-48: Replace "a" with 'a';

(prettier/prettier)


[error] 50-56: Replace (⏎····<Comp⏎······ref={ref}⏎······className={cn("transition-colors·hover:text-foreground",·className)}⏎······{...props}⏎····/>⏎··) with <Comp·ref={ref}·className={cn('hover:text-foreground·transition-colors',·className)}·{...props}·/>;

(prettier/prettier)


[error] 57-57: Insert ;

(prettier/prettier)


[error] 58-58: Replace "BreadcrumbLink" with 'BreadcrumbLink';

(prettier/prettier)


[error] 60-63: Replace ⏎··HTMLSpanElement,⏎··React.ComponentPropsWithoutRef<"span">⏎>( with HTMLSpanElement,·React.ComponentPropsWithoutRef<'span'>>(⏎··

(prettier/prettier)


[error] 64-64: Insert ··

(prettier/prettier)


[error] 65-65: Replace ···· with ······

(prettier/prettier)


[error] 66-66: Insert ··

(prettier/prettier)


[error] 67-67: Insert ··

(prettier/prettier)


[error] 68-68: Replace ···· with ······

(prettier/prettier)


[error] 69-69: Replace className={cn("font-normal·text-foreground" with ··className={cn('text-foreground·font-normal'

(prettier/prettier)


[error] 70-70: Insert ··

(prettier/prettier)


[error] 71-71: Insert ··

(prettier/prettier)


[error] 72-72: Replace )) with ··),⏎);

(prettier/prettier)


[error] 73-73: Replace "BreadcrumbPage" with 'BreadcrumbPage';

(prettier/prettier)


[error] 75-79: Replace ⏎··children,⏎··className,⏎··...props⏎}:·React.ComponentProps<"li" with ·children,·className,·...props·}:·React.ComponentProps<'li'

(prettier/prettier)


[error] 80-85: Replace ⏎····role="presentation"⏎····aria-hidden="true"⏎····className={cn("[&>svg]:w-3.5·[&>svg]:h-3.5",·className)}⏎····{...props}⏎·· with ·role="presentation"·aria-hidden="true"·className={cn('[&>svg]:h-3.5·[&>svg]:w-3.5',·className)}·{...props}

(prettier/prettier)


[error] 88-88: Insert ;

(prettier/prettier)


[error] 89-89: Replace "BreadcrumbSeparator" with 'BreadcrumbSeparator';

(prettier/prettier)


[error] 91-94: Replace ⏎··className,⏎··...props⏎}:·React.ComponentProps<"span" with ·className,·...props·}:·React.ComponentProps<'span'

(prettier/prettier)


[error] 98-98: Replace "flex·h-9·w-9·items-center·justify-center" with 'flex·h-9·w-9·items-center·justify-center'

(prettier/prettier)


[error] 99-100: Delete ⏎··

(prettier/prettier)


[error] 104-104: Insert ;

(prettier/prettier)


[error] 105-105: Replace "BreadcrumbElipssis" with 'BreadcrumbElipssis';

(prettier/prettier)


[error] 107-115: Run autofix to sort these exports!

(simple-import-sort/exports)


[error] 115-115: Insert ;

(prettier/prettier)

🔇 Additional comments (21)
packages/ui/tsconfig.json (1)

12-12: 소스 맵 생성 설정이 적절히 추가되었습니다.

declarationMap 옵션 추가는 다음과 같은 이점을 제공합니다:

  • IDE에서 타입 정의 파일에서 원본 소스로의 이동이 가능해집니다
  • 선언 파일 디버깅이 용이해집니다
  • "정의로 이동" 기능이 향상됩니다

이는 개발자 경험을 개선하는 좋은 변경사항입니다.

packages/web/src/components/ui/textarea.tsx (1)

9-9: 스타일 단순화가 적절히 이루어졌습니다.

테두리와 포커스 링 스타일이 제거되었지만, 기본적인 접근성과 사용성이 유지되고 있습니다. 다만, 포커스 표시의 시각적 피드백이 충분한지 확인이 필요합니다.

포커스 표시의 적절성을 확인하기 위해 다음 스크립트를 실행하세요:

✅ Verification successful

포커스 스타일이 적절히 구현되어 있습니다.

검증 결과, Textarea 컴포넌트는 focus-visible:outline-none 스타일과 함께 기본적인 접근성 스타일을 유지하고 있습니다. 또한 전체 UI 시스템에서 일관된 포커스 처리 패턴이 적용되어 있어 접근성 측면에서도 문제가 없습니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: 컴포넌트의 포커스 관련 스타일 사용 검증

# MemoForm 등 Textarea를 사용하는 컴포넌트에서 추가적인 포커스 스타일 확인
rg -l "Textarea.*className.*focus" 

# 전역 포커스 스타일 설정 확인
rg "focus-visible" "*.css"

Length of output: 109


Script:

#!/bin/bash
# 포커스 스타일 관련 파일 검색을 위한 더 넓은 범위의 검증

# 전역 CSS 파일 찾기
fd -e css

# Textarea 컴포넌트 사용 검색
rg -l "Textarea"

# focus-visible 스타일 검색 (모든 파일 대상)
rg "focus-visible"

# tailwind 설정 파일 확인
fd "tailwind.config" 

Length of output: 11017

packages/shared/src/hooks/supabase/useMemosQuery.ts (1)

23-23: 빈 배열을 기본값으로 사용하는 것은 좋은 방어적 프로그래밍 접근 방식입니다.

데이터가 없을 때 undefined 대신 빈 배열을 반환하도록 수정한 것은 매우 적절한 변경입니다. 이는 컴포넌트에서 추가적인 null 체크가 필요 없게 만들어 코드를 더 안정적이고 간결하게 만듭니다.

이 변경이 다른 컴포넌트에 미치는 영향을 확인하기 위해 다음 스크립트를 실행해보세요:

✅ Verification successful

Let me analyze the usage of useMemosQuery to verify the impact of the change.


빈 배열을 기본값으로 사용하는 변경은 안전하고 적절합니다.

이 변경이 컴포넌트들에 미치는 영향을 분석한 결과, 매우 적절한 개선으로 확인되었습니다. 현재 코드에서:

  • MemoTable은 memos가 없거나 빈 배열일 때 명시적으로 처리하고 있어 안전합니다.
  • MemoView는 옵셔널 체이닝(?.)을 사용하여 filter 작업을 수행하므로, 빈 배열이 반환되어도 안전하게 동작합니다.

따라서 undefined 대신 빈 배열을 반환하도록 한 이번 변경은 코드의 안정성을 높이는 긍정적인 개선입니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: useMemosQuery 훅을 사용하는 모든 컴포넌트 검색

# useMemosQuery 사용 패턴 검색
rg "useMemosQuery" -A 5

Length of output: 3167


Script:

#!/bin/bash
# Let's check how the memos data is being used in the components

# Check MemoTable implementation
rg "const \{ memos \}" -A 10 packages/web/src/app/\[lng\]/memos/components/legacy/MemoTable.tsx

# Check MemoView implementation
rg "const \{ memos \}" -A 10 packages/web/src/app/\[lng\]/memos/components/MemoView/index.tsx

Length of output: 780

packages/web/src/app/[lng]/memos/components/MemoSidebarTrigger/index.tsx (1)

14-24: 불필요한 div 래퍼 제거에 대한 승인

불필요한 div 래퍼를 제거한 것은 컴포넌트 구조를 개선하는 좋은 변경사항입니다.

packages/web/src/app/[lng]/memos/page.tsx (1)

11-17: 인터페이스 정의가 명확하고 적절합니다!

PageProps 인터페이스가 LanguageParams를 확장하고 URL 검색 매개변수를 위한 타입을 잘 정의하고 있습니다.

packages/web/src/app/[lng]/memos/components/MemoView/index.tsx (1)

11-14: 인터페이스와 함수 시그니처가 적절히 정의되었습니다!

선택적 프로퍼티(optional properties)와 기본값이 잘 설정되어 있어 타입 안정성이 보장됩니다.

Also applies to: 16-16

packages/web/src/app/[lng]/memos/layout.tsx (1)

13-13: 사이드바 관련 임포트 간소화가 잘 되었습니다.

불필요한 MemoSidebarTrigger 컴포넌트를 제거하고 MemoSidebar만 임포트하도록 변경한 것이 좋습니다. 이는 코드를 더 간결하고 유지보수하기 쉽게 만듭니다.

packages/web/src/app/[lng]/login/components/LoginSection/index.tsx (1)

25-25: 코드 가독성이 개선되었습니다.

번역 텍스트를 새 줄로 이동한 것이 코드의 가독성을 향상시켰습니다.

packages/web/src/app/[lng]/memos/components/MemoView/MemoItem.tsx (2)

Line range hint 24-29: 이벤트 핸들러가 개선되었습니다

이벤트 타입을 더 일반적인 HTMLElement로 변경하고 ID 검사를 추가한 것은 좋은 개선사항입니다. 코드가 더 안전하고 재사용 가능해졌습니다.


53-53: CardContent 렌더링이 개선되었습니다

조건부 렌더링을 단순화하고 whitespace 처리를 추가한 것은 좋은 개선입니다. 코드가 더 깔끔하고 가독성이 향상되었습니다.

packages/web/src/modules/i18n/locales/en/translation.json (2)

69-69: 키 조합 표현이 표준화되어 개선되었습니다!

플랫폼별 키 조합(Option/Alt, Command/Ctrl)을 {{key}}로 통일하여 유지보수성이 향상되었습니다.

Also applies to: 73-73


83-84: 메모 총계 표시 기능이 적절히 추가되었습니다!

새로운 totalMemos 항목이 한국어 번역과 일관성있게 추가되었으며, i18n 플레이스홀더 문법도 올바르게 사용되었습니다.

packages/web/src/components/ui/breadcrumb.tsx (5)

15-28: 시맨틱 마크업과 스타일링이 잘 구현되었습니다

순서가 있는 리스트(ol)를 사용한 것이 시맨틱하고 적절합니다. 반응형 디자인도 잘 고려되었습니다.

🧰 Tools
🪛 eslint

[error] 15-18: Replace ⏎··HTMLOListElement,⏎··React.ComponentPropsWithoutRef<"ol">⏎>( with HTMLOListElement,·React.ComponentPropsWithoutRef<'ol'>>(⏎··

(prettier/prettier)


[error] 19-19: Insert ··

(prettier/prettier)


[error] 20-20: Insert ··

(prettier/prettier)


[error] 21-21: Replace ···· with ······

(prettier/prettier)


[error] 22-22: Replace "flex·flex-wrap·items-center·gap-1.5·break-words·text-sm·text-muted-foreground·sm:gap-2.5" with ··'text-muted-foreground·flex·flex-wrap·items-center·gap-1.5·break-words·text-sm·sm:gap-2.5'

(prettier/prettier)


[error] 23-23: Replace ······className with ········className,

(prettier/prettier)


[error] 24-24: Replace ···· with ······

(prettier/prettier)


[error] 25-25: Insert ··

(prettier/prettier)


[error] 26-26: Insert ··

(prettier/prettier)


[error] 27-27: Replace )) with ··),⏎);

(prettier/prettier)


[error] 28-28: Replace "BreadcrumbList" with 'BreadcrumbList';

(prettier/prettier)


30-40: 구현이 간단하고 명확합니다

리스트 아이템의 스타일링이 적절하게 되어있습니다.

🧰 Tools
🪛 eslint

[error] 30-33: Replace ⏎··HTMLLIElement,⏎··React.ComponentPropsWithoutRef<"li">⏎>( with HTMLLIElement,·React.ComponentPropsWithoutRef<'li'>>(⏎··

(prettier/prettier)


[error] 34-38: Replace ··<li⏎····ref={ref}⏎····className={cn("inline-flex·items-center·gap-1.5",·className)}⏎····{...props}⏎· with ····<li·ref={ref}·className={cn('inline-flex·items-center·gap-1.5',·className)}·{...props}

(prettier/prettier)


[error] 39-39: Replace )) with ··),⏎);

(prettier/prettier)


[error] 40-40: Replace "BreadcrumbItem" with 'BreadcrumbItem';

(prettier/prettier)


60-73: 접근성 속성이 잘 구현되었습니다

현재 페이지를 나타내는 span 요소에 적절한 role과 aria 속성이 잘 적용되어 있습니다.

🧰 Tools
🪛 eslint

[error] 60-63: Replace ⏎··HTMLSpanElement,⏎··React.ComponentPropsWithoutRef<"span">⏎>( with HTMLSpanElement,·React.ComponentPropsWithoutRef<'span'>>(⏎··

(prettier/prettier)


[error] 64-64: Insert ··

(prettier/prettier)


[error] 65-65: Replace ···· with ······

(prettier/prettier)


[error] 66-66: Insert ··

(prettier/prettier)


[error] 67-67: Insert ··

(prettier/prettier)


[error] 68-68: Replace ···· with ······

(prettier/prettier)


[error] 69-69: Replace className={cn("font-normal·text-foreground" with ··className={cn('text-foreground·font-normal'

(prettier/prettier)


[error] 70-70: Insert ··

(prettier/prettier)


[error] 71-71: Insert ··

(prettier/prettier)


[error] 72-72: Replace )) with ··),⏎);

(prettier/prettier)


[error] 73-73: Replace "BreadcrumbPage" with 'BreadcrumbPage';

(prettier/prettier)


75-89: 구분자 컴포넌트가 잘 구현되었습니다

presentation role과 aria-hidden 속성을 통해 스크린 리더에서 불필요한 요소를 제외시킨 것이 좋습니다.

🧰 Tools
🪛 eslint

[error] 75-79: Replace ⏎··children,⏎··className,⏎··...props⏎}:·React.ComponentProps<"li" with ·children,·className,·. 8000 ..props·}:·React.ComponentProps<'li'

(prettier/prettier)


[error] 80-85: Replace ⏎····role="presentation"⏎····aria-hidden="true"⏎····className={cn("[&>svg]:w-3.5·[&>svg]:h-3.5",·className)}⏎····{...props}⏎·· with ·role="presentation"·aria-hidden="true"·className={cn('[&>svg]:h-3.5·[&>svg]:w-3.5',·className)}·{...props}

(prettier/prettier)


[error] 88-88: Insert ;

(prettier/prettier)


[error] 89-89: Replace "BreadcrumbSeparator" with 'BreadcrumbSeparator';

(prettier/prettier)


107-115: 모든 컴포넌트가 적절하게 export 되어있습니다

필요한 모든 컴포넌트들이 명시적으로 export 되어있습니다.

🧰 Tools
🪛 eslint

[error] 107-115: Run autofix to sort these exports!

(simple-import-sort/exports)


[error] 115-115: Insert ;

(prettier/prettier)

packages/web/src/app/[lng]/memos/components/MemoCardFooter/index.tsx (1)

5-5: UI 컴포넌트 import 추가가 적절합니다!

Button 컴포넌트의 추가는 접근성과 사용자 경험 향상에 도움이 됩니다.

packages/web/src/app/[lng]/memos/components/MemoDialog/index.tsx (2)

14-14: 타입 정의와 의존성이 적절하게 업데이트되었습니다.

인터페이스에 id 프로퍼티를 추가한 것은 컴포넌트의 용도에 맞는 적절한 변경입니다.

Also applies to: 21-23


73-75: 🛠️ Refactor suggestion

불필요한 리렌더링이 발생할 수 있습니다.

id 값이 변경될 때마다 setOpen이 호출되어 리렌더링이 발생합니다. 이는 성능에 영향을 미칠 수 있습니다.

useCallback을 사용하여 최적화하는 것을 추천드립니다:

+const handleOpenChange = useCallback((value: boolean) => {
+  setOpen(value);
+  if (!value) closeDialog();
+}, [closeDialog]);

useEffect(() => {
  setOpen(!!id);
}, [id]);

Likely invalid or redundant comment.

packages/web/src/app/[lng]/memos/components/MemoCardFooter/MemoOption.tsx (1)

105-108: 버튼 스타일 변경이 UI/UX를 개선했습니다.

ghost 변형과 icon 크기를 사용하여 더 모던한 UI로 개선되었습니다. 이벤트 전파 방지도 적절히 구현되었습니다.

@guesung guesung merged commit faa33d8 into develop Nov 29, 2024
7 of 8 checks passed
@guesung guesung deleted the feature/ux-3 branch November 29, 2024 03:21
Copy link
@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: 0

🧹 Outside diff range and nitpick comments (2)
packages/web/src/app/[lng]/memos/components/MemoDialog/index.tsx (2)

32-32: 상태 관리 로직을 개선할 수 있습니다.

현재 구현은 idopen 상태를 별도로 관리하여 불필요한 리렌더링이 발생할 수 있습니다.

다음과 같이 개선해보세요:

-const [open, setOpen] = useState(false);
+const open = !!id;

-useEffect(() => {
-  setOpen(!!id);
-}, [id]);

Also applies to: 73-75


Line range hint 42-50: 메모 저장 시 에러 처리를 추가해주세요.

현재 구현에서는 메모 저장 실패 시의 에러 처리가 누락되어 있습니다.

다음과 같이 개선해보세요:

 mutateMemoPatch(
   { id: Number(id), memoRequest: { memo: watch('memo') } },
   {
     onSuccess: () => toast({ title: t('toastTitle.memoEdited') }),
+    onError: (error) => toast({
+      title: t('toastTitle.error'),
+      description: t('toastDescription.memoEditFailed'),
+      variant: 'destructive'
+    }),
   },
 );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 077f5be and ae22101.

📒 Files selected for processing (4)
  • packages/web/src/app/[lng]/memos/components/MemoDialog/index.tsx (4 hunks)
  • packages/web/src/app/[lng]/memos/components/MemoSidebar/SidebarMenuItemAddCategory.tsx (2 hunks)
  • packages/web/src/modules/i18n/locales/en/translation.json (3 hunks)
  • packages/web/src/modules/i18n/locales/ko/translation.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/web/src/app/[lng]/memos/components/MemoSidebar/SidebarMenuItemAddCategory.tsx
  • packages/web/src/modules/i18n/locales/en/translation.json
  • packages/web/src/modules/i18n/locales/ko/translation.json
🔇 Additional comments (3)
packages/web/src/app/[lng]/memos/components/MemoDialog/index.tsx (3)

21-23: 인터페이스 변경이 적절합니다!

타입 안전성이 향상되었고 컴포넌트의 props가 명확해졌습니다.

Also applies to: 25-25


79-80: Dialog 컴포넌트의 접근성 문제가 여전히 존재합니다.

이전 리뷰에서 지적된 접근성 관련 속성이 아직 추가되지 않았습니다.


99-106: 버튼 레이아웃과 국제화가 잘 구현되었습니다!

  • Flex 레이아웃을 사용하여 버튼 배치가 깔끔합니다
  • 번역 키를 적절히 사용하여 국제화가 잘 되어있습니다

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
0