Skip to content
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

[Fix] api 스키마 수정 사항 반영 #134

Merged
merged 6 commits into from
Feb 17, 2025
Merged

Conversation

kongnayeon
Copy link
Member

@kongnayeon kongnayeon commented Feb 17, 2025

관련 이슈

#133

변경 사항

  • 결과 수정 > 상세 페이지에서 나는 빌드 에러를 수정했습니다!
  • deprecated 된 로직들 제거했습니다
  • 자잘하게 기능이 동작하지 않던 부분들 수정했습니다

레퍼런스

Summary by CodeRabbit

  • 신규 기능

    • 선택된 콘텐츠 항목이 부드럽게 스크롤되어 중앙에 표시됩니다.
    • 게시물 관리 인터페이스에 상태 업데이트 드롭다운과 삭제 확인 모달이 추가되었습니다.
    • 파일 업로드 시 최대 4개 파일, 5MB 제한 등의 검증 로직이 강화되었습니다.
  • 스타일

    • 편집 페이지 레이아웃이 개선되고 오버플로우 속성이 업데이트되었습니다.
    • 전역 스크롤바가 사용자 지정 스타일로 새롭게 디자인되었습니다.
  • 리팩토링

    • 데이터 불러오기 및 드래그 앤 드롭 기능이 개선되어 보다 원활한 사용자 경험을 제공합니다.

@kongnayeon kongnayeon self-assigned this Feb 17, 2025
Copy link

coderabbitai bot commented Feb 17, 2025

Walkthrough

이번 PR은 다양한 프론트엔드 컴포넌트에 대한 업데이트를 포함합니다.
주요 변경사항으로는 ContentItem 컴포넌트에 클라이언트 지시어 추가 및 스크롤 동작 구현,
스타일 파일 및 CSS-in-JS 관련 업데이트와 삭제, 쿼리 및 변이 훅의 네이밍 변경 및 리팩터링,
포스트 조회/수정/삭제 기능 개선, 드롭다운 및 아이콘 컴포넌트 기능 추가 등이 있습니다.
전반적으로 포스트 관리 및 UI 구성 요소 로직이 업데이트되었습니다.

Changes

파일 변경 요약
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/_components/ContentItem/ContentItem.tsx 클라이언트 지시어 추가, useEffectuseRef 적용해 선택 시 부드럽게 스크롤되는 기능 추가
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/{EditDetail.css.ts,EditDetail.tsx,page.css.ts,page.tsx} EditDetail 관련 파일: CSS 파일 삭제 및 임포트 경로 변경, overflow 속성 추가, 서버 fetch 옵션 함수 수정
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/{EditPost.css.ts,EditPost.tsx} EditPost 컴포넌트 업데이트: 새 스타일 추가, 포스트 조회/삭제/상태 변경 로직 및 모달, 드롭다운 기능 추가
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPromptField/EditPromptField.tsx 쿼리 및 변이 훅 업데이트, 포스트 배열 평탄화와 폼 제출 시 로직 수정
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/{EditSidebar.css.ts,EditSidebar.tsx} EditSidebar: 스타일 및 데이터 조회, drag-and-drop 키 업데이트 및 포스트 수정 로직 간소화
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/PostEditor.tsx 쿼리 변경 및 파일 검증 로직 강화, 변이 훅 변경 및 로딩 상태 관리 간소화
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_hooks/useAdjacentPosts.tsx 포스트 데이터 구조 변경에 따른 후보 포스트 추출 로직 수정 및 안전한 라우팅 처리
apps/web/src/app/globals.css
apps/web/src/components/common/DNDController/context/DndContext.tsx
글로벌 스타일 수정 (스크롤바 커스터마이징, 기본 마진/패딩 리셋) 및 드래그-앤-드롭 센서 지연 제거
Mutation/Query Hooks
useModifyPostsMutation.ts, usePatchPromptMutation.ts, useUpdatePostMutation.ts, useUpdatePromptMutation.ts, useGroupPostsQuery.ts
기존 mutation 및 query 훅 삭제/대체, 네이밍 변경 및 인터페이스 업데이트
UI Dropdown 관련
packages/ui/src/components/Dropdown/{Dropdown.css.ts,Dropdown.tsx,DropdownContent.tsx,DropdownIcon.tsx}
드롭다운 아이콘 스타일 추가, Dropdown 컴포넌트에 Icon 프로퍼티와 타입 추가, CSS 클래스 순서 변경, 신규 DropdownIcon 컴포넌트 도입

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant CI as ContentItem
    participant Browser as 브라우저
    User->>CI: 항목 선택 (isSelected=true)
    CI->>CI: useEffect 실행하여 itemRef 확인
    CI->>Browser: scrollIntoView (smooth, center)
Loading
sequenceDiagram
    participant User as 사용자
    participant EP as EditPost 컴포넌트
    participant Modal as 모달 컴포넌트
    participant API as API 서버
    User->>EP: 삭제 버튼 클릭
    EP->>Modal: useModal 호출하여 확인 모달 표시
    User->>Modal: 삭제 확인 응답
    Modal->>EP: 사용자 확인 전달
    EP->>API: handleDeletePost 실행, POST 삭제 요청 전송
    API-->>EP: 삭제 성공 응답
Loading

Possibly related PRs

Suggested reviewers

  • minseong0324

Poem

나는 작은 토끼, 코드를 뛰노네,
기능이 늘어나도 귀는 쫑긋!
스크롤이 부드럽게 춤추며
모달과 드롭다운, 모두 함께하네.
지저귀는 코드 숲 속에서 🎶
달빛 아래, 버그는 사라지고
행복한 배포, Hop-Hop!

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.tsx

Oops! Something went wrong! :(

ESLint: 9.17.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@repo/eslint-config' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/_components/ContentItem/ContentItem.tsx

Oops! Something went wrong! :(

ESLint: 9.17.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@repo/eslint-config' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.tsx

Oops! Something went wrong! :(

ESLint: 9.17.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@repo/eslint-config' imported from /eslint.config.mjs
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

  • 15 others
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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: 3

🧹 Nitpick comments (28)
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.tsx (1)

9-13: 서버 fetch 옵션 구성이 개선되었습니다.

객체 형태로 파라미터를 전달하는 방식으로 변경되어 코드의 가독성과 유지보수성이 향상되었습니다.

다만, 타입 안정성을 위해 다음과 같은 개선을 제안드립니다:

   const serverFetchOptions = getAllPostsQueryOptions({
-    agentId: params.agentId,
-    postGroupId: params.postGroupId,
+    agentId: Number(params.agentId),
+    postGroupId: Number(params.postGroupId),
     tokens,
   });
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.css.ts (2)

3-8: 스타일 속성이 적절하게 추가되었습니다.

overflow: 'hidden' 속성 추가로 컨테이너 영역을 벗어나는 콘텐츠를 제어할 수 있게 되었습니다. 이는 스크롤 관련 문제를 해결하는데 도움이 될 것으로 보입니다.

하지만 반응형 디자인을 고려하여 다음과 같은 개선을 제안드립니다:

 export const editDetailPage = style({
   display: 'flex',
-  height: '100vh',
+  minHeight: '100vh',
   flexShrink: 0,
   overflow: 'hidden',
 });

이렇게 수정하면 모바일 환경에서 키보드가 올라올 때 발생할 수 있는 레이아웃 이슈를 방지할 수 있습니다.


10-14: flexColumn 스타일의 용도를 명확히 해주세요.

현재 flexColumn이라는 이름은 스타일의 목적보다는 구현 방식을 설명하고 있습니다. 이 스타일의 실제 사용 목적을 반영하는 더 명확한 이름을 사용하면 좋을 것 같습니다.

-export const flexColumn = style({
+export const contentContainer = style({
   display: 'flex',
   width: '100%',
   justifyContent: 'center',
 });
apps/web/src/store/mutation/useUpdatePostMutation.ts (1)

36-40: 에러 처리 개선이 필요합니다.

현재 구현은 Error 인스턴스만 처리하고 있어, 네트워크 오류나 다른 타입의 오류는 적절히 처리되지 않을 수 있습니다.

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

    onError: (error) => {
-      if (error instanceof Error) {
-        toast.error(error.message);
-      }
+      const errorMessage = error instanceof Error 
+        ? error.message 
+        : '게시물 업데이트 중 오류가 발생했습니다.';
+      toast.error(errorMessage);
    },
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/_components/ContentItem/ContentItem.tsx (1)

68-74: 스크롤 동작이 잘 구현되었으나, 성능 최적화를 고려해보세요.

스크롤 동작이 매끄럽게 구현되었습니다. 다만, 성능 최적화를 위해 다음과 같은 개선을 고려해보시면 좋겠습니다:

 useEffect(() => {
   if (isSelected && itemRef.current) {
-    itemRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
+    // requestAnimationFrame을 사용하여 렌더링 성능 최적화
+    requestAnimationFrame(() => {
+      itemRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' });
+    });
   }
 }, [isSelected]);
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.css.ts (2)

37-47: 드롭다운 트리거 스타일에 접근성 개선이 필요합니다.

스타일이 잘 구성되어 있으나, 접근성 향상을 위한 개선사항이 있습니다:

 export const chipDropdownTrigger = style({
   display: 'flex',
   alignItems: 'center',
   gap: '0.8rem',
   padding: '1.2rem 0.8rem',
   borderRadius: vars.borderRadius[12],
+  cursor: 'pointer',
+  outline: 'none',
 
   ':hover': {
     backgroundColor: vars.colors.grey25,
   },
+  ':focus-visible': {
+    boxShadow: `0 0 0 2px ${vars.colors.grey100}`,
+  },
 });

49-53: 드롭다운 아이템의 상호작용 상태를 추가해주세요.

사용자 경험 향상을 위해 hover와 focus 상태를 추가하는 것이 좋습니다:

 export const dropdownItem = style({
   display: 'flex',
   alignItems: 'center',
   gap: '1rem',
+  padding: '0.8rem 1.2rem',
+  cursor: 'pointer',
+  borderRadius: vars.borderRadius[8],
+
+  ':hover': {
+    backgroundColor: vars.colors.grey25,
+  },
+  ':focus-visible': {
+    outline: 'none',
+    backgroundColor: vars.colors.grey25,
+  },
 });
apps/web/src/store/mutation/useUpdatePromptMutation.ts (2)

13-17: 타입 정의를 더 명확하게 개선할 수 있습니다.

현재 구현도 잘 되어있지만, 타입 안전성을 더 높이기 위해 다음과 같은 개선을 제안합니다:

-export type MutationUpdatePrompt = {
-  agentId: IdParams['agentId'];
-  postGroupId: IdParams['postGroupId'];
-  postId?: IdParams['postId'];
-};
+export type MutationUpdatePrompt = Pick<IdParams, 'agentId' | 'postGroupId'> & {
+  postId?: IdParams['postId'];
+};

이렇게 하면:

  • Pick 유틸리티 타입을 사용하여 더 간결하고 유지보수하기 쉬운 코드가 됩니다
  • IdParams 타입이 변경되어도 자동으로 반영됩니다

32-41: API 엔드포인트 구성의 타입 안전성을 개선할 수 있습니다.

현재 구현은 잘 작동하지만, 다음과 같은 개선 사항을 제안합니다:

  1. API 엔드포인트를 상수로 정의하여 타입 안전성을 높이고 재사용성을 개선:
const API_ENDPOINTS = {
  updateEntirePrompt: (params: Pick<MutationUpdatePrompt, 'agentId' | 'postGroupId'>) =>
    `agents/${params.agentId}/post-groups/${params.postGroupId}/posts/prompt`,
  updateSinglePrompt: (params: Required<MutationUpdatePrompt>) =>
    `agents/${params.agentId}/post-groups/${params.postGroupId}/posts/${params.postId}/prompt`
} as const;
  1. 이를 활용한 리팩토링:
-      const endpoint = isEntire
-        ? `agents/${agentId}/post-groups/${postGroupId}/posts/prompt`
-        : `agents/${agentId}/post-groups/${postGroupId}/posts/${postId}/prompt`;
+      const endpoint = isEntire
+        ? API_ENDPOINTS.updateEntirePrompt({ agentId, postGroupId })
+        : API_ENDPOINTS.updateSinglePrompt({ agentId, postGroupId, postId: postId! });

이렇게 하면:

  • 엔드포인트 문자열의 타입 안전성이 향상됩니다
  • 재사용이 용이해집니다
  • IDE의 자동완성 지원이 개선됩니다
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_hooks/useAdjacentPosts.tsx (4)

18-20: 성능 최적화를 위한 제안

Object.values(posts).flat()는 매번 새로운 배열을 생성합니다. 데이터가 큰 경우 성능에 영향을 줄 수 있습니다.

다음과 같이 캐시를 활용하는 방법을 고려해보세요:

  const allPosts = useMemo(() => {
-   return Object.values(posts).flat();
+   const cache = new Map();
+   const key = Object.keys(posts).join(',');
+   if (!cache.has(key)) {
+     cache.set(key, Object.values(posts).flat());
+   }
+   return cache.get(key);
  }, [posts]);

8-16: TODO 주석 처리 필요

PostStatusType에 대한 TODO 주석이 있습니다. 이 부분을 구현하여 타입 안정성을 개선할 필요가 있습니다.

이 부분의 구현을 도와드릴까요?


22-54: 코드 가독성 개선 제안

포스트 정렬 및 필터링 로직이 복잡합니다. 이를 별도의 유틸리티 함수로 분리하면 가독성과 유지보수성이 향상될 것 같습니다.

다음과 같은 구조를 제안드립니다:

const sortByStatusAndOrder = (a: Post, b: Post, statusPriority: Record<Post['status'], number>) => {
  if (a.status !== b.status) {
    return statusPriority[a.status] - statusPriority[b.status];
  }
  return b.displayOrder - a.displayOrder;
};

const filterPreviousPosts = (post: Post, currentPost: Post, statusPriority: Record<Post['status'], number>) => {
  if (post.status === currentPost.status) {
    return post.displayOrder < currentPost.displayOrder;
  }
  return statusPriority[post.status] > statusPriority[currentPost.status];
};

59-69: URL 처리 개선 제안

현재 URL 생성이 문자열 연결로 이루어지고 있습니다. 더 안전한 URL 처리를 위해 개선이 필요합니다.

다음과 같은 방식을 고려해보세요:

  const routePreviousPost = () => {
    if (previousPost) {
-     router.push(`?postId=${previousPost.id}`);
+     const url = new URL(window.location.href);
+     url.searchParams.set('postId', previousPost.id);
+     router.push(url.search);
    }
  };
packages/ui/src/components/Dropdown/DropdownIcon.tsx (1)

13-19: 클래스명 결합 로직을 개선할 수 있습니다.

템플릿 리터럴 내의 불필요한 공백을 제거하고, 조건부 클래스 결합을 위해 clsx나 classnames 같은 유틸리티 사용을 고려해보세요.

-        className={`${props.className} ${dropdownIcon({ open: isOpen })}`}
+        className={clsx(props.className, dropdownIcon({ open: isOpen }))}
packages/ui/src/components/Dropdown/Dropdown.css.ts (1)

59-72: 아이콘 회전 애니메이션이 잘 구현되어 있습니다.

transition과 transform을 사용한 아이콘 회전 구현이 적절합니다. 다만 다음과 같은 개선사항을 고려해보세요:

  1. 성능 최적화를 위해 transform 속성에 대해서만 transition을 적용
  2. 하드코딩된 시간 값을 변수로 추출하여 재사용성 향상
  base: {
-    transition: 'transform 0.3s ease',
+    transition: 'transform var(--transition-duration, 0.3s) ease',
  },
packages/ui/src/components/Dropdown/DropdownContent.tsx (2)

30-32: 클래스명 결합 로직을 개선할 수 있습니다.

템플릿 리터럴을 사용한 클래스명 결합이 복잡해 보입니다. clsx나 classnames 같은 유틸리티를 사용하여 가독성을 개선할 수 있습니다.

-    const contentClassName = `${dropdownContent} ${positionAbove ? dropdownContentAbove : ''} ${
-      align === 'right' ? dropdownContentRight : dropdownContentLeft
-    } ${className}`;
+    const contentClassName = clsx(
+      dropdownContent,
+      positionAbove && dropdownContentAbove,
+      align === 'right' ? dropdownContentRight : dropdownContentLeft,
+      className
+    );

64-64: 불필요한 템플릿 리터럴 사용을 제거하세요.

단일 변수를 템플릿 리터럴로 감싸는 것은 불필요합니다.

-        className={`${contentClassName}`}
+        className={contentClassName}
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPromptField/EditPromptField.tsx (3)

37-40: 상태 관리 최적화가 필요합니다.

posts 배열을 매번 flat()하고 filter()하는 것은 성능에 영향을 줄 수 있습니다. 다음과 같은 최적화를 제안합니다:

-  const editingPosts = Object.values(posts)
-    .flat()
-    .filter((post) => post.status === 'EDITING')
-    .map((post) => post.id);
+  const editingPosts = posts.EDITING?.map(post => post.id) ?? [];

49-67: TODO 주석 처리가 필요합니다.

"제거 예정"이라고 표시된 TODO 주석이 있는 코드의 처리 계획을 명확히 해주시기 바랍니다.

이 부분의 리팩토링이 필요하시다면 도움을 드릴 수 있습니다.


69-77: 뮤테이션 로직 단순화가 필요합니다.

뮤테이션 호출 로직을 더 간단하게 리팩토링할 수 있습니다:

-    if (isEntire) {
-      updatePrompt({ ...data, postsId: editingPostIds });
-    } else {
-      updatePrompt({ ...data });
-    }
+    updatePrompt({
+      ...data,
+      ...(isEntire && { postsId: editingPostIds })
+    });
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/PostEditor.tsx (2)

52-52: 사용되지 않는 컨텍스트 변수 확인 요청
setLoadingPosts가 선언되었지만, 현재 코드에는 사용되는 부분이 보이지 않습니다. 불필요한 코드라면 삭제를 검토하시고, 추후 사용 예정이다면 관련 로직이나 주석을 추가해 관리하면 좋습니다.


134-148: 파일 업로드 유효성 검증 및 제한 로직
최대 업로드 개수, 파일 크기 제한 등을 명시적으로 처리해 사용자 오류를 예방하는 점이 좋습니다. 다만, 서버에서도 같은 제한을 처리하지 않으면 클라이언트 단에서만 통제가 이뤄질 수 있으니, 필요하다면 서버 검증 로직도 확인해 보세요.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.tsx (3)

38-38: Context 사용 시 성능 영향 검토
DetailPageContextloadingPosts 사용으로 화면 갱신이 자주 발생할 수 있으므로, 큰 규모의 데이터나 잦은 업데이트가 있는지 검토하세요. 현재 코드 상에서는 큰 문제 없어 보입니다.


43-46: 에러/로딩 상태 처리 누락 가능성
useGetAllPostsQuery로부터 받은 posts가 아직 로딩 중이거나 에러일 때 화면 처리가 누락될 수 있습니다. isLoading, error 등을 활용해 안전하게 분기 처리하는 것을 권장합니다.


279-279: 쿼리 중복 호출 시 성능 고려
이미 상단에서 useGetAllPostsQuery를 사용 중인데 다시 호출하여 동일한 데이터에 접근하므로, 중복 호출이 성능에 영향을 주지 않는지 검토하십시오. 혹은 상위에서 한 번만 가져온 뒤 props로 전달하는 방식을 고려해볼 수 있습니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.tsx (3)

98-108: 상태 변경 후 처리 로직 제안
handleChipClick 함수에서 상태만 업데이트하고 있는데, 성공/실패에 따른 추가 로직(알림, 롤백 등)도 고려해 보시면 좋겠습니다.


127-143: Dropdown 내 단일 항목 value
Dropdown.Itemvalue가 동일한 "option1"로 설정되어 있어 추후 확장 시 충돌이 발생할 수 있으니, 구분이 필요한 경우 서로 다른 value를 부여해 주세요.


160-189: Chip 상태 드롭다운 내 value 처리
각 항목이 모두 "option1"이라는 같은 값을 사용하고 있어, 선택 구분이 필요한 경우 다른 value를 사용하면 좋겠습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43fd365 and b906ebc.

📒 Files selected for processing (23)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/_components/ContentItem/ContentItem.tsx (3 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.css.ts (0 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.tsx (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.css.ts (2 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.tsx (3 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPromptField/EditPromptField.tsx (3 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.css.ts (2 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.tsx (8 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/PostEditor.tsx (3 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_hooks/useAdjacentPosts.tsx (4 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.css.ts (1 hunks)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.tsx (1 hunks)
  • apps/web/src/app/globals.css (3 hunks)
  • apps/web/src/components/common/DNDController/context/DndContext.tsx (0 hunks)
  • apps/web/src/store/mutation/useModifyPostsMutation.ts (0 hunks)
  • apps/web/src/store/mutation/usePatchPromptMutation.ts (0 hunks)
  • apps/web/src/store/mutation/useUpdatePostMutation.ts (1 hunks)
  • apps/web/src/store/mutation/useUpdatePromptMutation.ts (2 hunks)
  • apps/web/src/store/query/useGroupPostsQuery.ts (0 hunks)
  • packages/ui/src/components/Dropdown/Dropdown.css.ts (2 hunks)
  • packages/ui/src/components/Dropdown/Dropdown.tsx (2 hunks)
  • packages/ui/src/components/Dropdown/DropdownContent.tsx (2 hunks)
  • packages/ui/src/components/Dropdown/DropdownIcon.tsx (1 hunks)
💤 Files with no reviewable changes (5)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.css.ts
  • apps/web/src/components/common/DNDController/context/DndContext.tsx
  • apps/web/src/store/mutation/useModifyPostsMutation.ts
  • apps/web/src/store/mutation/usePatchPromptMutation.ts
  • apps/web/src/store/query/useGroupPostsQuery.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/EditDetail.tsx
🔇 Additional comments (42)
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/page.tsx (1)

5-5: 새로운 쿼리 옵션 import 변경이 적절합니다.

groupPostsQueryQueryOptions에서 getAllPostsQueryOptions로의 마이그레이션이 잘 이루어졌습니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.css.ts (2)

5-7: 사이드바 래퍼의 스타일 개선이 적절합니다!

높이를 fit-content로 변경하고 overflow: hidden을 추가한 것은 불필요한 스크롤을 방지하고 컨텐츠를 더 잘 제어할 수 있게 해줍니다.


21-21: 뷰포트 높이가 작을 때를 고려해 주세요.

calc(100vh - 8rem)는 브레드크럼의 높이를 고려한 적절한 계산이지만, 작은 화면에서 컨텐츠가 잘릴 수 있습니다.

다음과 같은 개선을 고려해보세요:

-  height: 'calc(100vh - 8rem)',
+  minHeight: 'calc(100vh - 8rem)',
+  height: 'fit-content',
apps/web/src/store/mutation/useUpdatePostMutation.ts (1)

6-18: 인터페이스 정의가 잘 구성되어 있습니다!

타입 정의가 명확하고, 옵셔널 필드 처리가 적절하게 되어있습니다. updateType의 유니온 타입 사용으로 타입 안정성이 잘 보장되어 있습니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/_components/ContentItem/ContentItem.tsx (2)

1-16: 클라이언트 사이드 기능 추가에 필요한 적절한 설정이 이루어졌습니다!

'use client' 지시어와 필요한 훅들의 import가 올바르게 추가되었습니다.


77-77: ref 병합이 적절하게 구현되었습니다!

mergeRefs를 사용하여 외부 ref와 내부 ref를 효과적으로 결합했습니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.css.ts (1)

1-2: 테마 변수와 스타일 유틸리티 임포트가 적절히 추가되었습니다!

새로 추가된 스타일에 필요한 varsglobalStyle 임포트가 잘 구성되어 있습니다.

apps/web/src/app/globals.css (3)

18-19: 리셋된 마진 및 패딩 값 적용 확인
html, body 요소에 대해 margin: 0;padding: 0;을 명시적으로 설정하여 브라우저 기본 스타일로 인한 불필요한 간격 문제를 해결한 점이 좋습니다.


39-40: 스크롤바 기본 스타일 지정
전체 선택자 *에 추가한 scrollbar-width: thin;scrollbar-color: var(--color-grey-200, #c1ceda) transparent; 설정은 Firefox와 같이 해당 속성을 지원하는 브라우저에서 일관된 스크롤바 디자인을 제공할 수 있게 합니다.


53-76: 웹킷 기반 브라우저용 스크롤바 커스터마이징
::-webkit-scrollbar 및 관련 선택자들을 활용하여 스크롤바의 너비, 트랙, thumb 그리고 hover 상태를 세밀하게 조정한 점이 인상적입니다. 각 섹션에 대한 주석도 상세하여 유지보수 및 향후 수정 시 가독성을 높여줍니다.

apps/web/src/store/mutation/useUpdatePromptMutation.ts (1)

7-11: 인터페이스 변경이 잘 구현되었습니다!

isEntirepostsId를 선택적 프로퍼티로 만든 것은 API의 유연성을 높이는 좋은 접근입니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_hooks/useAdjacentPosts.tsx (1)

1-1: 타입 변경이 적절하게 이루어졌습니다!

Post[]에서 PostsByStatus로의 타입 변경은 포스트를 상태별로 그룹화하는 새로운 API 스키마와 잘 부합합니다.

Also applies to: 5-5

packages/ui/src/components/Dropdown/DropdownIcon.tsx (3)

1-7: 클라이언트 컴포넌트 선언과 타입 정의가 적절합니다.

'use client' 지시어와 필요한 의존성들이 올바르게 가져와졌습니다.


9-21: forwardRef 구현이 잘 되어있습니다.

SVG 요소에 대한 ref 전달과 컨텍스트 사용이 적절하게 구현되었습니다.


23-24: displayName 설정이 적절합니다.

디버깅을 위한 컴포넌트 이름 지정이 잘 되어있습니다.

packages/ui/src/components/Dropdown/Dropdown.tsx (3)

7-7: 새로운 DropdownIcon 컴포넌트 import가 적절합니다.

컴포넌트 구조에 맞게 import가 추가되었습니다.


44-44: Icon 서브컴포넌트 추가가 잘 되었습니다.

Dropdown 복합 컴포넌트에 Icon이 적절하게 통합되었습니다.


51-51: 타입 익스포트가 적절합니다.

DropdownIconProps 타입이 올바르게 익스포트되었습니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPromptField/EditPromptField.tsx (2)

5-5: 임포트 변경사항이 적절합니다!

폼 핸들링과 API 통합 방식의 변경이 일관성 있게 반영되었습니다.

Also applies to: 12-13


32-35:

❓ Verification inconclusive

API 통합 검증이 필요합니다.

쿼리와 뮤테이션 훅의 변경사항이 새로운 API 스키마와 일치하는지 확인이 필요합니다.

Also applies to: 42-47


🏁 Script executed:

#!/bin/bash
# API 스키마 변경사항 검증
rg -A 5 "useGetAllPostsQuery|useUpdatePromptMutation" --type typescript

Length of output: 108


API 통합 검증 필요

현재 PR에서 EditPromptField.tsx 파일(32-35, 그리고 42-47 라인) 내에 있는 쿼리와 뮤테이션 훅(useGetAllPostsQuery, useUpdatePromptMutation)이 새로운 API 스키마와 일치하는지 확인해야 합니다. 기존에 실행한 검증 스크립트가 파일 유형 인식 문제(typescript → 인식되지 않음)로 인해 올바른 결과를 제공하지 못했습니다.

아래와 같이 새 스크립트를 사용하여 .tsx 파일에 대해 검색해주시기 바랍니다:

스크립트 실행 결과를 바탕으로 API 통합 변경 사항이 올바르게 반영되었는지 최종적으로 매뉴얼 검증 부탁드립니다.


🏁 Script executed:

[run_scripts]
#!/bin/bash
# API 스키마 변경사항 검증: tsx 파일에서 검색
rg -A 5 "useGetAllPostsQuery|useUpdatePromptMutation" -g "*.tsx"

Length of output: 15340


API 통합 검증 재요청

  • 파일 apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPromptField/EditPromptField.tsx에서는 useGetAllPostsQueryuseUpdatePromptMutation이 새로운 API 스키마에 맞게 사용되고 있는 것으로 보입니다.
  • 다만, 다른 관련 파일(예: /Edit.tsx에서는 useUpdatePostsMutation이 사용됨)과의 차이가 있으므로, 전체 API 스키마와 비교 및 일관성 검증이 필요합니다.
  • 향후 API 문서와 비교하여 모든 변경사항이 올바르게 반영되었는지 추가 검증 부탁드립니다.
apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/PostEditor/PostEditor.tsx (6)

23-23: 유틸 함수 임포트 확인
validateFiles를 활용한 파일 검증 로직 강화가 이뤄지고 있어, 파일 업로드 시 에러 처리를 좀 더 체계적으로 관리할 수 있게 되었습니다.


25-25: useUpdatePostMutation 임포트 추가
기존 useModifyPostMutation 대신 새 뮤테이션 훅을 가져와 사용하는 방식으로 보입니다. 변경된 API 스펙에 맞춰 적절히 수정된 것 같습니다.


28-28: useGetAllPostsQuery 사용으로 쿼리 교체
이전 useGroupPostsQuery 사용을 대체하는 로직으로 추측됩니다. 다른 파일들과의 연동을 고려할 때, 새로운 쿼리 훅으로 일관성 있게 교체된 점이 좋습니다.


29-29: 토스트 훅 도입
에러 메시지나 안내를 toast로 노출해 사용자 경험을 향상시키는 접근이 좋습니다.


32-32: useToast 초기화 확인
toast 객체를 통해 업로드 제한, 파일 형식 에러 등 다양한 상황에서 유연하게 알림을 띄우고 있어 유용해 보입니다.


56-56: 새 뮤테이션 훅 사용
이전 뮤테이션 훅을 대체해 useUpdatePostMutation에 맞춘 호출 로직이 적용되었습니다. API 측과 정상 동작 여부만 다시 한번 확인해 주세요.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditSidebar/EditSidebar.tsx (6)

32-34: 새로운 훅 임포트 확인 및 불필요 코드 제거 검토 권장
useGetAllPostsQuery, useUpdatePostsMutation, PostId를 새롭게 가져오는 점은 변경 의도에 잘 맞게 보입니다. 다만 기존 useGroupPostsQuery 등 사용되지 않는 관련 코드를 프로젝트 전체에서 모두 제거했는지 확인해보시길 권장합니다.


48-48: DndController로부터 가져오는 훅 사용
getItemsByStatus를 통해 항목을 상태별로 구분하는 로직은 가독성과 유지보수성을 높여주어 적절해 보입니다.


73-78: 인자 타입 명확화 및 예외 처리 고려
handleClickidPostId로 한정해 명시적인 의도가 드러납니다. 다만 유효하지 않은 id에 대한 예외 처리가 필요한지 검토가 필요합니다.


172-172: 수정 로직 콜백 깔끔한 재사용
onModify 콜백에서 동일한 handleClick 함수를 활용해 수정 로직을 일관성 있게 유지한 점이 좋습니다.

Also applies to: 211-211, 252-252


274-277: 새로운 뮤테이션 훅 전환
useUpdatePostsMutation으로 교체하여 최신 API 스펙에 맞춘 점이 확인됩니다. 뮤테이션 호출 시 에러 처리와 로딩 상태 표시도 함께 고려하면 좋겠습니다.


286-303: 드래그 종료 시점 로직의 예외/성능 처리 점검
DndControllerinitialItemskey를 설정할 때 posts.data.postsundefined인 경우를 대비한 방어 로직이 필요해 보입니다. 또한 onDragEnd에서 바로 updatePosts를 호출하면 서버 부하가 증가할 수 있으니, 배치 업데이트 등 최적화 방안을 고려해보시길 권장합니다.

apps/web/src/app/(prompt)/edit/[agentId]/[postGroupId]/detail/_components/EditPost/EditPost.tsx (10)

4-11: 스타일 import 확인
해당 스타일 클래스들을 모두 적절하게 활용하고 있어 문제 없어 보입니다.


19-19: 새로운 쿼리 훅 사용
이전 useGroupPostsQuery를 대체하여 useGetAllPostsQuery를 사용하고 있으니, 마이그레이션이 전부 완료되었는지 확인해 주세요.


21-23: Dropdown, Icon, useDeletePostMutation import
새로 import된 항목들이 이후 로직에서 정상적으로 사용되고 있어 특별한 문제는 없어 보입니다.


24-29: 추가된 훅 및 컴포넌트 import
useModal, Modal, Chip, PostStatus, ReactNode, useUpdatePostsMutation 등이 적절하게 도입되었으며, 정상적으로 활용되고 있습니다.


31-53: CHIP_DROPDOWN 상태 매핑
PostStatus 열거형에 매핑된 상태가 3가지인데, 추가 상태가 필요하거나 누락된 상태가 없는지 확인해 보세요. 현재 구성은 문제 없어 보입니다.


57-57: 모달 훅 사용
useModal 훅을 통해 모달 제어 로직을 도입한 점이 좋습니다.


70-71: useAdjacentPosts 인자 확인
posts?.data?.posts가 없는 상황에서 훅 내부에 예외 처리가 필요한지 검토해 주세요.


73-76: useDeletePostMutation 사용
삭제 뮤테이션이 잘 연결되었으며, 에러 처리나 성공 후 UI 갱신 등의 추가 로직이 필요한지 고려해 보세요.


78-91: 삭제 확인 모달 로직
삭제 전에 모달을 통해 사용자에게 재확인을 받는 로직이 적절합니다. 삭제 완료 후 페이지 이동이나 목록 재로드가 필요한지 검토해 주세요.


93-96: useUpdatePostsMutation 사용
포스트 수정 뮤테이션이 잘 연결되었습니다. 에러 처리나 성공 시 UI 반영이 필요한지 고려해 보세요.

Comment on lines +36 to +42
const { data: posts } = useGetAllPostsQuery({
agentId: Number(agentId),
postGroupId: Number(postGroupId),
});
const post = Object.values(posts?.data?.posts)
.flat()
.find((post) => post.id === Number(postId));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Object.values(...) 사용 시 안전성 문제
posts?.data?.postsundefined일 경우 Object.values(undefined)에서 런타임 에러가 발생할 수 있습니다.

아래와 같이 ?? {}를 사용해 undefined 방지 로직을 추가해주세요:

- const post = Object.values(posts?.data?.posts)
-   .flat()
-   .find((post) => post.id === Number(postId));
+ const post = Object.values(posts?.data?.posts ?? {})
+   .flat()
+   .find((p) => p.id === Number(postId));
📝 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
const { data: posts } = useGetAllPostsQuery({
agentId: Number(agentId),
postGroupId: Number(postGroupId),
});
const post = Object.values(posts?.data?.posts)
.flat()
.find((post) => post.id === Number(postId));
const { data: posts } = useGetAllPostsQuery({
agentId: Number(agentId),
postGroupId: Number(postGroupId),
});
const post = Object.values(posts?.data?.posts ?? {})
.flat()
.find((p) => p.id === Number(postId));

Comment on lines +61 to +63
const defaultValue = Object.values(posts?.data.posts)
.flat()
.find((post) => post.id === Number(postParam))?.status;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

posts?.data.posts 접근 시 안전성 보강 필요
posts?.data.postsundefined일 경우 Object.values(posts?.data.posts) 호출 시 런타임 에러가 발생할 수 있습니다. 옵셔널 체이닝 이후에도 안전하게 처리할 로직을 추가하시길 권장합니다.

예시 수정안:

- const defaultValue = Object.values(posts?.data.posts)
-   .flat()
-   .find((post) => post.id === Number(postParam))?.status;
+ const defaultValue = posts?.data?.posts
+   ? Object.values(posts.data.posts)
+       .flat()
+       .find((p) => p.id === Number(postParam))?.status
+   : undefined;
📝 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
const defaultValue = Object.values(posts?.data.posts)
.flat()
.find((post) => post.id === Number(postParam))?.status;
const defaultValue = posts?.data?.posts
? Object.values(posts.data.posts)
.flat()
.find((p) => p.id === Number(postParam))?.status
: undefined;

Comment on lines 61 to +68
const postId = searchParams.get('postId');
const { data } = useGroupPostsQuery(Number(agentId), Number(postGroupId));
const post = data?.data?.posts.find((post) => String(post.id) === postId);

console.log('dfddf', data, post);
console.log('postId:', postId, 'Converted:', Number(postId));
const { data: posts } = useGetAllPostsQuery({
agentId: Number(agentId),
postGroupId: Number(postGroupId),
});
const post = Object.values(posts?.data?.posts)
.flat()
.find((post) => String(post.id) === postId);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

posts?.data?.posts가 undefined일 수 있는 경우
Object.values(posts?.data?.posts)에서 posts?.data?.postsundefined이면 런타임 오류가 발생합니다. 안전한 처리가 필요합니다.

다음과 같이 수정하여 예외를 방지할 수 있습니다:

-  const post = Object.values(posts?.data?.posts)
-    .flat()
-    .find((post) => String(post.id) === postId);
+  const post = Object.values(posts?.data?.posts ?? {})
+    .flat()
+    .find((post) => String(post.id) === postId);
📝 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
const postId = searchParams.get('postId');
const { data } = useGroupPostsQuery(Number(agentId), Number(postGroupId));
const post = data?.data?.posts.find((post) => String(post.id) === postId);
console.log('dfddf', data, post);
console.log('postId:', postId, 'Converted:', Number(postId));
const { data: posts } = useGetAllPostsQuery({
agentId: Number(agentId),
postGroupId: Number(postGroupId),
});
const post = Object.values(posts?.data?.posts)
.flat()
.find((post) => String(post.id) === postId);
const postId = searchParams.get('postId');
const { data: posts } = useGetAllPostsQuery({
agentId: Number(agentId),
postGroupId: Number(postGroupId),
});
const post = Object.values(posts?.data?.posts ?? {})
.flat()
.find((post) => String(post.id) === postId);

Copy link
Contributor

@minseong0324 minseong0324 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!

@kongnayeon kongnayeon merged commit d1984a2 into develop Feb 17, 2025
3 of 6 checks passed
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.

2 participants