Skip to content

feat: 자료실 기능 및 워크스페이스 목업 확장 (#21) - #22

Merged
seongjinss555 merged 5 commits into
developfrom
feat/#21/resources-sidebar
Jul 8, 2026
Merged

feat: 자료실 기능 및 워크스페이스 목업 확장 (#21)#22
seongjinss555 merged 5 commits into
developfrom
feat/#21/resources-sidebar

Conversation

@seongjinss555

@seongjinss555 seongjinss555 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Pull Request

작업 내용

  • 워크스페이스 자료실 화면을 추가했습니다.
  • 자료실을 공지처럼 여러 도메인에서 재사용할 수 있도록 entities/resource, features/manage-resources, views/store-operation/resources로 분리했습니다.
  • workspace purpose별 사이드바 확인이 가능하도록 목업 워크스페이스를 팀 프로젝트, 사이드 프로젝트, 매장 운영 케이스로 확장했습니다.
  • 공통 헤더의 주요 액션 버튼 색상을 brand token 기준으로 맞췄습니다.

작업 결과

  • /workspaces/[workspaceId]/files에서 자료실 목록을 확인할 수 있습니다.
  • ex) /workspaces/store-workspace/files / /workspaces/side-workspace/files / /workspaces/team-workspace/files
  • 링크 저장, 파일 업로드 버튼으로 자료 추가 모달을 열 수 있습니다.
  • 모달은 버튼 순서와 동일하게 링크, 파일 순서의 탭을 제공합니다.
  • 링크 자료는 링크, 노션, 피그마, 깃허브 유형을 선택할 수 있습니다.
  • 자료실 목업 데이터는 workspaceId 기준으로 필터링됩니다.

변경 사항

Added

  • 자료실 라우트: src/app/workspaces/[workspaceId]/files/page.tsx
  • 자료실 엔티티와 목업 데이터: src/entities/resource
  • 자료실 공통 기능: src/features/manage-resources
  • 매장 운영 자료실 화면 조합: src/views/store-operation/resources

Changed

  • workspace 목업에 team-workspace, side-workspace, store-workspace 케이스를 추가했습니다.
  • workspace entity public API에서 목업 목록을 함께 export합니다.
  • 헤더 CTA 색상을 var(--color-brand) 기준으로 정리했습니다.

Fixed

  • 없음

실행화면

  • 매장 운영 도메인
image
  • 사이드 프로젝트 도메인
image
  • 팀프로젝트 도메인
image
  • 파일과 링크 첨부 화면
image image

테스트

  • 로컬 실행 확인
  • 주요 시나리오 확인
  • 영향 범위 확인

검증 명령:

  • npm run lint
  • npm run typecheck
  • npm run build
  • curl -I http://localhost:3000/workspaces/store-workspace/files

리뷰 체크리스트

  • PR base branch가 올바릅니다. (feature/* -> develop, 배포 시 develop 또는 release/* -> main)
  • 브랜치명이 Type/#issue-number/description 형식을 따릅니다.
  • 커밋 메시지가 컨벤션을 따릅니다.
  • 불필요한 console.log, 주석, 임시 코드를 제거했습니다.
  • 타입 에러와 린트 에러를 확인했습니다.
  • CodeRabbit 1차 리뷰를 확인했습니다.
  • CodeRabbit 리뷰 반영 후 Discord에 공유했습니다.
  • 최소 1명 이상의 approve 후 merge합니다.

리뷰 요청사항

  • 자료실은 팀 프로젝트/사이드 프로젝트에서도 사용할 예정이라 feature가 widget에 의존하지 않도록 분리했습니다.
  • 다른 도메인 자료실은 아래 의존 방향으로 view만 새로 조합하면 됩니다.
views/<domain>/resources
  -> features/manage-resources
    -> entities/resource
  • 추후 Supabase 연동 시 entities/resourceRESOURCES 테이블 row/mapper, features/manage-resources는 저장/업로드 액션 연결 지점으로 확장하면 됩니다.

관련 이슈

Closes #21
ref #2

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • 워크스페이스별 자료실(리소스) 페이지가 추가되어 파일/링크 자료를 카드 형태로 확인할 수 있습니다.
    • 목록에서 파일은 다운로드, 링크는 새 탭으로 열 수 있습니다.
    • 리소스 추가 모달을 통해 파일 업로드 또는 링크 저장을 제목/설명과 함께 추가할 수 있습니다.
  • Bug Fixes
    • 제목·파일명·링크 URL 누락 등 기본 입력 검증이 강화되었습니다.
  • Style
    • 멤버 초대 버튼 색상이 브랜드 톤에 맞게 조정되었습니다.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

자료실 화면이 추가되어 타입, 목업 데이터, 상태 훅, 다이얼로그, 목록, 뷰, 라우트가 연결되었습니다. 워크스페이스 목업 조회가 보강되었고, 헤더 버튼 스타일이 변경되었습니다.

Changes

자료실 기능

Layer / File(s) Summary
리소스 타입과 목업 데이터
src/entities/resource/model/resource.types.ts, src/entities/resource/model/mock-resources.ts, src/entities/resource/index.ts
ResourceType, ResourceLinkProvider, ResourceItem, ResourceFormValues 타입이 추가되고, mockResources 목업 배열과 도메인 공개 export가 구성됩니다.
리소스 상태 훅과 공개 API
src/features/manage-resources/model/use-resource-library-state.ts, src/features/manage-resources/index.ts
워크스페이스별 리소스 필터링/정렬, 다이얼로그 상태, 리소스 추가 검증 및 ID/날짜 생성 로직이 구현되고 기능 barrel export가 추가됩니다.
리소스 추가 다이얼로그와 목록
src/features/manage-resources/ui/ResourceAddDialog.tsx, src/features/manage-resources/ui/ResourceList.tsx
파일/링크 입력을 지원하는 추가 다이얼로그와 카드형 리소스 목록, 링크 열기/파일 다운로드 동작이 추가됩니다.
자료실 뷰와 files 라우트
src/views/store-operation/resources/ui/ResourcesView.tsx, src/views/store-operation/resources/index.ts, src/app/workspaces/[workspaceId]/files/page.tsx
ResourcesView가 상태 훅과 UI를 연결하고, files 페이지가 workspaceId를 받아 자료실 화면을 렌더링합니다.

워크스페이스 보조 변경

Layer / File(s) Summary
워크스페이스 목업과 조회
src/entities/workspace/model/mock-workspace.ts
워크스페이스 목업 레코드가 교체되고, React cache 기반 조회 함수가 추가됩니다.
헤더 버튼 스타일
src/widgets/workspace-shell/ui/WorkspaceHeader.tsx
멤버 초대 버튼의 배경색 클래스가 CSS 변수 기반 값으로 변경됩니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ResourcesView
    participant useResourceLibraryState
    participant ResourceAddDialog
    participant ResourceList

    User->>ResourcesView: "링크 저장" / "파일 업로드" 클릭
    ResourcesView->>useResourceLibraryState: openDialog(resourceType)
    ResourcesView->>ResourceAddDialog: isOpen, initialResourceType 전달
    User->>ResourceAddDialog: 폼 입력 후 저장
    ResourceAddDialog->>useResourceLibraryState: addResource(formValues)
    useResourceLibraryState->>useResourceLibraryState: 검증, ID/createdAt 생성, 목록 갱신
    useResourceLibraryState-->>ResourceList: resources 전달
    ResourceList-->>User: 카드 목록 렌더링
Loading

Possibly related issues

Possibly related PRs

  • TeampleRun/syncly#12: 동일한 WorkspaceHeader 컴포넌트가 수정되어 코드 레벨로 직접 연결됩니다.

Suggested reviewers: Kwon812, 0011810, wjswlgh96

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning #21은 회의록 페이지 구현인데, PR은 자료실/리소스 기능으로 다른 요구사항을 구현했습니다. #21의 회의록 목록/작성 페이지와 네비게이션, mock 데이터 구성을 구현하도록 변경하세요.
Out of Scope Changes check ⚠️ Warning 링크된 이슈와 무관한 자료실 리소스 분리, 목업 확장, 헤더 색상 변경이 포함되어 있습니다. 회의록 기능 범위로 제한하고, 자료실/헤더 변경은 별도 PR로 분리하세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 자료실 기능과 워크스페이스 목업 확장을 잘 요약합니다.
Description check ✅ Passed 작업 내용, 결과, 변경 사항, 테스트, 리뷰 체크리스트가 대부분 채워져 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#21/resources-sidebar

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

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

Inline comments:
In `@src/entities/resource/model/mock-resources.ts`:
- Around line 110-119: The mock resource in mock-resources.ts uses an invalid
createdAt date value for resource-11, so update that fixture to a real calendar
date. Keep the fix localized to the resource-11 object in the mock data and
ensure the createdAt string remains in the same format as the other resource
entries so functions like sortResources and any future Date parsing continue to
work with valid input.
- Around line 112-124: The user-facing description strings in the mock resource
data contain typos: update the `resource-11` and `resource-12` entries in
`mock-resources.ts` so the `description` values read correctly, since they are
rendered directly by `ResourceList`. Keep the fix limited to those mock text
fields and verify the displayed labels now match the intended Korean wording.

In `@src/entities/workspace/model/mock-workspace.ts`:
- Around line 21-30: The mock workspace entries in mockWorkspaceData use the
same display name for both store-test and store-workspace, which makes them
indistinguishable in sidebar/list tests. Update the name field for one of these
entries so each workspace has a unique, clearly different label while keeping
the existing ids and purpose values unchanged. Use the mockWorkspaceData object
and the store-test/store-workspace entries to locate the duplicates.

In `@src/features/manage-resources/model/use-resource-library-state.ts`:
- Around line 34-38: `use-resource-library-state`의 `resources` 상태가
`useState(workspaceResources)` 초기값에만 의존해서 `workspaceId`가 바뀌어도 이전 워크스페이스 데이터가 남는
문제를 수정하세요. `workspaceResources`를 만드는 `useMemo`와 `setResources`를 사용하는
`useResourceLibraryState`에서 `workspaceId` 또는 `initialResources` 변경을 감지하는
`useEffect`를 추가해 `resources`를 다시 동기화하도록 하세요. `workspaceResources`, `resources`,
`setResources` 식별자를 기준으로 상태 초기화 로직을 찾아 반영하면 됩니다.

In `@src/features/manage-resources/ui/ResourceAddDialog.tsx`:
- Around line 16-25: The linkProviders mapping in ResourceAddDialog currently
assigns the same Link icon to link, notion, and figma, so update that array to
use distinct icons for each provider. Use the existing ResourceAddDialog
component and linkProviders symbols to swap in different lucide-react icons
where available, ensuring the label/value/icon combinations are visually
distinguishable while keeping GitBranch for github.
- Around line 135-156: ResourceAddDialog’s URL input, title input, and
description textarea rely only on placeholders, so add accessible names for each
field by wiring in explicit <label> elements or aria-labels. Update the inputs
in ResourceAddDialog to give the URL field, the title field, and the description
textarea clear screen-reader labels, keeping the existing layout and
placeholders intact.
- Around line 61-178: The modal rendered by ResourceAddDialog is missing dialog
accessibility behavior, so update the outer modal container to use the dialog
semantics by adding role="dialog" and aria-modal="true" in the ResourceAddDialog
return markup. Also add ESC-to-close handling in ResourceAddDialog with a
useEffect keydown listener that calls onClose when Escape is pressed, and
include focus management or a focus trap so keyboard users stay within the modal
while it is open.
- Around line 40-59: The ResourceAddDialog state is not reset when the dialog is
closed, so reopening it can show stale title/description/URL/file values. In
ResourceAddDialog, add a useEffect that watches isOpen (and, if needed,
resourceType) and clears the local form state whenever the dialog closes, rather
than relying on onSubmit alone. Make sure to import useEffect from react and
keep the reset logic alongside the existing submitResource, canSubmit, and
isOpen handling.

In `@src/features/manage-resources/ui/ResourceList.tsx`:
- Around line 65-71: The ResourceList item action button is decorative only
because it has an aria-label but no click behavior. Update the button in
ResourceList to wire an onClick handler that opens resource.url in a new tab for
link resources, and routes file resources to the appropriate download/preview
action; use the existing resource object and the ChevronRight button markup to
locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3bb401fe-baeb-49c0-ac1c-8095567d6a39

📥 Commits

Reviewing files that changed from the base of the PR and between 1d4418d and 67e8c0c.

📒 Files selected for processing (13)
  • src/app/workspaces/[workspaceId]/files/page.tsx
  • src/entities/resource/index.ts
  • src/entities/resource/model/mock-resources.ts
  • src/entities/resource/model/resource.types.ts
  • src/entities/workspace/index.ts
  • src/entities/workspace/model/mock-workspace.ts
  • src/features/manage-resources/index.ts
  • src/features/manage-resources/model/use-resource-library-state.ts
  • src/features/manage-resources/ui/ResourceAddDialog.tsx
  • src/features/manage-resources/ui/ResourceList.tsx
  • src/views/store-operation/resources/index.ts
  • src/views/store-operation/resources/ui/ResourcesView.tsx
  • src/widgets/workspace-shell/ui/WorkspaceHeader.tsx

Comment on lines +110 to +119
id: 'resource-11',
workspaceId: 'side-workspace',
title: 'Github Repository',
description: '매인 코드 저장소',
resourceType: 'link',
linkProvider: 'github',
url: 'https://github.com',
uploadedBy: '이하은',
createdAt: '2025-06-31',
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

존재하지 않는 날짜 값 (2025-06-31).

resource-11createdAt'2025-06-31'인데, 6월은 30일까지만 존재합니다. sortResourceslocaleCompare로 문자열 비교만 하므로 크래시는 나지 않지만, 실제로는 유효하지 않은 날짜라 향후 Date 파싱이나 표시 로직에서 문제가 될 수 있습니다.

🛠️ 제안 수정
     resourceType: 'link',
     linkProvider: 'github',
     url: 'https://github.com',
     uploadedBy: '이하은',
-    createdAt: '2025-06-31',
+    createdAt: '2025-06-30',
   },
📝 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
id: 'resource-11',
workspaceId: 'side-workspace',
title: 'Github Repository',
description: '매인 코드 저장소',
resourceType: 'link',
linkProvider: 'github',
url: 'https://github.com',
uploadedBy: '이하은',
createdAt: '2025-06-31',
},
id: 'resource-11',
workspaceId: 'side-workspace',
title: 'Github Repository',
description: '매인 코드 저장소',
resourceType: 'link',
linkProvider: 'github',
url: 'https://github.com',
uploadedBy: '이하은',
createdAt: '2025-06-30',
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/entities/resource/model/mock-resources.ts` around lines 110 - 119, The
mock resource in mock-resources.ts uses an invalid createdAt date value for
resource-11, so update that fixture to a real calendar date. Keep the fix
localized to the resource-11 object in the mock data and ensure the createdAt
string remains in the same format as the other resource entries so functions
like sortResources and any future Date parsing continue to work with valid
input.

Comment thread src/entities/resource/model/mock-resources.ts Outdated
Comment thread src/entities/workspace/model/mock-workspace.ts Outdated
Comment thread src/features/manage-resources/model/use-resource-library-state.ts Outdated
Comment on lines +16 to +25
const linkProviders: Array<{
label: string;
value: ResourceLinkProvider;
icon: typeof Link;
}> = [
{ label: '링크', value: 'link', icon: Link },
{ label: '노션', value: 'notion', icon: Link },
{ label: '피그마', value: 'figma', icon: Link },
{ label: '깃허브', value: 'github', icon: GitBranch },
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

link/notion/figma 아이콘이 모두 동일합니다.

Link 아이콘이 link/notion/figma 세 항목에 공통으로 사용되어 시각적으로 구분되지 않습니다. lucide-react에 브랜드 전용 아이콘이 없다면, 최소한 서로 다른 일반 아이콘(예: FileText, Figma 유사 아이콘 등)을 배정해 구분성을 높이는 것을 고려해보세요.

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

In `@src/features/manage-resources/ui/ResourceAddDialog.tsx` around lines 16 - 25,
The linkProviders mapping in ResourceAddDialog currently assigns the same Link
icon to link, notion, and figma, so update that array to use distinct icons for
each provider. Use the existing ResourceAddDialog component and linkProviders
symbols to swap in different lucide-react icons where available, ensuring the
label/value/icon combinations are visually distinguishable while keeping
GitBranch for github.

Comment on lines +40 to +59
if (!isOpen) {
return null;
}

const canSubmit = resourceType === 'file' ? fileName.trim().length > 0 : url.trim().length > 0;

const submitResource = () => {
onSubmit({
resourceType,
linkProvider,
url,
fileName,
title,
description,
});
setUrl('');
setFileName('');
setTitle('');
setDescription('');
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

취소 후 재오픈 시 이전 입력값이 남습니다.

isOpen=false일 때 컴포넌트가 언마운트되지 않고 null만 렌더링하므로, 취소(onClose) 시 폼 state가 초기화되지 않습니다. ResourcesView에서 key={dialogResourceType}을 사용하지만 같은 타입으로 다시 열면 리마운트되지 않아, 이전에 입력했던 제목/설명/URL/파일명이 그대로 남은 채 다시 표시됩니다.

🐛 제안 수정
+  useEffect(() => {
+    if (!isOpen) {
+      setUrl('');
+      setFileName('');
+      setTitle('');
+      setDescription('');
+    }
+  }, [isOpen]);
+
   if (!isOpen) {
     return null;
   }

(useEffectreact에서 import 해야 합니다.)

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

In `@src/features/manage-resources/ui/ResourceAddDialog.tsx` around lines 40 - 59,
The ResourceAddDialog state is not reset when the dialog is closed, so reopening
it can show stale title/description/URL/file values. In ResourceAddDialog, add a
useEffect that watches isOpen (and, if needed, resourceType) and clears the
local form state whenever the dialog closes, rather than relying on onSubmit
alone. Make sure to import useEffect from react and keep the reset logic
alongside the existing submitResource, canSubmit, and isOpen handling.

Comment thread src/features/manage-resources/ui/ResourceAddDialog.tsx
Comment thread src/features/manage-resources/ui/ResourceAddDialog.tsx
Comment thread src/features/manage-resources/ui/ResourceList.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

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

Inline comments:
In `@src/features/manage-resources/ui/ResourceAddDialog.tsx`:
- Around line 122-131: This is an optional improvement, not a required change:
keep the current custom dialog implementation in ResourceAddDialog and do not
migrate it to a native <dialog>. The existing role="dialog" structure with
dialogRef, keepFocusInsideDialog, and the ESC/focus handling is sufficient here,
so leave this block as-is and address only the separate effect bug elsewhere.
- Around line 97-115: The ResourceAddDialog useEffect is re-running on every
render because it has no dependency array, which keeps refocusing the dialog and
steals input focus while typing. Update the useEffect in ResourceAddDialog to
depend on the open state so the focus/keydown listener logic only runs when
isOpen changes, and keep the Escape handler cleanup tied to that effect.

In `@src/features/manage-resources/ui/ResourceList.tsx`:
- Around line 24-39: In openResource, the fallback download path is saving plain
text Blob contents with the original resource.fileName, which can mislead users
into thinking it is a real binary file. Update the download naming logic so
mock/text exports use a .txt filename (for example by normalizing
resource.fileName in openResource or when setting downloadLink.download), while
keeping the existing link handling for resourceType === 'link'.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4ca052c1-4070-4965-8f20-b35d8381de34

📥 Commits

Reviewing files that changed from the base of the PR and between 554ddbe and ffedcc4.

📒 Files selected for processing (3)
  • src/features/manage-resources/model/use-resource-library-state.ts
  • src/features/manage-resources/ui/ResourceAddDialog.tsx
  • src/features/manage-resources/ui/ResourceList.tsx

Comment thread src/features/manage-resources/ui/ResourceAddDialog.tsx Outdated
Comment thread src/features/manage-resources/ui/ResourceAddDialog.tsx
Comment on lines +24 to +39
function openResource(resource: ResourceItem) {
if (resource.resourceType === 'link' && resource.url) {
window.open(resource.url, '_blank', 'noopener,noreferrer');
return;
}

const fileContents = `${resource.title}\n\n${resource.description}`;
const file = new Blob([fileContents], { type: 'text/plain;charset=utf-8' });
const fileUrl = URL.createObjectURL(file);
const downloadLink = document.createElement('a');

downloadLink.href = fileUrl;
downloadLink.download = resource.fileName ?? `${resource.title}.txt`;
downloadLink.click();
URL.revokeObjectURL(fileUrl);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

다운로드 파일명과 실제 내용이 불일치합니다.

resource.fileName을 그대로 다운로드 파일명으로 사용하지만, 실제 Blob 내용은 title+description으로 만든 일반 텍스트입니다. 원본 확장자(.xlsx, .pdf 등)를 유지한 채 저장되면 사용자가 파일을 열었을 때 손상된 파일로 오인할 수 있습니다. 실제 파일 저장이 불가능한 목업 환경이라면, 다운로드 파일명에 .txt 확장자를 강제하는 편이 안전합니다.

🐛 제안 수정
   const fileContents = `${resource.title}\n\n${resource.description}`;
   const file = new Blob([fileContents], { type: 'text/plain;charset=utf-8' });
   const fileUrl = URL.createObjectURL(file);
   const downloadLink = document.createElement('a');

   downloadLink.href = fileUrl;
-  downloadLink.download = resource.fileName ?? `${resource.title}.txt`;
+  downloadLink.download = `${(resource.fileName ?? resource.title).replace(/\.[^./]+$/, '')}.txt`;
   downloadLink.click();
📝 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
function openResource(resource: ResourceItem) {
if (resource.resourceType === 'link' && resource.url) {
window.open(resource.url, '_blank', 'noopener,noreferrer');
return;
}
const fileContents = `${resource.title}\n\n${resource.description}`;
const file = new Blob([fileContents], { type: 'text/plain;charset=utf-8' });
const fileUrl = URL.createObjectURL(file);
const downloadLink = document.createElement('a');
downloadLink.href = fileUrl;
downloadLink.download = resource.fileName ?? `${resource.title}.txt`;
downloadLink.click();
URL.revokeObjectURL(fileUrl);
}
function openResource(resource: ResourceItem) {
if (resource.resourceType === 'link' && resource.url) {
window.open(resource.url, '_blank', 'noopener,noreferrer');
return;
}
const fileContents = `${resource.title}\n\n${resource.description}`;
const file = new Blob([fileContents], { type: 'text/plain;charset=utf-8' });
const fileUrl = URL.createObjectURL(file);
const downloadLink = document.createElement('a');
downloadLink.href = fileUrl;
downloadLink.download = `${(resource.fileName ?? resource.title).replace(/\.[^./]+$/, '')}.txt`;
downloadLink.click();
URL.revokeObjectURL(fileUrl);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/manage-resources/ui/ResourceList.tsx` around lines 24 - 39, In
openResource, the fallback download path is saving plain text Blob contents with
the original resource.fileName, which can mislead users into thinking it is a
real binary file. Update the download naming logic so mock/text exports use a
.txt filename (for example by normalizing resource.fileName in openResource or
when setting downloadLink.download), while keeping the existing link handling
for resourceType === 'link'.

@seongjinss555
seongjinss555 force-pushed the feat/#21/resources-sidebar branch from bd9f457 to 5ee4095 Compare July 8, 2026 05:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
src/features/manage-resources/ui/ResourceAddDialog.tsx (1)

97-115: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

useEffect에 의존성 배열이 없어 입력 중 포커스가 계속 빼앗깁니다.

Line 115의 });에 의존성 배열이 없어, title/url/description state가 바뀔 때마다(즉 매 keystroke마다) 이펙트가 재실행되고 dialogRef.current?.focus()(Line 102)가 다이얼로그 컨테이너로 포커스를 되돌립니다. 결과적으로 입력 필드에 연속으로 타이핑할 수 없습니다. 이 문제는 이전 리뷰에서 Critical로 지적되었으나 현재 코드에도 여전히 존재합니다.

🐛 제안 수정
     return () => {
       document.removeEventListener('keydown', closeOnEscape);
     };
-  });
+  }, [isOpen]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/manage-resources/ui/ResourceAddDialog.tsx` around lines 97 -
115, The ResourceAddDialog useEffect is missing a dependency array, so it reruns
on every render and keeps calling dialogRef.current?.focus(), stealing focus
from the form inputs while typing. Update the effect tied to isOpen/closeDialog
so it only runs when the dialog opens or the close handler changes, and keep the
Escape key listener cleanup intact. Locate this in ResourceAddDialog around the
useEffect that sets up closeOnEscape and focuses dialogRef.
src/features/manage-resources/ui/ResourceList.tsx (1)

24-39: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

다운로드 파일명과 실제 내용이 불일치합니다 (미해결).

resource.fileName(예: 6월 매출 정산.xls)을 그대로 다운로드 파일명으로 사용하지만 실제 Blob 내용은 일반 텍스트입니다. 원본 확장자를 유지한 채 저장되면 사용자가 손상된 파일로 오인할 수 있습니다. 이전 리뷰에서 지적되었으나 현재 코드에도 그대로 남아 있습니다.

🐛 제안 수정
   downloadLink.href = fileUrl;
-  downloadLink.download = resource.fileName ?? `${resource.title}.txt`;
+  downloadLink.download = `${(resource.fileName ?? resource.title).replace(/\.[^./]+$/, '')}.txt`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/manage-resources/ui/ResourceList.tsx` around lines 24 - 39, The
`openResource` download path is still using `resource.fileName` as-is even
though it creates a plain text Blob, so the saved filename can misleadingly keep
an original extension. Update the download logic in `openResource` to derive a
safe text filename for generated content (for example, base it on
`resource.title` and ensure a `.txt` extension) instead of reusing
`resource.fileName` when the content is synthesized. Keep the existing
link-handling branch unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/features/manage-resources/ui/ResourceAddDialog.tsx`:
- Around line 97-115: The ResourceAddDialog useEffect is missing a dependency
array, so it reruns on every render and keeps calling
dialogRef.current?.focus(), stealing focus from the form inputs while typing.
Update the effect tied to isOpen/closeDialog so it only runs when the dialog
opens or the close handler changes, and keep the Escape key listener cleanup
intact. Locate this in ResourceAddDialog around the useEffect that sets up
closeOnEscape and focuses dialogRef.

In `@src/features/manage-resources/ui/ResourceList.tsx`:
- Around line 24-39: The `openResource` download path is still using
`resource.fileName` as-is even though it creates a plain text Blob, so the saved
filename can misleadingly keep an original extension. Update the download logic
in `openResource` to derive a safe text filename for generated content (for
example, base it on `resource.title` and ensure a `.txt` extension) instead of
reusing `resource.fileName` when the content is synthesized. Keep the existing
link-handling branch unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d663510e-d969-4ce3-9983-9cc3afb98202

📥 Commits

Reviewing files that changed from the base of the PR and between bd9f457 and 5ee4095.

📒 Files selected for processing (12)
  • src/app/workspaces/[workspaceId]/files/page.tsx
  • src/entities/resource/index.ts
  • src/entities/resource/model/mock-resources.ts
  • src/entities/resource/model/resource.types.ts
  • src/entities/workspace/model/mock-workspace.ts
  • src/features/manage-resources/index.ts
  • src/features/manage-resources/model/use-resource-library-state.ts
  • src/features/manage-resources/ui/ResourceAddDialog.tsx
  • src/features/manage-resources/ui/ResourceList.tsx
  • src/views/store-operation/resources/index.ts
  • src/views/store-operation/resources/ui/ResourcesView.tsx
  • src/widgets/workspace-shell/ui/WorkspaceHeader.tsx

@Kwon812 Kwon812 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

고생하셨습니다~

@seongjinss555
seongjinss555 merged commit 216be5f into develop Jul 8, 2026
1 check 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.

[Feat] 팀프로젝트 회의록 페이지 및 작성 페이지 구현

2 participants