Skip to content

Conversation

aaryan610
Copy link
Member

@aaryan610 aaryan610 commented Sep 12, 2025

Description

This PR introduces a new, generic description input component. All the already existing feature specific components are now replaced with this.

Type of Change

  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring

Summary by CodeRabbit

  • New Features
    • Added DescriptionInput with a dedicated skeleton loader for description fields.
  • Refactor
    • Replaced IssueDescriptionInput with DescriptionInput across main, peek, and inbox views for consistent editing.
    • Switched to an explicit onSubmit flow and clarified asset/entity handling (entityId, fileAssetType).
  • Bug Fixes
    • Improved error visibility during archiving by logging errors as errors.
  • Chores
    • Added a public entry point re-export for the description input component.

Copy link
Contributor

coderabbitai bot commented Sep 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Introduces a generalized DescriptionInput component and DescriptionInputLoader, replaces IssueDescriptionInput usages across multiple issue views, refactors props to an entity-/asset-based API (entityId, fileAssetType, onSubmit, etc.), updates asset upload/search wiring, and adds an index re-export.

Changes

Cohort / File(s) Summary of changes
Description input module
apps/web/core/components/editor/rich-text/description-input/index.ts, apps/web/core/components/editor/rich-text/description-input/loader.tsx, apps/web/core/components/editor/rich-text/description-input/root.tsx
Added index.ts re-export. Added DescriptionInputLoader skeleton component. Renamed/refactored IssueDescriptionInputDescriptionInput with new Props (entityId, fileAssetType, onSubmit, disabledExtensions, swrDescription, optional projectId, etc.), moved submit logic to external onSubmit, updated rich-text editor wiring, and changed asset upload/search payloads to use entity identifiers.
Inbox integration
apps/web/core/components/inbox/content/issue-root.tsx
Replaced IssueDescriptionInput with DescriptionInput and DescriptionInputLoader; wired onSubmit to call issueOperations.update; pass entityId and fileAssetType; adjusted props and minor logging change.
Issue detail main content
apps/web/core/components/issues/issue-detail/main-content.tsx
Swapped to DescriptionInput, added EFileAssetType import, implemented onSubmit wrapper to update description_html via issueOperations.update, updated prop mapping and container class.
Peek overview integration
apps/web/core/components/issues/peek-overview/issue-detail.tsx
Replaced IssueDescriptionInput with DescriptionInput; added EFileAssetType; provided onSubmit to call issueOperations.update; preserved disabled/archive guards and editorRef wiring; minor import reorg.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant DI as DescriptionInput
  participant Caller as Issue view (onSubmit wrapper)
  participant Ops as issueOperations
  participant API as Backend

  User->>DI: Edit description, click submit
  DI->>Caller: onSubmit(value)  ## new external callback
  Caller->>Ops: update(workspaceSlug, project_id, id, {description_html: value})
  Ops->>API: PUT /issues/:id {description_html}
  API-->>Ops: 200 OK
  Ops-->>Caller: Promise resolved
  Caller-->>DI: setIsSubmitting(false) / UI update

  Note over DI: When description not ready -> show DescriptionInputLoader
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

✍️editor

Suggested reviewers

  • Palanikannan1437
  • sriramveeraghanta

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "[WIKI-538] chore: common description component" succinctly and accurately summarizes the primary change — introducing a shared/common description input component — and includes the ticket reference and conventional commit prefix; it is concise, relevant, and not noisy.
Description Check ✅ Passed The PR description contains the required "Description" and "Type of Change" sections from the repository template and correctly states that a generic description input replaces feature-specific components, but it is brief and omits the template's "Screenshots and Media", "Test Scenarios", and "References" sections as well as implementation/migration details (API/prop changes) that would aid reviewers.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

I nibble on docs with a gentle bite,
New input sprouts to make the edits right.
entityId, assets, onSubmit in tow,
Loaders shimmer while changes flow.
I thump two paws: "Refactor done!" — hop, go! 🐇✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/common-description-component

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.

Copy link

makeplane bot commented Sep 12, 2025

Pull Request Linked with Plane Work Items

Comment Automatically Generated by Plane

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the issue description input component by replacing multiple feature-specific description components with a generic DescriptionInput component. The refactoring standardizes the API for description inputs across the application while maintaining the same functionality.

  • Replaces IssueDescriptionInput with the new generic DescriptionInput component
  • Introduces a reusable description input loader component
  • Updates import statements to include new file asset types and utilities

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
apps/web/core/components/issues/peek-overview/issue-detail.tsx Updates to use the new DescriptionInput component with standardized props
apps/web/core/components/issues/issue-detail/main-content.tsx Replaces issue-specific description input with generic component
apps/web/core/components/inbox/content/issue-root.tsx Migrates inbox issue description to use the new component and loader
apps/web/core/components/editor/rich-text/description-input/root.tsx Implements the new generic description input component with comprehensive props
apps/web/core/components/editor/rich-text/description-input/loader.tsx Creates a dedicated loader component for description input
apps/web/core/components/editor/rich-text/description-input/index.ts Adds barrel export for the description input components
Comments suppressed due to low confidence (1)

apps/web/core/components/editor/rich-text/description-input/root.tsx:1

  • The swrDescription parameter is nullable but projectId is not validated for null/undefined before being passed to the API call. Consider adding validation or making the parameter handling consistent.
"use client";

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@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: 5

Caution

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

⚠️ Outside diff range comments (2)
apps/web/core/components/issues/peek-overview/issue-detail.tsx (2)

1-1: Fix client directive.

Must be "use client" (space), otherwise the file won’t be treated as a client component.

-"use-client";
+"use client";

85-90: Null/undefined check is incorrect (always truthy).

Use a nullish check and handle empty string explicitly.

-  const issueDescription =
-    issue.description_html !== undefined || issue.description_html !== null
-      ? issue.description_html != ""
-        ? issue.description_html
-        : "<p></p>"
-      : undefined;
+  const issueDescription =
+    issue.description_html == null
+      ? undefined
+      : issue.description_html.trim() === ""
+        ? "<p></p>"
+        : issue.description_html;
🧹 Nitpick comments (8)
apps/web/core/components/editor/rich-text/description-input/index.ts (1)

1-1: Unify public surface: also re-export the loader from the index.

This keeps consumers from importing a sibling path for the loader.

 export * from "./root";
+export * from "./loader";
apps/web/core/components/editor/rich-text/description-input/loader.tsx (1)

15-17: Tailwind padding classes are redundant.

p-3 py-2 pt-3 resolves to top: 0.75rem, bottom: 0.5rem, x: 0.75rem. Use explicit shorthands to avoid overrides.

-        "min-h-[120px] max-h-64 space-y-2 overflow-hidden rounded-md border border-custom-border-200 p-3 py-2 pt-3",
+        "min-h-[120px] max-h-64 space-y-2 overflow-hidden rounded-md border border-custom-border-200 px-3 pt-3 pb-2",
apps/web/core/components/inbox/content/issue-root.tsx (1)

192-196: Match loader layout with editor container to avoid layout shift.

Use the same container classes as the editor for a seamless skeleton.

-        {loader === "issue-loading" ? (
-          <DescriptionInputLoader />
+        {loader === "issue-loading" ? (
+          <DescriptionInputLoader className="-ml-3 border-none" />
apps/web/core/components/editor/rich-text/description-input/root.tsx (5)

189-207: Bind Controller value to the editor when SWR value is absent to avoid saving stale form data.

Controller currently ignores value, so form state can drift from displayed content if no SWR value is provided. Use form value as a fallback.

-render={({ field: { onChange } }) => (
+render={({ field: { onChange, value } }) => (
   <RichTextEditor
@@
-    value={swrDescription ?? null}
+    value={swrDescription ?? value ?? null}
@@
     onChange={(_description, description_html) => {
       setIsSubmitting("submitting");
       onChange(description_html);
       hasUnsavedChanges.current = true;
       debouncedFormSave();
     }}

198-199: Avoid passing empty string as workspaceId.

Prefer undefined when unknown to prevent accidental "truthy" empty ID handling downstream.

-workspaceId={workspaceDetails?.id ?? ""}
+workspaceId={workspaceDetails?.id}

209-213: Don’t send project_id: undefined in search payload.

Drop the key when projectId is falsy to keep API contracts clean.

-  await workspaceService.searchEntity(workspaceSlug?.toString() ?? "", {
-    ...payload,
-    project_id: projectId,
-  })
+  await workspaceService.searchEntity(workspaceSlug ?? "", {
+    ...payload,
+    ...(projectId ? { project_id: projectId } : {}),
+  })

229-231: Use console.error for failures and a clearer message.

-  console.log("Error in uploading asset:", error);
+  console.error("Error uploading editor asset:", error);

73-74: TNameDescriptionLoader supports "submitting" and "submitted" — types are correct.

Confirmed: packages/types/src/common.ts exports TNameDescriptionLoader = "submitting" | "submitted" | "saved". Optional: replace the string-union with an exported enum/const to avoid magic strings across the codebase.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3e7cfd and 31f3a81.

📒 Files selected for processing (6)
  • apps/web/core/components/editor/rich-text/description-input/index.ts (1 hunks)
  • apps/web/core/components/editor/rich-text/description-input/loader.tsx (1 hunks)
  • apps/web/core/components/editor/rich-text/description-input/root.tsx (4 hunks)
  • apps/web/core/components/inbox/content/issue-root.tsx (3 hunks)
  • apps/web/core/components/issues/issue-detail/main-content.tsx (2 hunks)
  • apps/web/core/components/issues/peek-overview/issue-detail.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
apps/web/core/components/editor/rich-text/description-input/loader.tsx (1)
packages/ui/src/loader.tsx (1)
  • Loader (30-30)
apps/web/core/components/issues/peek-overview/issue-detail.tsx (1)
apps/web/core/components/editor/rich-text/description-input/root.tsx (1)
  • DescriptionInput (88-241)
apps/web/core/components/issues/issue-detail/main-content.tsx (1)
apps/web/core/components/editor/rich-text/description-input/root.tsx (1)
  • DescriptionInput (88-241)
apps/web/core/components/editor/rich-text/description-input/root.tsx (4)
packages/editor/src/core/types/extensions.ts (1)
  • TExtensions (1-1)
packages/editor/src/core/types/editor.ts (1)
  • EditorRefApi (99-137)
packages/utils/src/work-item/base.ts (1)
  • getDescriptionPlaceholderI18n (223-227)
apps/web/core/components/editor/rich-text/description-input/loader.tsx (1)
  • DescriptionInputLoader (9-38)
apps/web/core/components/inbox/content/issue-root.tsx (2)
apps/web/core/components/editor/rich-text/description-input/loader.tsx (1)
  • DescriptionInputLoader (9-38)
apps/web/core/components/editor/rich-text/description-input/root.tsx (1)
  • DescriptionInput (88-241)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Build and lint web apps
🔇 Additional comments (4)
apps/web/core/components/issues/issue-detail/main-content.tsx (1)

137-142: Await/return the update promise in onSubmit.

Return (or await) the promise from issueOperations.update so the debounced autosave only marks "submitted" after persistence.

File: apps/web/core/components/issues/issue-detail/main-content.tsx Lines: 137-142

-          onSubmit={async (value) => {
+          onSubmit={async (value) => {
             if (!issue.id || !issue.project_id) return;
-            issueOperations.update(workspaceSlug, issue.project_id, issue.id, {
+            return issueOperations.update(workspaceSlug, issue.project_id, issue.id, {
               description_html: value,
             });
-          }}
+          }}

Run to find other similar callsites (avoid process-substitution errors):

#!/bin/bash
rg -l --hidden --no-ignore-vcs 'onSubmit=\{\s*async' -g '!node_modules/**' > /tmp/on_submit_files.txt || true
rg -l --hidden --no-ignore-vcs 'issueOperations\.update\(' -g '!node_modules/**' > /tmp/update_files.txt || true
sort /tmp/on_submit_files.txt -o /tmp/on_submit_files.txt
sort /tmp/update_files.txt -o /tmp/update_files.txt
comm -12 /tmp/on_submit_files.txt /tmp/update_files.txt || true
apps/web/core/components/inbox/content/issue-root.tsx (1)

153-153: Good change: escalate logging on failure.

Switching to console.error improves visibility in monitoring/log capture.

apps/web/core/components/editor/rich-text/description-input/root.tsx (2)

84-89: Nice generalization and clear docstring.

The componentization and prop hygiene look good.


45-46: No action required — RichTextEditor forwards EditorRefApi.
RichTextEditorWithRef (packages/editor) and apps/web's RichTextEditor are declared with forwardRef, so passing editorRef?: React.RefObject to ref is type-compatible.

Comment on lines +24 to +27
type TFormData = {
id: string;
description_html: string;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make description_html strictly string (prevent undefined) and align initial/reset values.

TFormData.description_html is typed as string, but reset() and local state can set it to undefined, forcing extra ?? "<p></p>" later and risking type drift. Normalize at source and drop the redundant fallback in submit.

Apply:

@@
 const [localDescription, setLocalDescription] = useState({
-  id: entityId,
-  description_html: initialValue,
+  id: entityId,
+  description_html:
+    initialValue?.trim() === "" ? "<p></p>" : (initialValue ?? "<p></p>"),
 });
@@
   const { handleSubmit, reset, control } = useForm<TFormData>({
     defaultValues: {
-      id: entityId,
-      description_html: initialValue || "",
+      id: entityId,
+      description_html:
+        initialValue?.trim() === "" ? "<p></p>" : (initialValue ?? "<p></p>"),
     },
   });
@@
   const handleDescriptionFormSubmit = useCallback(
     async (formData: TFormData) => {
-      await onSubmit(formData.description_html ?? "<p></p>");
+      await onSubmit(formData.description_html);
     },
     [onSubmit]
   );
@@
   reset({
-    id: entityId,
-    description_html: initialValue?.trim() === "" ? "<p></p>" : initialValue,
+    id: entityId,
+    description_html:
+      initialValue?.trim() === "" ? "<p></p>" : (initialValue ?? "<p></p>"),
   });
   setLocalDescription({
-    id: entityId,
-    description_html: initialValue?.trim() === "" ? "<p></p>" : initialValue,
+    id: entityId,
+    description_html:
+      initialValue?.trim() === "" ? "<p></p>" : (initialValue ?? "<p></p>"),
   });

Also applies to: 105-108, 119-124, 137-144, 128-131

🤖 Prompt for AI Agents
In apps/web/core/components/editor/rich-text/description-input/root.tsx around
lines 24-27 (and also apply same change to blocks at 105-108, 119-124, 128-131,
137-144): TFormData currently allows description_html to become undefined via
initial state/reset which forces later fallbacks; change the initialization and
reset logic so description_html is always a concrete string (e.g., default to
"<p></p>") and update any local state types to string (not string | undefined),
then remove the redundant "?? '<p></p>'" fallback in submit paths so the value
flow is consistently a non-undefined string. Ensure all assignments, form
resets, and setState calls use the string default to keep types aligned.

Comment on lines +126 to 133
// submit handler
const handleDescriptionFormSubmit = useCallback(
async (formData: Partial<TIssue>) => {
await issueOperations.update(workspaceSlug, projectId, issueId, {
description_html: formData.description_html ?? "<p></p>",
});
async (formData: TFormData) => {
await onSubmit(formData.description_html ?? "<p></p>");
},
[workspaceSlug, projectId, issueId, issueOperations]
[onSubmit]
);

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Debounced save can capture stale onSubmit; also treat failures without clearing dirty flag.

Current deps omit onSubmit, so the debounced closure can call an outdated handler. Use a ref to keep the latest callback, and only clear hasUnsavedChanges on success.

@@
-// submit handler
-const handleDescriptionFormSubmit = useCallback(
-  async (formData: TFormData) => {
-    await onSubmit(formData.description_html);
-  },
-  [onSubmit]
-);
+// keep latest submit handler without re-creating debounced fn
+const onSubmitRef = useRef(onSubmit);
+useEffect(() => {
+  onSubmitRef.current = onSubmit;
+}, [onSubmit]);
+
+// submit handler
+const handleDescriptionFormSubmit = useCallback(async (formData: TFormData) => {
+  await onSubmitRef.current(formData.description_html);
+}, []);
@@
-const debouncedFormSave = useCallback(
-  debounce(async () => {
-    handleSubmit(handleDescriptionFormSubmit)().finally(() => {
-      setIsSubmitting("submitted");
-      hasUnsavedChanges.current = false;
-    });
-  }, 1500),
-  [entityId, handleSubmit]
-);
+const debouncedFormSave = useCallback(
+  debounce(async () => {
+    handleSubmit(handleDescriptionFormSubmit)()
+      .then(() => {
+        setIsSubmitting("submitted");
+        hasUnsavedChanges.current = false;
+      })
+      .catch((error) => {
+        console.error("Auto-save failed:", error);
+        // consider exposing a 'failed' state if TNameDescriptionLoader supports it
+      });
+  }, 1500),
+  [entityId, handleSubmit]
+);

Also applies to: 152-160

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/core/components/issues/peek-overview/issue-detail.tsx (1)

6-11: Nit: keep import grouping consistent (duplicate of prior feedback).

Maintain consistent grouping of plane utils/components imports; this matches project conventions and reduces churn.

🧹 Nitpick comments (1)
apps/web/core/components/issues/peek-overview/issue-detail.tsx (1)

134-139: Optional: explicitly return the update promise for clarity/tests.

Returning the awaited promise makes intent explicit and can help in unit tests that assert on the resolved value.

-          await issueOperations.update(workspaceSlug, issue.project_id, issue.id, {
+          return await issueOperations.update(workspaceSlug, issue.project_id, issue.id, {
             description_html: value,
           });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31f3a81 and 670e986.

📒 Files selected for processing (4)
  • apps/web/core/components/editor/rich-text/description-input/loader.tsx (1 hunks)
  • apps/web/core/components/inbox/content/issue-root.tsx (3 hunks)
  • apps/web/core/components/issues/issue-detail/main-content.tsx (2 hunks)
  • apps/web/core/components/issues/peek-overview/issue-detail.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/core/components/editor/rich-text/description-input/loader.tsx
  • apps/web/core/components/issues/issue-detail/main-content.tsx
  • apps/web/core/components/inbox/content/issue-root.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/core/components/issues/peek-overview/issue-detail.tsx (1)
apps/web/core/components/editor/rich-text/description-input/root.tsx (1)
  • DescriptionInput (88-241)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build and lint web apps
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/web/core/components/issues/peek-overview/issue-detail.tsx (1)

127-143: LGTM: DescriptionInput integration and autosave flow are correct.

Passing editorRef, entityId, fileAssetType, workspaceSlug/projectId, and wiring onSubmit now awaits the update—addressing the earlier “return/await” concern so autosave completion mirrors the actual update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant