Skip to content

refactor/communication#193

Merged
rklpoi5678 merged 14 commits intodevfrom
refactor/communication
Mar 9, 2026
Merged

refactor/communication#193
rklpoi5678 merged 14 commits intodevfrom
refactor/communication

Conversation

@rklpoi5678
Copy link
Contributor

@rklpoi5678 rklpoi5678 commented Mar 8, 2026

🔗 관련 이슈

  • Closes #이슈번호

✨ 작업 단계 및 변경 사항

PostCommentSVC, PostComment 동일

  • 첨부파일 수정 / 삭제시 orphan객체 삭제
  • 프론트 댓글 이미지만 가능 (이미지 미리 보기가능)
  • 학생이 답변 완료 처리 유지

🧪 테스트 방법

리뷰어가 확인해야 할 핵심 포인트를 적어주세요.

  • 브라우저에서 ID: B0 페이지가 정상적으로 렌더링되는가?
  • Phase 1 기준, 버튼 클릭 시 콘솔에 로그가 잘 찍히는가?

📸 스크린샷 (선택)

  • UI 변경이 있거나 Phase 2 작업인 경우 반드시 첨부해주세요.

✅ 체크리스트

  • Phase에 맞는 이슈 체크리스트를 모두 완료했는가?
  • lint / type-check 통과 및 불필요한 console.log 제거
  • 머지 후 이 이슈가 [QA] 컬럼으로 이동함을 인지하고 있는가?

Summary by CodeRabbit

  • New Features
    • File attachment support expanded to posts and comments—attach files when creating or editing.
    • Image previews rendered as clickable thumbnails that open full-size.
    • Edit-mode file management: select, replace, or remove attachments with clear UI and automatic reset on cancel/save.
    • Attachments shown inline in both read and edit views; editor can propagate selected files for upload.

@rklpoi5678 rklpoi5678 self-assigned this Mar 8, 2026
@rklpoi5678 rklpoi5678 added the FE 프런트 생성 label Mar 8, 2026
@rklpoi5678 rklpoi5678 changed the title Refactor/communication refactor/communication Mar 8, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 8, 2026

📝 Walkthrough

Walkthrough

Adds file-attachment support to post/comment editors: UI for selecting, previewing, replacing, and clearing files; editors emit selected files; page handlers and services send FormData; types and hooks widened to accept FormData.

Changes

Cohort / File(s) Summary
Educator Comment UI
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
Added file input ref and editFile state, image preview with click-to-open, file selection/clear UI; onUpdateComment and internal onUpdate now accept optional file param.
Educator Page Handler
src/app/(dashboard)/educators/communication/[communicationId]/page.tsx
Switched comment create/update to FormData (content + optional file); updated handleUpdateComment signature to accept optional file and adjusted download/attachment payload logic.
Learner Comment UI
src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
Mirror of educator comment changes: editFile state, preview/replace UI, file input ref, and onUpdateComment signature expanded to accept optional file.
Learner Post Content UI
src/app/(dashboard)/learners/communication/[communicationId]/_components/PostContentSVC.tsx
Introduced editFile/setEditFile props, hidden file input ref, existingAttachments handling, preview/replace/remove controls and thumbnail rendering.
Learner Page Handler
src/app/(dashboard)/learners/communication/[communicationId]/page.tsx
Added editFile state, reset on cancel; submit post edits and comment create/update via FormData; pass editFile/setEditFile to PostContentSVC.
Editor Component
src/components/common/editor/TiptapEditor.tsx
Exposes onFileUpload propagation; removed an inline comment (no behavioral change).
Service Layer
src/services/SVC/studentPost.service.ts
updateStudentPostSVC and updateStudentPostCommentSVC payload types widened to accept FormData unions (signatures updated).
Hooks / Mutations
src/hooks/SVC/useCommunicationSVC.ts
Widened mutation payload types to accept FormData unions for instructor/student post comment create/update and student post update.
Types
src/types/communication/instructorPost.ts, src/types/communication/studentPost.ts
Expanded CreateInstructorPostCommentRequest and CreateStudentPostCommentRequest to `{ content: string; materialIds?: string[] }

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CommentUI as Comment UI
    participant Editor as TiptapEditor
    participant PageHandler as Page Handler
    participant Mutation as useCommunicationMutation
    participant Service as Service Layer
    participant Server

    User->>CommentUI: open editor, select file
    CommentUI->>Editor: onFileUpload(file)
    Editor-->>CommentUI: file propagated
    CommentUI->>CommentUI: show preview, set editFile
    User->>CommentUI: submit edit (content + file)
    CommentUI->>PageHandler: onUpdateComment(id, content, file)
    PageHandler->>PageHandler: build FormData(content, file)
    PageHandler->>Mutation: updateCommentMutation(FormData)
    Mutation->>Service: updateCommentSVC(FormData)
    Service->>Server: PATCH /... (multipart/form-data)
    Server-->>Service: success
    Service-->>Mutation: response
    Mutation-->>PageHandler: onSuccess
    PageHandler->>CommentUI: clear editFile, exit edit mode
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

API

Suggested reviewers

  • p-changki

Poem

🐰
I nudge the upload with a twitch and spin,
Tiny files tumble gently in,
FormData cradles picture and text,
Comments brighten — neat and next,
A hopping patch of features, ready to begin.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'refactor/communication' is vague and generic, using a non-descriptive term that does not convey meaningful information about the changeset's primary purpose. Replace with a specific title that describes the main change, such as 'Add file attachment support to comments and posts' or 'Implement image attachment preview for communication features'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/communication

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

❤️ Share

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

@github-actions
Copy link

github-actions bot commented Mar 8, 2026

⚡️ Lighthouse Report

🏠 URL: http://localhost:3000/

Category Score
🔴 Performance 39
🟢 Accessibility 96
🟢 Best practices 93
🟢 SEO 100

상세 리포트는 하단의 lhci/url Checks 내 Details 링크를 확인해주세요! 🚀

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
src/hooks/SVC/useCommunicationSVC.ts (3)

325-332: ⚠️ Potential issue | 🟠 Major

Missing FormData in createCommentSVC payload type.

The createCommentSVC mutation's payload type is still CreateStudentPostCommentRequest only, but the page component (learners/.../page.tsx line 210) passes formData to it. This is inconsistent with the other mutations updated in this PR and will cause a type error.

🛠️ Proposed fix
   const createCommentSVC = useMutation({
     mutationFn: ({
       postId,
       payload,
     }: {
       postId: string;
-      payload: CreateStudentPostCommentRequest;
+      payload: FormData | CreateStudentPostCommentRequest;
     }) => myPostServiceSVC.createStudentPostCommentSVC(postId, payload),

Additionally, createStudentPostCommentSVC in the service layer needs FormData handling.

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

In `@src/hooks/SVC/useCommunicationSVC.ts` around lines 325 - 332, Update the
createCommentSVC mutation input type to accept the FormData wrapper the page
sends and ensure the service handles it: change the mutationFn parameter type
from CreateStudentPostCommentRequest to a union or wrapper that includes
FormData (e.g., { postId: string; payload: CreateStudentPostCommentRequest |
FormData }), update the call site in createCommentSVC to pass the payload
through unchanged, and modify createStudentPostCommentSVC to detect and handle
FormData payloads (convert/extract fields or forward as-is) so both callers and
the service accept FormData without type errors.

78-93: ⚠️ Potential issue | 🔴 Critical

Same type mismatch issue for updateInstructorPostCommentSVC.

The payload type union includes FormData, but the underlying service function at lines 57-67 of studentPost.service.ts doesn't support it. Apply the same fix as needed for create.

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

In `@src/hooks/SVC/useCommunicationSVC.ts` around lines 78 - 93, The
updateInstructorPostCommentSVC mutation incorrectly types its payload as
FormData | CreateInstructorPostCommentRequest while the underlying service
method instructorPostServiceSVC.updateInstructorPostCommentSVC only accepts the
typed request object; remove FormData from the union (use
CreateInstructorPostCommentRequest only) or add explicit handling/overload in
updateInstructorPostCommentSVC to convert FormData into
CreateInstructorPostCommentRequest before calling
instructorPostServiceSVC.updateInstructorPostCommentSVC so the types match and
calls align with the service signature.

49-57: ⚠️ Potential issue | 🔴 Critical

Type mismatch: service function doesn't accept FormData.

The hook's payload type is FormData | CreateInstructorPostCommentRequest, but instructorPostServiceSVC.createInstructorPostCommentSVC only accepts CreateInstructorPostCommentRequest (see context snippet from studentPost.service.ts:44-54). This will cause a TypeScript compilation error and, if bypassed, runtime issues since the service doesn't set the multipart/form-data Content-Type for FormData payloads.

The service function needs to be updated to:

  1. Accept FormData | CreateInstructorPostCommentRequest in its signature
  2. Add instanceof FormData check with conditional Content-Type header
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/SVC/useCommunicationSVC.ts` around lines 49 - 57, Update the
service method instructorPostServiceSVC.createInstructorPostCommentSVC to accept
payload: FormData | CreateInstructorPostCommentRequest (instead of only
CreateInstructorPostCommentRequest) and inside the implementation add a runtime
check (payload instanceof FormData) to choose headers: when payload is FormData,
send the request without manually setting Content-Type so the browser/HTTP
client can set multipart/form-data with boundary; otherwise set Content-Type:
application/json and JSON.stringify the body as needed; keep the existing call
site createInstructorPostCommentSVC in the hook unchanged except for the
adjusted service signature.
src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx (2)

29-29: ⚠️ Potential issue | 🔴 Critical

Type mismatch: onUpdateComment is missing the file parameter.

The prop type defines onUpdateComment without a file parameter, but CommentItemSVC (line 144) declares onUpdate with file?: File | null and calls it with the file at line 211. This means the file attachment will be silently ignored when updating comments from the learner view.

🐛 Proposed fix to add the file parameter
-  onUpdateComment: (commentId: string, content: JSONContent) => void;
+  onUpdateComment: (
+    commentId: string,
+    content: JSONContent,
+    file?: File | null
+  ) => void;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/`(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
at line 29, The prop type for onUpdateComment in PostCommentSVC.tsx is missing
the file parameter so file attachments are dropped; update the type signature to
accept an optional file (e.g., onUpdateComment: (commentId: string, content:
JSONContent, file?: File | null) => void) and ensure any callers (notably
CommentItemSVC which calls onUpdate with a file) match this signature so the
file parameter is forwarded when updating comments.

115-120: ⚠️ Potential issue | 🟡 Minor

Missing accept attribute on main file input.

The edit-mode file input (line 295) restricts to accept="image/*", but the main file input here lacks this restriction. Given the PR states "front-end allows only comment images," this should be consistent.

Proposed fix
                 <input
                   type="file"
+                  accept="image/*"
                   ref={fileInputRef}
                   onChange={handleFileChange}
                   className="hidden"
                 />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/`(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
around lines 115 - 120, The main file input in the PostCommentSVC component is
missing the accept attribute while the edit-mode input already restricts files
to images; update the primary <input> (the one using fileInputRef and
handleFileChange) to include accept="image/*" so it only allows image uploads,
matching the edit-mode input's behavior and the PR requirement that comment
attachments be images only.
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx (1)

118-122: ⚠️ Potential issue | 🟡 Minor

Missing accept attribute on main file input.

Same issue as the learners component: the edit-mode file input (line 296) restricts to images, but this input allows any file type.

Proposed fix
              <input
                type="file"
+               accept="image/*"
                ref={fileInputRef}
                onChange={handleFileChange}
                className="hidden"
              />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
around lines 118 - 122, The main file input in PostComment.tsx (the input using
fileInputRef and handleFileChange) is missing an accept attribute and currently
allows any file type; update that input to match the edit-mode file input by
adding an appropriate accept value (e.g., accept="image/*" if only images are
allowed) so both the primary and edit-mode inputs enforce the same file type
restrictions and use the same validation expectations in handleFileChange.
🧹 Nitpick comments (1)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx (1)

140-365: Consider extracting CommentItem into a shared component.

CommentItem in this file and CommentItemSVC in PostCommentSVC.tsx are nearly identical (~95% code overlap). Extracting this into a shared component would reduce duplication and ensure consistent behavior across educator and learner views.

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

In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
around lines 140 - 365, The CommentItem component is duplicated (CommentItem in
this file and CommentItemSVC in PostCommentSVC.tsx); extract a single shared
component and import it from both places: create a new shared component (e.g.,
SharedCommentItem) that accepts the same props signature (comment:
CommonPostComment, onUpdate, onDelete) and preserves behavior for edit state,
getParsedContent, handleSave, file input ref, TiptapEditor usage, and image
rendering, then replace CommentItem and CommentItemSVC usages with the new
SharedCommentItem and remove the duplicated implementations so both educator and
SVC views reuse the same component.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx:
- Around line 347-360: The image container div in PostComment.tsx (the element
keyed by img.id || img.filename that wraps the Image) lacks keyboard
accessibility; update that div to include role="button", tabIndex={0}, and an
onKeyDown handler that triggers the same action as onClick (opening img.fileUrl
in a new tab) when Enter or Space is pressed (handle Space to prevent default
scrolling). Also add an appropriate aria-label (e.g., using img.filename) so
screen reader users know the action. Ensure the existing onClick behavior and
Image props remain unchanged.

In
`@src/app/`(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx:
- Around line 346-360: The clickable image container in PostCommentSVC uses an
onClick on a div (the img container) but lacks keyboard accessibility; make the
container focusable and operable via keyboard by adding role="button" and
tabIndex={0}, provide an accessible name with aria-label (e.g., using
img.filename or a descriptive string), and add an onKeyDown handler that calls
the same window.open(img.fileUrl, "_blank") when Enter or Space is pressed
(handle Space by preventing default to avoid scrolling). Alternatively replace
the div with a semantic <button> or <a> that opens the image; ensure the Image
alt text/aria-label conveys purpose.

In `@src/components/common/editor/TiptapEditor.tsx`:
- Around line 144-147: The file input element is visible because its hidden
class was removed; restore the hidden behavior by adding the "hidden" class back
to the <input> that uses fileInputRef and handleFileChange so only the styled
ToolbarButton (with the Paperclip icon) is shown and clicking the button still
triggers the input; update the input element associated with fileInputRef and
handleFileChange to include className="hidden".

In `@src/services/SVC/studentPost.service.ts`:
- Around line 133-140: The updateStudentPostCommentSVC function currently types
its payload as CreateStudentPostCommentRequest but must accept FormData too to
match useStudentPostCommentMutationsSVC; update the signature to payload:
CreateStudentPostCommentRequest | FormData, add const isFormData = payload
instanceof FormData and pass headers: isFormData ? { "Content-Type":
"multipart/form-data" } : {} into the axiosClientSVC.patch (same pattern as
updateStudentPostSVC) so file uploads work correctly when editing comments.

---

Outside diff comments:
In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx:
- Around line 118-122: The main file input in PostComment.tsx (the input using
fileInputRef and handleFileChange) is missing an accept attribute and currently
allows any file type; update that input to match the edit-mode file input by
adding an appropriate accept value (e.g., accept="image/*" if only images are
allowed) so both the primary and edit-mode inputs enforce the same file type
restrictions and use the same validation expectations in handleFileChange.

In
`@src/app/`(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx:
- Line 29: The prop type for onUpdateComment in PostCommentSVC.tsx is missing
the file parameter so file attachments are dropped; update the type signature to
accept an optional file (e.g., onUpdateComment: (commentId: string, content:
JSONContent, file?: File | null) => void) and ensure any callers (notably
CommentItemSVC which calls onUpdate with a file) match this signature so the
file parameter is forwarded when updating comments.
- Around line 115-120: The main file input in the PostCommentSVC component is
missing the accept attribute while the edit-mode input already restricts files
to images; update the primary <input> (the one using fileInputRef and
handleFileChange) to include accept="image/*" so it only allows image uploads,
matching the edit-mode input's behavior and the PR requirement that comment
attachments be images only.

In `@src/hooks/SVC/useCommunicationSVC.ts`:
- Around line 325-332: Update the createCommentSVC mutation input type to accept
the FormData wrapper the page sends and ensure the service handles it: change
the mutationFn parameter type from CreateStudentPostCommentRequest to a union or
wrapper that includes FormData (e.g., { postId: string; payload:
CreateStudentPostCommentRequest | FormData }), update the call site in
createCommentSVC to pass the payload through unchanged, and modify
createStudentPostCommentSVC to detect and handle FormData payloads
(convert/extract fields or forward as-is) so both callers and the service accept
FormData without type errors.
- Around line 78-93: The updateInstructorPostCommentSVC mutation incorrectly
types its payload as FormData | CreateInstructorPostCommentRequest while the
underlying service method
instructorPostServiceSVC.updateInstructorPostCommentSVC only accepts the typed
request object; remove FormData from the union (use
CreateInstructorPostCommentRequest only) or add explicit handling/overload in
updateInstructorPostCommentSVC to convert FormData into
CreateInstructorPostCommentRequest before calling
instructorPostServiceSVC.updateInstructorPostCommentSVC so the types match and
calls align with the service signature.
- Around line 49-57: Update the service method
instructorPostServiceSVC.createInstructorPostCommentSVC to accept payload:
FormData | CreateInstructorPostCommentRequest (instead of only
CreateInstructorPostCommentRequest) and inside the implementation add a runtime
check (payload instanceof FormData) to choose headers: when payload is FormData,
send the request without manually setting Content-Type so the browser/HTTP
client can set multipart/form-data with boundary; otherwise set Content-Type:
application/json and JSON.stringify the body as needed; keep the existing call
site createInstructorPostCommentSVC in the hook unchanged except for the
adjusted service signature.

---

Nitpick comments:
In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx:
- Around line 140-365: The CommentItem component is duplicated (CommentItem in
this file and CommentItemSVC in PostCommentSVC.tsx); extract a single shared
component and import it from both places: create a new shared component (e.g.,
SharedCommentItem) that accepts the same props signature (comment:
CommonPostComment, onUpdate, onDelete) and preserves behavior for edit state,
getParsedContent, handleSave, file input ref, TiptapEditor usage, and image
rendering, then replace CommentItem and CommentItemSVC usages with the new
SharedCommentItem and remove the duplicated implementations so both educator and
SVC views reuse the same component.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6bee1957-7089-4a2b-b35c-7b2f78df5bb7

📥 Commits

Reviewing files that changed from the base of the PR and between 7e0d6c2 and 79c93a8.

📒 Files selected for processing (10)
  • src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
  • src/app/(dashboard)/educators/communication/[communicationId]/page.tsx
  • src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
  • src/app/(dashboard)/learners/communication/[communicationId]/_components/PostContentSVC.tsx
  • src/app/(dashboard)/learners/communication/[communicationId]/page.tsx
  • src/components/common/editor/TiptapEditor.tsx
  • src/hooks/SVC/useCommunicationSVC.ts
  • src/services/SVC/studentPost.service.ts
  • src/types/communication/instructorPost.ts
  • src/types/communication/studentPost.ts

@github-actions
Copy link

github-actions bot commented Mar 8, 2026

⚡️ Lighthouse Report

🏠 URL: http://localhost:3000/

Category Score
🔴 Performance 40
🟢 Accessibility 96
🟢 Best practices 93
🟢 SEO 100

상세 리포트는 하단의 lhci/url Checks 내 Details 링크를 확인해주세요! 🚀

@github-actions
Copy link

github-actions bot commented Mar 9, 2026

⚡️ Lighthouse Report

🏠 URL: http://localhost:3000/

Category Score
🔴 Performance 39
🟢 Accessibility 96
🟢 Best practices 93
🟢 SEO 100

상세 리포트는 하단의 lhci/url Checks 내 Details 링크를 확인해주세요! 🚀

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

♻️ Duplicate comments (2)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx (1)

347-357: ⚠️ Potential issue | 🟡 Minor

Prevent page scroll on Space activation.

This faux button now reacts to Space, but it still keeps the browser's default scroll behavior. Prevent the default first so it behaves like a native button.

Suggested fix
               onKeyDown={(e) => {
                 if (e.key === "Enter" || e.key === " ") {
+                  e.preventDefault();
                   window.open(img.fileUrl, "_blank");
                 }
               }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
around lines 347 - 357, The faux button div (the element with role="button" and
the onKeyDown handler in PostComment) currently opens the image on Space but
doesn't prevent the browser's default scroll; update the onKeyDown handler to
call e.preventDefault() when handling activation keys (e.key === "Enter" or
e.key === " " / "Spacebar") before calling window.open(img.fileUrl, "_blank") so
Space behaves like a native button and doesn't scroll the page.
src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx (1)

346-356: ⚠️ Potential issue | 🟡 Minor

Prevent page scroll on Space activation.

Same issue here: Space opens the preview, but it also keeps the browser's default scroll behavior.

Suggested fix
               onKeyDown={(e) => {
                 if (e.key === "Enter" || e.key === " ") {
+                  e.preventDefault();
                   window.open(img.fileUrl, "_blank");
                 }
               }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/`(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
around lines 346 - 356, The key handler on the clickable image div in
PostCommentSVC.tsx opens the image on Space but doesn't stop the browser's
default page-scroll; update the onKeyDown handler (the anonymous function
attached to the div with role="button" and tabIndex={0}) to call
e.preventDefault() when the activation key is Space (e.key === " " or e.code ===
"Space") before calling window.open; keep the existing Enter behavior but only
preventDefault for Space to avoid the scroll.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx:
- Around line 80-83: The file selection UI lets users attach files via
TiptapEditor (onFileUpload -> setSelectedFile) but the selected-file chip is
only rendered when !isNoticePost, so notice posts can pick files without preview
or a remove action; update the PostComment component to render the selected-file
preview/chip regardless of isNoticePost (or move the conditional into the submit
logic only), ensure the chip receives the selectedFile state and a clear handler
that calls setSelectedFile(null)/undefined, and include a visible remove
affordance (and thumbnail/alt text) so users can preview and remove attachments
before submitting; touch the TiptapEditor usage and the selected-file chip JSX
(the component that shows file name/thumbnail and the remove button) to
implement this.

In
`@src/app/`(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx:
- Around line 144-145: Update the exported callback types to accept the optional
file parameter so the component API matches CommentItemSVC's behavior: change
the onUpdate / onUpdateComment type signatures (in PostCommentSVCProps and any
exported types) from (id: string, content: JSONContent) => void to (id: string,
content: JSONContent, file?: File | null) => void; ensure all occurrences
(including the definitions around the onUpdate/onDelete lines and the referenced
onUpdateComment at the top of the file) are updated so callers can pass editFile
through.

In `@src/services/SVC/studentPost.service.ts`:
- Around line 133-140: The code is manually setting "Content-Type":
"multipart/form-data" when payload is a FormData instance (see isFormData and
the axiosClientSVC.patch calls), which prevents the browser from adding the
required boundary; remove the manual Content-Type for FormData payloads so
Axios/browser can set the header automatically—leave headers empty or omit the
header when isFormData is true in both the update/patch call around the
isFormData check and the second similar axiosClientSVC.post/patch block (lines
referenced) that sets headers for FormData.

---

Duplicate comments:
In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx:
- Around line 347-357: The faux button div (the element with role="button" and
the onKeyDown handler in PostComment) currently opens the image on Space but
doesn't prevent the browser's default scroll; update the onKeyDown handler to
call e.preventDefault() when handling activation keys (e.key === "Enter" or
e.key === " " / "Spacebar") before calling window.open(img.fileUrl, "_blank") so
Space behaves like a native button and doesn't scroll the page.

In
`@src/app/`(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx:
- Around line 346-356: The key handler on the clickable image div in
PostCommentSVC.tsx opens the image on Space but doesn't stop the browser's
default page-scroll; update the onKeyDown handler (the anonymous function
attached to the div with role="button" and tabIndex={0}) to call
e.preventDefault() when the activation key is Space (e.key === " " or e.code ===
"Space") before calling window.open; keep the existing Enter behavior but only
preventDefault for Space to avoid the scroll.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99a11adc-79a2-4e9c-9b13-888a8f9e0943

📥 Commits

Reviewing files that changed from the base of the PR and between 79c93a8 and 692eae9.

📒 Files selected for processing (6)
  • src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
  • src/app/(dashboard)/educators/communication/[communicationId]/page.tsx
  • src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
  • src/app/(dashboard)/learners/communication/[communicationId]/page.tsx
  • src/components/common/editor/TiptapEditor.tsx
  • src/services/SVC/studentPost.service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/common/editor/TiptapEditor.tsx

@github-actions
Copy link

github-actions bot commented Mar 9, 2026

⚡️ Lighthouse Report

🏠 URL: http://localhost:3000/

Category Score
🔴 Performance 41
🟢 Accessibility 96
🟢 Best practices 93
🟢 SEO 100

상세 리포트는 하단의 lhci/url Checks 내 Details 링크를 확인해주세요! 🚀

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

♻️ Duplicate comments (2)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx (2)

347-358: ⚠️ Potential issue | 🟡 Minor

Finish the keyboard accessibility fix for the image card.

This is closer now, but Space should call preventDefault() and the interactive container still needs an accessible name. Without those, keyboard users can still get page scroll on Space and screen readers won't announce the action clearly.

Proposed fix
             <div
               key={img.id || img.filename}
               className="relative w-full overflow-hidden rounded-lg cursor-pointer bg-slate-50/50"
               onClick={() => window.open(img.fileUrl, "_blank")}
               onKeyDown={(e) => {
                 if (e.key === "Enter" || e.key === " ") {
+                  e.preventDefault();
                   window.open(img.fileUrl, "_blank");
                 }
               }}
               role="button"
               tabIndex={0}
+              aria-label={`${img.filename} 이미지를 새 탭에서 열기`}
             >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
around lines 347 - 358, The image-card interactive container in PostComment must
stop Space from scrolling and provide an accessible name: update the onKeyDown
handler used on the div (the element keyed by img.id || img.filename with
role="button" and tabIndex={0}) so that when e.key === " " it calls
e.preventDefault() before opening the image, and ensure the element exposes an
accessible name by adding an aria-label (for example aria-label={`Open
${img.filename || 'image'} in new tab`} or use aria-labelledby pointing to a
visible caption) so screen readers announce the action.

91-114: ⚠️ Potential issue | 🟠 Major

The attachment chip still isn't an image preview.

These blocks only show a paperclip plus filename/size, so users still can't visually confirm the image they picked before submitting or saving. That leaves the “image preview” part of this change incomplete.

Also applies to: 301-330

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

In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
around lines 91 - 114, The attachment chip currently only shows a paperclip,
name and size in the PostComment component; update the JSX around selectedFile
to show an actual image preview when selectedFile.type startsWith('image/') by
creating an object URL (URL.createObjectURL) and rendering an <img> with proper
alt, sizes and object-fit/rounded styling in place of the Paperclip icon,
falling back to the Paperclip for non-image files; ensure you revoke the object
URL on cleanup and when clearing the file (useEffect inside PostComment tied to
selectedFile and call URL.revokeObjectURL when setSelectedFile(null) is invoked)
and keep existing controls (Button/X) intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx:
- Around line 186-188: When clearing the selected edit file you must also reset
the native file input element so re-selecting the same file works; update the
places that call setEditFile(null) (e.g., the save handler that calls
onUpdate(comment.id, editContent, editFile) followed by setIsEditing(false) and
setEditFile(null), and the cancel/clear handlers) to also clear the DOM input
via the ref: if (editFileInputRef.current) editFileInputRef.current.value = ''
after calling setEditFile(null). Ensure every code path that clears editFile
(including handlers referenced by onUpdate, setIsEditing, and setEditFile)
performs this reset so the native <input type="file"> is always cleared.
- Around line 29-33: The onUpdateComment contract currently uses a nullable File
(onUpdateComment(commentId: string, content: JSONContent, file?: File | null))
which conflates “keep existing attachment” vs “remove attachment”; change the
API to explicitly convey the intended attachment action (e.g.,
onUpdateComment(commentId: string, content: JSONContent, attachment: { action:
'keep' | 'replace' | 'remove'; file?: File })). Update all callers (e.g., where
editFile is passed from CommentItem and where PostComment invokes
onUpdateComment) to pass the appropriate discriminated object: action:'keep'
when leaving the current image, action:'remove' to delete it, and
action:'replace' with file when uploading a new file; adjust types/interfaces
and any handler logic that reads the third parameter accordingly.

---

Duplicate comments:
In
`@src/app/`(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx:
- Around line 347-358: The image-card interactive container in PostComment must
stop Space from scrolling and provide an accessible name: update the onKeyDown
handler used on the div (the element keyed by img.id || img.filename with
role="button" and tabIndex={0}) so that when e.key === " " it calls
e.preventDefault() before opening the image, and ensure the element exposes an
accessible name by adding an aria-label (for example aria-label={`Open
${img.filename || 'image'} in new tab`} or use aria-labelledby pointing to a
visible caption) so screen readers announce the action.
- Around line 91-114: The attachment chip currently only shows a paperclip, name
and size in the PostComment component; update the JSX around selectedFile to
show an actual image preview when selectedFile.type startsWith('image/') by
creating an object URL (URL.createObjectURL) and rendering an <img> with proper
alt, sizes and object-fit/rounded styling in place of the Paperclip icon,
falling back to the Paperclip for non-image files; ensure you revoke the object
URL on cleanup and when clearing the file (useEffect inside PostComment tied to
selectedFile and call URL.revokeObjectURL when setSelectedFile(null) is invoked)
and keep existing controls (Button/X) intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f52976a-69f3-4a07-885d-f9694c7143a9

📥 Commits

Reviewing files that changed from the base of the PR and between 692eae9 and 1ec00af.

📒 Files selected for processing (3)
  • src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
  • src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
  • src/services/SVC/studentPost.service.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
  • src/services/SVC/studentPost.service.ts

Copy link
Contributor

@yoorrll yoorrll left a comment

Choose a reason for hiding this comment

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

LGTM

@rklpoi5678 rklpoi5678 merged commit a7c28ef into dev Mar 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

FE 프런트 생성

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants