Conversation
📝 WalkthroughWalkthroughAdds 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
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
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
⚡️ Lighthouse Report🏠 URL: http://localhost:3000/
|
There was a problem hiding this comment.
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 | 🟠 MajorMissing FormData in createCommentSVC payload type.
The
createCommentSVCmutation's payload type is stillCreateStudentPostCommentRequestonly, but the page component (learners/.../page.tsxline 210) passesformDatato 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,
createStudentPostCommentSVCin 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 | 🔴 CriticalSame type mismatch issue for updateInstructorPostCommentSVC.
The payload type union includes
FormData, but the underlying service function at lines 57-67 ofstudentPost.service.tsdoesn'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 | 🔴 CriticalType mismatch: service function doesn't accept FormData.
The hook's payload type is
FormData | CreateInstructorPostCommentRequest, butinstructorPostServiceSVC.createInstructorPostCommentSVConly acceptsCreateInstructorPostCommentRequest(see context snippet fromstudentPost.service.ts:44-54). This will cause a TypeScript compilation error and, if bypassed, runtime issues since the service doesn't set themultipart/form-dataContent-Type for FormData payloads.The service function needs to be updated to:
- Accept
FormData | CreateInstructorPostCommentRequestin its signature- Add
instanceof FormDatacheck 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 | 🔴 CriticalType mismatch:
onUpdateCommentis missing thefileparameter.The prop type defines
onUpdateCommentwithout a file parameter, butCommentItemSVC(line 144) declaresonUpdatewithfile?: File | nulland 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 | 🟡 MinorMissing
acceptattribute 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 | 🟡 MinorMissing
acceptattribute 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 extractingCommentIteminto a shared component.
CommentItemin this file andCommentItemSVCinPostCommentSVC.tsxare 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
📒 Files selected for processing (10)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsxsrc/app/(dashboard)/educators/communication/[communicationId]/page.tsxsrc/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsxsrc/app/(dashboard)/learners/communication/[communicationId]/_components/PostContentSVC.tsxsrc/app/(dashboard)/learners/communication/[communicationId]/page.tsxsrc/components/common/editor/TiptapEditor.tsxsrc/hooks/SVC/useCommunicationSVC.tssrc/services/SVC/studentPost.service.tssrc/types/communication/instructorPost.tssrc/types/communication/studentPost.ts
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
Show resolved
Hide resolved
src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
Show resolved
Hide resolved
⚡️ Lighthouse Report🏠 URL: http://localhost:3000/
|
⚡️ Lighthouse Report🏠 URL: http://localhost:3000/
|
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx (1)
347-357:⚠️ Potential issue | 🟡 MinorPrevent 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 | 🟡 MinorPrevent 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
📒 Files selected for processing (6)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsxsrc/app/(dashboard)/educators/communication/[communicationId]/page.tsxsrc/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsxsrc/app/(dashboard)/learners/communication/[communicationId]/page.tsxsrc/components/common/editor/TiptapEditor.tsxsrc/services/SVC/studentPost.service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/common/editor/TiptapEditor.tsx
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx
Show resolved
Hide resolved
src/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsx
Show resolved
Hide resolved
⚡️ Lighthouse Report🏠 URL: http://localhost:3000/
|
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsx (2)
347-358:⚠️ Potential issue | 🟡 MinorFinish 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 | 🟠 MajorThe 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
📒 Files selected for processing (3)
src/app/(dashboard)/educators/communication/[communicationId]/_components/PostComment.tsxsrc/app/(dashboard)/learners/communication/[communicationId]/_components/PostCommentSVC.tsxsrc/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
🔗 관련 이슈
✨ 작업 단계 및 변경 사항
PostCommentSVC, PostComment 동일
🧪 테스트 방법
리뷰어가 확인해야 할 핵심 포인트를 적어주세요.
📸 스크린샷 (선택)
✅ 체크리스트
Phase에 맞는 이슈 체크리스트를 모두 완료했는가?Summary by CodeRabbit