Skip to content

[WEB-3748, 3749] feat: work item description version history#6863

Merged
sriramveeraghanta merged 16 commits intopreviewfrom
feat/work-item-versions
Apr 4, 2025
Merged

[WEB-3748, 3749] feat: work item description version history#6863
sriramveeraghanta merged 16 commits intopreviewfrom
feat/work-item-versions

Conversation

@aaryan610
Copy link
Member

@aaryan610 aaryan610 commented Apr 2, 2025

Description

This PR adds description version history to work items and intake work items.

Type of Change

  • Feature (non-breaking change which adds functionality)

Media

Screen.Recording.2025-04-02.at.17.59.16.mov

Summary by CodeRabbit

  • New Features
    • Introduced a comprehensive version history interface allowing users to view, navigate, and restore previous description versions with detailed edit information.
    • Added new visual components, such as dropdown menus and modals, for seamless version selection.
    • Enhanced editor behavior with flexible update options and incorporated a “copy markdown” action.
    • Expanded localization with additional multi-language support for version history labels.
    • Added new API endpoints for accessing and managing description versions of work items.
  • Bug Fixes
    • Improved filtering accuracy for displaying version data.
    • Updated error handling to provide clearer permission-related responses.
  • Refactor
    • Streamlined API routes and naming for clearer description version management.
  • Chores
    • Added new services for managing work item description versions and integrated them into the existing architecture.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 2, 2025

Walkthrough

This pull request introduces new URL endpoints and view methods for handling description versions of intake work items and work items. It renames and restructures existing endpoints, updates serializers and permission checks, and revises view logic to trigger asynchronous tasks on create/update operations. Additionally, it enhances localization by adding new translation sections, updates editor hook signatures to allow optional update emissions, and provides new TypeScript declarations. New React components and service classes are also implemented for managing and displaying description version information.

Changes

File(s) Change Summary
apiserver/plane/app/urls/intake.py
apiserver/plane/app/urls/issue.py
Added new URL patterns for accessing description versions. Renamed endpoint from IssueDescriptionVersionEndpoint to WorkItemDescriptionVersionEndpoint, updating path names and associated views.
apiserver/plane/app/views/__init__.py
apiserver/plane/app/views/intake/base.py
apiserver/plane/app/views/issue/base.py
apiserver/plane/app/views/issue/version.py
Updated import statements and endpoint classes. Adjusted view methods in both intake and issue modules to process description version requests, including serializer updates and permission checks.
packages/constants/src/issue/common.ts Added new enum member WORK_ITEMS to EIssueServiceType.
packages/editor/src/core/hooks/use-editor.ts
packages/editor/src/core/hooks/use-read-only-editor.ts
packages/editor/src/core/types/editor.ts
Updated setEditorValue method signatures to include an optional emitUpdate parameter, enabling conditional update emissions.
packages/i18n/src/locales/*/translations.json Added new translation sections (description_versions) across multiple locales and a new "copy_markdown" entry in the English translations.
packages/types/src/description_version.d.ts
packages/types/src/index.d.ts
packages/types/src/issues/issue.d.ts
Introduced new TypeScript types for description versions and updated exports. Expanded TIssueServiceType to include the new WORK_ITEMS value.
web/core/components/core/description-versions/* Added new React components (dropdown-item.tsx, dropdown.tsx, modal.tsx, root.tsx, and index.ts) to display and manage description versions.
web/core/components/inbox/content/issue-root.tsx
web/core/components/issues/*
Enhanced issue-related components with new editor references, integrated version services, and conditionally rendered the description versions component.
web/core/services/inbox/index.ts
web/core/services/inbox/intake-work_item_version.service.ts
Added a new export and introduced the IntakeWorkItemVersionService class for API calls related to intake work item description versions.
web/core/services/issue/index.ts
web/core/services/issue/work_item_version.service.ts
Added a new export and introduced the WorkItemVersionService class to handle API operations for work item description versions.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant A as APIServer
    participant IV as IntakeWorkItemDescriptionVersionEndpoint
    participant DB as Database

    C->>A: GET /workspaces/{slug}/projects/{project_id}/intake-work-items/{work_item_id}/description-versions/
    A->>IV: Invoke get() method
    IV->>DB: Query paginated description versions
    DB-->>IV: Return version list
    IV-->>A: Send JSON response
    A-->>C: 200 OK with version data
Loading
sequenceDiagram
    participant C as Client
    participant A as APIServer
    participant WV as WorkItemDescriptionVersionEndpoint
    participant PM as ProjectMember Model
    participant DB as Database

    C->>A: GET /workspaces/{slug}/projects/{project_id}/work-items/{work_item_id}/description-versions/[version_id]
    A->>WV: Invoke get() method
    WV->>PM: Check user permissions
    PM-->>WV: Return permission status
    WV->>DB: Fetch description version(s)
    DB-->>WV: Return requested data
    WV-->>A: JSON response
    A-->>C: 200 OK with version details
Loading

Suggested labels

🌟improvement, 🧹chore

Poem

I hopped through lines of code so spry,
Adding endpoints that reach the sky.
With hooks and services in every byte,
My versioned trail gleams so bright.
A rabbit’s cheer for code so fine 🐇✨
Leap into progress—one hop at a time!


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 69f972a and e9ed142.

📒 Files selected for processing (1)
  • web/core/components/core/description-versions/modal.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/core/components/core/description-versions/modal.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@makeplane
Copy link

makeplane bot commented Apr 2, 2025

Pull Request Linked with Plane Work Items

Comment Automatically Generated by Plane

@aaryan610 aaryan610 changed the title [WEB-3748, 3749] Feat/work item versions [WEB-3748, 3749] feat: work item description version history Apr 2, 2025
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: 2

🧹 Nitpick comments (35)
packages/i18n/src/locales/vi-VN/translations.json (2)

2325-2329: Cycle Block Validation:
The "cycle" section now uses a plural formatting pattern with "label": "{count, plural, one {chu kỳ} other {chu kỳ}}" and a message for the empty state. In Vietnamese, pluralization is often handled by context rather than word changes. Please double-check that this behavior is consistent with other locale files and aligns with your UI’s expectations.


2335-2340: New Description Versions Section:
The newly added "description_versions" block defines the keys "last_edited_by", "previously_edited_by", and "edited_by" with clear and straightforward translations. These entries will support the added version history feature effectively. Please verify that these translations align with the corresponding labels in other language files and match your overall tone.

packages/i18n/src/locales/de/translations.json (1)

2324-2340: Minor Whitespace and Formatting Adjustments in Module-Related Keys

The blocks for "project_modules", "cycle", and "module" appear to show slight whitespace or formatting modifications. These changes do not affect functionality but please ensure that the formatting remains consistent with the rest of the locale files and follows the project’s JSON style guidelines.

packages/i18n/src/locales/ru/translations.json (1)

2379-2382: New "description_versions" Section Added

The newly added translation block provides keys for version history—specifically, "last_edited_by", "previously_edited_by", and "edited_by". The Russian translations are clear and appropriate. Please ensure these keys are consistently used with the corresponding functionality in the UI and that similar changes are mirrored across locales for uniform behavior.

packages/i18n/src/locales/ro/translations.json (1)

2373-2377: New Translation Section for Version History

The new "description_versions" section adds the keys "last_edited_by", "previously_edited_by", and "edited_by" with their respective Romanian translations. This is a useful enhancement for supporting version history information. Please verify that these new entries follow the same structure as in other locale files for consistency.

packages/i18n/src/locales/ua/translations.json (1)

2337-2341: New Localization Keys for Description Versions Added

The new "description_versions" section with keys "last_edited_by", "previously_edited_by", and "edited_by" is introduced correctly to support the version history feature for work items. The translations are concise and clear. Please ensure that these terms remain consistent with the corresponding keys in other locale files for uniformity across the application.

packages/i18n/src/locales/pl/translations.json (1)

502-503: Formatting Adjustment: Removal of Unnecessary Blank Line
The removal of the blank line between the "new_password_must_be_different_from_old_password" and "edited" entries improves the file’s visual consistency without affecting functionality.

web/core/components/issues/description-input.tsx (1)

8-27: Consider adding JSDoc comments for the new properties

While the code changes look good, adding JSDoc comments for the new ref properties would improve code documentation and help other developers understand their purpose and usage.

-  editorReadOnlyRef?: React.RefObject<EditorReadOnlyRefApi>;
-  editorRef?: React.RefObject<EditorRefApi>;
+  /** Reference to access the read-only editor API methods (for version history, copying, etc.) */
+  editorReadOnlyRef?: React.RefObject<EditorReadOnlyRefApi>;
+  /** Reference to access the editable editor API methods (for version history, copying, etc.) */
+  editorRef?: React.RefObject<EditorRefApi>;
web/core/components/core/description-versions/dropdown-item.tsx (1)

14-32: Well-implemented dropdown item component with clean structure.

The component follows React best practices and properly handles version data display. Consider these minor improvements:

  1. Add accessibility attributes for better screen reader support
  2. Include a fallback for when creator information is unavailable
-    <CustomMenu.MenuItem key={version.id} className="flex items-center gap-1" onClick={() => onClick(version.id)}>
+    <CustomMenu.MenuItem 
+      key={version.id} 
+      className="flex items-center gap-1" 
+      onClick={() => onClick(version.id)}
+      aria-label={`Version by ${versionCreator?.display_name || 'Unknown user'} from ${calculateTimeAgo(version.last_saved_at)}`}
+    >
       <span className="flex-shrink-0">
         <Avatar name={versionCreator?.display_name} size="sm" src={versionCreator?.avatar_url} />
       </span>
       <p className="text-xs text-custom-text-200 flex items-center gap-1.5">
-        <span className="font-medium">{versionCreator?.display_name}</span>
+        <span className="font-medium">{versionCreator?.display_name || 'Unknown user'}</span>
         <span>{calculateTimeAgo(version.last_saved_at)}</span>
       </p>
     </CustomMenu.MenuItem>
apiserver/plane/app/urls/intake.py (1)

60-69: URL patterns for description versions look good, but consider unique naming.

The URL patterns are well-structured and follow Django conventions, providing proper access to intake work item description versions.

Both URL patterns use the same name ("intake-work-item-versions"). Consider giving the second URL pattern a more specific name to differentiate it, such as "intake-work-item-version-detail":

path(
    "workspaces/<str:slug>/projects/<uuid:project_id>/intake-work-items/<uuid:work_item_id>/description-versions/<uuid:pk>/",
    IntakeWorkItemDescriptionVersionEndpoint.as_view(),
-   name="intake-work-item-versions",
+   name="intake-work-item-version-detail",
),

This makes URL reverse lookup more explicit when you need to reference a specific version.

web/core/components/core/description-versions/modal.tsx (4)

54-64: Add error handling to clipboard functionality and use translation for message.

The copy functionality works, but lacks error handling for the promise and has a hardcoded message that should use translations.

  const handleCopyMarkdown = useCallback(() => {
    if (!editorRef.current) return;
-   copyTextToClipboard(editorRef.current.getMarkDown()).then(() =>
-     setToast({
-       type: TOAST_TYPE.SUCCESS,
-       title: t("toast.success"),
-       message: "Markdown copied to clipboard.",
-     })
-   );
+   copyTextToClipboard(editorRef.current.getMarkDown())
+     .then(() => {
+       setToast({
+         type: TOAST_TYPE.SUCCESS,
+         title: t("toast.success"),
+         message: t("common.copied_to_clipboard"),
+       });
+     })
+     .catch(() => {
+       setToast({
+         type: TOAST_TYPE.ERROR,
+         title: t("toast.error"),
+         message: t("common.errors.default.message"),
+       });
+     });
  }, [t]);

66-113: Improve accessibility for the modal interface and navigation.

The modal could benefit from better accessibility features for screen readers.

Consider adding:

  1. An aria-label for the modal to describe its purpose
  2. Appropriate aria-labels for navigation buttons
  3. A more semantic header structure
-   <ModalCore isOpen={isOpen} handleClose={handleClose}>
+   <ModalCore isOpen={isOpen} handleClose={handleClose} aria-label={t("description_versions.modal_title", "Description Version History")}>
      <div className="p-4" data-prevent-outside-click>
        {/* Header */}
-       <div className="flex items-center justify-between gap-2 py-0.5">
+       <div className="flex items-center justify-between gap-2 py-0.5" role="heading" aria-level={1}>
          <div className="flex-shrink-0 flex items-center gap-2 text-sm">
            <p className="flex items-center gap-1">
              {t("description_versions.edited_by")}
              <span className="flex-shrink-0">
                <Avatar size="sm" src={versionCreator?.avatar_url} name={versionCreator?.display_name} />
              </span>
            </p>
            <p className="flex-shrink-0 text-custom-text-200">
              {calculateTimeAgo(activeVersionDetails?.last_saved_at ?? "")}
            </p>
          </div>
          <div className="flex-shrink-0 flex items-center">
            <button
              type="button"
              onClick={() => handleNavigation("prev")}
+             aria-label={t("common.previous")}
              className={cn(
                "size-6 text-custom-text-200 grid place-items-center rounded outline-none transition-colors",
                {
                  "hover:bg-custom-background-80": !isPrevDisabled,
                  "opacity-50": isPrevDisabled,
                }
              )}
              disabled={isPrevDisabled}
            >
              <ChevronLeft className="size-4" />
            </button>
            <button
              type="button"
              onClick={() => handleNavigation("next")}
+             aria-label={t("common.next")}
              className={cn(
                "size-6 text-custom-text-200 grid place-items-center rounded outline-none transition-colors",
                {
                  "hover:bg-custom-background-80": !isNextDisabled,
                  "opacity-50": isNextDisabled,
                }
              )}
              disabled={isNextDisabled}
            >
              <ChevronRight className="size-4" />
            </button>
          </div>
        </div>

50-51: Simplify workspace ID retrieval with optional chaining.

The current workspaceId derivation is more complex than needed.

- const workspaceId = getWorkspaceBySlug(workspaceSlug?.toString() ?? "")?.id ?? "";
+ const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id ?? "";

Since workspaceSlug is already typed as a string in your props, there's no need to call toString() on it.


146-159: Add screen reader text to the copy button for better accessibility.

The copy button uses only an icon, which isn't descriptive for screen readers.

  <Tooltip tooltipContent={t("common.actions.copy_markdown")}>
    <button
      type="button"
      className={cn(
        "flex-shrink-0",
        getButtonStyling("neutral-primary", "sm"),
        "border-none grid place-items-center"
      )}
      onClick={handleCopyMarkdown}
+     aria-label={t("common.actions.copy_markdown")}
    >
      <Copy className="size-4" />
+     <span className="sr-only">{t("common.actions.copy_markdown")}</span>
    </button>
  </Tooltip>
apiserver/plane/app/views/issue/version.py (2)

87-107: Consider using a constant for role code instead of hardcoded value

The code uses a hardcoded value of 5 for the role check. Consider using a constant or enum from ROLE to improve readability and maintainability.

- role=5,
+ role=ROLE.GUEST,

75-75: Maintain consistency in class naming

The class has been renamed to WorkItemDescriptionVersionEndpoint, but the model it uses is still called IssueDescriptionVersion, creating an inconsistency in naming. Consider whether the model also needs to be renamed or if this inconsistency is intentional.

web/core/services/issue/work_item_version.service.ts (3)

30-32: Enhance error handling with more context

The current error handling only throws the error response data without any additional context. This makes debugging harder when issues occur. Consider adding more context to the error.

.catch((error) => {
-  throw error?.response?.data;
+  const errorData = error?.response?.data;
+  throw {
+    message: 'Failed to list description versions',
+    status: error?.response?.status,
+    data: errorData
+  };
});

45-47: Apply the same error handling improvement here

Similar to the previous method, enhance the error handling here as well for consistency.

.catch((error) => {
-  throw error?.response?.data;
+  const errorData = error?.response?.data;
+  throw {
+    message: 'Failed to retrieve description version',
+    status: error?.response?.status,
+    data: errorData
+  };
});

16-19: Consider adding type annotation for constructor parameter

The constructor parameter serviceType has a type inference from the default value, but for clarity and to follow best practices, consider adding an explicit type annotation.

-  constructor(serviceType: TIssueServiceType = EIssueServiceType.WORK_ITEMS) {
+  constructor(serviceType: TIssueServiceType = EIssueServiceType.WORK_ITEMS) {
web/core/components/issues/issue-detail/main-content.tsx (2)

152-171: Consider consistent conditional rendering with isArchived check

The DescriptionVersionsRoot component is conditionally rendered based on isEditable, but unlike other components (like IssueTitleInput), it doesn't check for isArchived. For consistency, consider applying the same condition.

-          {isEditable && (
+          {isEditable && !isArchived && (

47-49: Consider initializing the ref with useRef<EditorRefApi | null>

To be more explicit about the possibility of the ref being null, consider adding the null union type.

-  const editorRef = useRef<EditorRefApi>(null);
+  const editorRef = useRef<EditorRefApi | null>(null);
web/core/services/inbox/intake-work_item_version.service.ts (3)

8-41: Consider abstracting common functionality between version services

There's significant code duplication between this service and WorkItemVersionService. Consider creating a base class or using composition to share common functionality.

You could create a base version service class that accepts endpoint configuration:

export class BaseVersionService extends APIService {
  constructor(private endpointPath: string) {
    super(API_BASE_URL);
  }

  async listDescriptionVersions(
    workspaceSlug: string, 
    projectId: string, 
    itemId: string
  ): Promise<TDescriptionVersionsListResponse> {
    return this.get(
      `/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.endpointPath}/${itemId}/description-versions/`
    )
      .then((response) => response?.data)
      .catch((error) => {
        throw error?.response?.data;
      });
  }

  // ...other methods
}

// Then implement specific services:
export class IntakeWorkItemVersionService extends BaseVersionService {
  constructor() {
    super('intake-work-items');
  }
}

export class WorkItemVersionService extends BaseVersionService {
  constructor() {
    super('work-items');
  }
}

22-24: Enhance error handling with more context

The current error handling only throws the error response data without any additional context. This makes debugging harder when issues occur.

.catch((error) => {
-  throw error?.response?.data;
+  const errorData = error?.response?.data;
+  throw {
+    message: 'Failed to list intake work item description versions',
+    status: error?.response?.status,
+    data: errorData
+  };
});

37-39: Apply the same error handling improvement here

Similar to the previous method, enhance the error handling here as well for consistency.

.catch((error) => {
-  throw error?.response?.data;
+  const errorData = error?.response?.data;
+  throw {
+    message: 'Failed to retrieve intake work item description version',
+    status: error?.response?.status,
+    data: errorData
+  };
});
web/core/components/inbox/content/issue-root.tsx (3)

3-3: Update import organization for better readability.

The import list is growing. Consider organizing imports alphabetically within each grouping for easier maintenance and readability.


137-137: Add error logging for issue update failures.

The error handling is now suppressing the error object completely. Consider logging the error for debugging purposes while keeping the error handling intact.

-        } catch {
+        } catch (error) {
+          console.error("Error updating inbox issue:", error);

221-250: Well-structured implementation of version history UI.

The component structure effectively integrates the version history feature. The conditional rendering based on isEditable ensures appropriate access control, and the proper props are passed to the DescriptionVersionsRoot component.

A few suggestions:

  1. Consider extracting the version-related props into a separate variable for readability
  2. Add a tooltip explaining the version history feature to users
web/core/components/issues/peek-overview/issue-detail.tsx (3)

2-2: Update import organization for better readability.

The React import line is growing. Consider organizing imports alphabetically within each grouping for easier maintenance and readability.


27-27: Initialize service outside component for better performance.

The service initialization happens inside the component definition. Move this outside to prevent unnecessary re-initialization on each render.

// services init
-const workItemVersionService = new WorkItemVersionService();
+const workItemVersionService = new WorkItemVersionService();

interface IPeekOverviewIssueDetails {

155-167: Consider simplifying the version service method calls.

The nested structure of the version service method calls is a bit verbose. Consider creating a helper function to simplify these calls.

fetchHandlers={{
  listDescriptionVersions: (issueId) =>
-    workItemVersionService.listDescriptionVersions(
-      workspaceSlug,
-      issue.project_id?.toString() ?? "",
-      issueId
-    ),
+    workItemVersionService.listDescriptionVersions(workspaceSlug, issue.project_id?.toString() ?? "", issueId),
  retrieveDescriptionVersion: (issueId, versionId) =>
-    workItemVersionService.retrieveDescriptionVersion(
-      workspaceSlug,
-      issue.project_id?.toString() ?? "",
-      issueId,
-      versionId
-    ),
+    workItemVersionService.retrieveDescriptionVersion(workspaceSlug, issue.project_id?.toString() ?? "", issueId, versionId),
}}
web/core/components/core/description-versions/root.tsx (3)

69-74: Consider using a single state update for modal closure.

The current implementation uses setTimeout to reset the active version ID after the modal closes. Consider using the modal's onClose callback to handle both state updates.

handleClose={() => {
  setIsModalOpen(false);
-  setTimeout(() => {
-    setActiveVersionId(null);
-  }, 300);
+  // If you need the delay, explain why in a comment
+  // Otherwise, consider:
+  setActiveVersionId(null);
}}

84-94: Consider adding empty state handling.

The component currently disables the dropdown when there are no versions, but it would be better to also show a message explaining why it's disabled.

<div className={cn(className)}>
+  {versionsCount === 0 && (
+    <div className="text-xs text-custom-text-300">
+      No version history available
+    </div>
+  )}
  <DescriptionVersionsDropdown
    disabled={versionsCount === 0}
    entityInformation={entityInformation}
    onVersionClick={(versionId) => {
      setIsModalOpen(true);
      setActiveVersionId(versionId);
    }}
    versions={versions}
  />
</div>

1-97: Add error handling for version operations.

The component doesn't currently handle errors that might occur during fetching operations. Consider adding error handling for a better user experience.

Add error states and appropriate error messages:

// fetch versions list
-const { data: versionsListResponse } = useSWR(
+const { data: versionsListResponse, error: versionsListError } = useSWR(
  entityId ? `DESCRIPTION_VERSIONS_LIST_${entityId}` : null,
  entityId ? () => fetchHandlers.listDescriptionVersions(entityId) : null
);
// fetch active version details
-const { data: activeVersionResponse } = useSWR(
+const { data: activeVersionResponse, error: activeVersionError } = useSWR(
  entityId && activeVersionId ? `DESCRIPTION_VERSION_DETAILS_${activeVersionId}` : null,
  entityId && activeVersionId ? () => fetchHandlers.retrieveDescriptionVersion(entityId, activeVersionId) : null
);

// Later in the JSX:
<div className={cn(className)}>
+  {(versionsListError || activeVersionError) && (
+    <div className="text-xs text-red-500">
+      Error loading version history
+    </div>
+  )}
  <DescriptionVersionsDropdown
    // existing props
  />
</div>
apiserver/plane/app/views/intake/base.py (2)

294-300: Consider adding error handling for the background task.

The asynchronous issue_description_version_task is triggered without any fallback or compensation if it fails. If this data is critical, consider implementing error handling or transactional logic to recover from task failures.


620-641: Consider returning 403 Forbidden instead of 400 Bad Request.

When the user lacks permission to view an issue, returning 400 may be misleading. An HTTP 403 would more accurately signal a permissions deficiency.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 16aa1d7 and 5e20066.

📒 Files selected for processing (46)
  • apiserver/plane/app/urls/intake.py (2 hunks)
  • apiserver/plane/app/urls/issue.py (2 hunks)
  • apiserver/plane/app/views/__init__.py (2 hunks)
  • apiserver/plane/app/views/intake/base.py (6 hunks)
  • apiserver/plane/app/views/issue/base.py (1 hunks)
  • apiserver/plane/app/views/issue/version.py (4 hunks)
  • packages/constants/src/issue/common.ts (1 hunks)
  • packages/editor/src/core/hooks/use-editor.ts (1 hunks)
  • packages/editor/src/core/hooks/use-read-only-editor.ts (1 hunks)
  • packages/editor/src/core/types/editor.ts (1 hunks)
  • packages/i18n/src/locales/cs/translations.json (1 hunks)
  • packages/i18n/src/locales/de/translations.json (2 hunks)
  • packages/i18n/src/locales/en/translations.json (2 hunks)
  • packages/i18n/src/locales/es/translations.json (1 hunks)
  • packages/i18n/src/locales/fr/translations.json (1 hunks)
  • packages/i18n/src/locales/id/translations.json (1 hunks)
  • packages/i18n/src/locales/it/translations.json (2 hunks)
  • packages/i18n/src/locales/ja/translations.json (1 hunks)
  • packages/i18n/src/locales/ko/translations.json (1 hunks)
  • packages/i18n/src/locales/pl/translations.json (2 hunks)
  • packages/i18n/src/locales/pt-BR/translations.json (1 hunks)
  • packages/i18n/src/locales/ro/translations.json (1 hunks)
  • packages/i18n/src/locales/ru/translations.json (1 hunks)
  • packages/i18n/src/locales/sk/translations.json (1 hunks)
  • packages/i18n/src/locales/ua/translations.json (2 hunks)
  • packages/i18n/src/locales/vi-VN/translations.json (1 hunks)
  • packages/i18n/src/locales/zh-CN/translations.json (1 hunks)
  • packages/i18n/src/locales/zh-TW/translations.json (1 hunks)
  • packages/types/src/description_version.d.ts (1 hunks)
  • packages/types/src/index.d.ts (1 hunks)
  • packages/types/src/issues/issue.d.ts (1 hunks)
  • web/core/components/core/description-versions/dropdown-item.tsx (1 hunks)
  • web/core/components/core/description-versions/dropdown.tsx (1 hunks)
  • web/core/components/core/description-versions/index.ts (1 hunks)
  • web/core/components/core/description-versions/modal.tsx (1 hunks)
  • web/core/components/core/description-versions/root.tsx (1 hunks)
  • web/core/components/inbox/content/issue-root.tsx (7 hunks)
  • web/core/components/issues/description-input.tsx (6 hunks)
  • web/core/components/issues/issue-detail/main-content.tsx (4 hunks)
  • web/core/components/issues/issue-detail/root.tsx (1 hunks)
  • web/core/components/issues/peek-overview/issue-detail.tsx (3 hunks)
  • web/core/components/issues/peek-overview/view.tsx (2 hunks)
  • web/core/services/inbox/index.ts (1 hunks)
  • web/core/services/inbox/intake-work_item_version.service.ts (1 hunks)
  • web/core/services/issue/index.ts (1 hunks)
  • web/core/services/issue/work_item_version.service.ts (1 hunks)
🧰 Additional context used
🧬 Code Definitions (11)
web/core/components/core/description-versions/dropdown-item.tsx (1)
packages/types/src/description_version.d.ts (1)
  • TDescriptionVersion (1-10)
apiserver/plane/app/views/issue/base.py (1)
apiserver/plane/app/serializers/issue.py (1)
  • IssueDetailSerializer (713-719)
web/core/components/core/description-versions/dropdown.tsx (3)
web/core/components/core/description-versions/root.tsx (1)
  • TDescriptionVersionEntityInformation (11-16)
packages/types/src/description_version.d.ts (1)
  • TDescriptionVersion (1-10)
web/core/components/core/description-versions/dropdown-item.tsx (1)
  • DescriptionVersionsDropdownItem (14-32)
web/core/components/issues/description-input.tsx (2)
packages/editor/src/core/types/editor.ts (2)
  • EditorReadOnlyRefApi (79-94)
  • EditorRefApi (96-113)
web/core/store/router.store.ts (1)
  • workspaceSlug (69-71)
web/core/services/issue/work_item_version.service.ts (1)
packages/types/src/issues/issue.d.ts (1)
  • TIssueServiceType (123-123)
web/core/components/core/description-versions/modal.tsx (2)
packages/types/src/description_version.d.ts (1)
  • TDescriptionVersion (1-10)
packages/editor/src/core/types/editor.ts (1)
  • EditorReadOnlyRefApi (79-94)
apiserver/plane/app/views/issue/version.py (1)
apiserver/plane/app/views/intake/base.py (1)
  • get (620-681)
apiserver/plane/app/views/intake/base.py (1)
apiserver/plane/app/serializers/issue.py (2)
  • IssueDetailSerializer (713-719)
  • IssueDescriptionVersionDetailSerializer (796-815)
web/core/components/issues/issue-detail/main-content.tsx (4)
web/core/services/issue/work_item_version.service.ts (1)
  • WorkItemVersionService (13-49)
packages/editor/src/core/types/editor.ts (1)
  • EditorRefApi (96-113)
web/core/components/issues/description-input.tsx (1)
  • IssueDescriptionInput (39-179)
web/core/components/core/description-versions/root.tsx (1)
  • DescriptionVersionsRoot (30-97)
web/core/components/core/description-versions/root.tsx (3)
packages/types/src/description_version.d.ts (2)
  • TDescriptionVersionsListResponse (19-29)
  • TDescriptionVersionDetails (12-17)
web/core/components/core/description-versions/modal.tsx (1)
  • DescriptionVersionsModal (29-182)
web/core/components/core/description-versions/dropdown.tsx (1)
  • DescriptionVersionsDropdown (21-59)
apiserver/plane/app/urls/issue.py (1)
apiserver/plane/app/views/issue/version.py (2)
  • WorkItemDescriptionVersionEndpoint (75-148)
  • IssueVersionEndpoint (23-72)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (70)
packages/i18n/src/locales/vi-VN/translations.json (1)

2330-2334: Module Block Consistency:
The "module" section mirrors the approach used in the "cycle" block by providing a singular/plural label and an empty state message ("no_module": "Không có mô-đun"). This is consistent with typical Vietnamese usage where the singular form is reused. If this consistency is intended across your translation files, then the implementation looks good.

packages/i18n/src/locales/de/translations.json (1)

2341-2345: New "description_versions" Translation Section Added

The new section "description_versions" with keys "last_edited_by", "previously_edited_by", and "edited_by" has been introduced to support the version history feature. The German translations seem appropriate. Please verify that similar updates have been applied consistently across other locale files.

packages/i18n/src/locales/it/translations.json (1)

2378-2382: Localization strings added successfully for description versions.

The Italian translations for the description version feature have been properly added, following the structure and naming patterns of the existing translation file. The translations accurately represent the context of who edited a description.

web/core/services/issue/index.ts (1)

10-10: Service export added correctly.

The work item version service export is properly integrated into the issue service index, making it accessible for components that need to interact with work item version history.

packages/types/src/index.d.ts (1)

6-6: Type declaration export added properly.

The description version type definitions are now correctly exported from the main types index file, making them available throughout the application wherever types are imported.

web/core/services/inbox/index.ts (1)

2-2: Intake work item version service exported correctly.

The intake work item version service export has been properly added to the inbox service index, allowing components to access version history for intake work items.

web/core/components/core/description-versions/index.ts (1)

1-2: Well-structured module export pattern!

Using a barrel file pattern to re-export components from the description versions module is a good practice. This simplifies imports for consumers and provides a cleaner API.

packages/constants/src/issue/common.ts (1)

44-44:

Details

❓ Verification inconclusive

Good addition to the enum for the new work item feature.

The addition of WORK_ITEMS = "work-items" to the EIssueServiceType enum aligns with the PR objective of introducing version history for work items. The naming follows the existing pattern.

Consider running this script to identify any switch statements or conditional blocks that might need to be updated to handle this new enum value:


🏁 Script executed:

#!/bin/bash
# Find any switch statements or conditionals using EIssueServiceType that might need to be updated

# Look for switch statements on EIssueServiceType
rg -A 10 -B 2 "switch.*EIssueServiceType" 

# Look for if/else chains comparing against EIssueServiceType values
rg -A 5 -B 2 "if.*EIssueServiceType\.(ISSUES|EPICS)"

Length of output: 10955


ENUM update approved – please verify WORK_ITEMS handling in conditionals

The addition of WORK_ITEMS = "work-items" follows the established pattern and supports the new work item version history feature. However, our search results indicate that existing conditional logic in files like web/core/services/issue/issue.service.ts and the related stores only explicitly check for ISSUES and EPICS. Please review these conditionals and switch statements to determine if they need to be updated to account for the new WORK_ITEMS value or if a default behavior is acceptable.

web/core/components/issues/issue-detail/root.tsx (1)

369-369:

Details

❓ Verification inconclusive

Verify archive status handling inconsistency.

The isEditable prop is now passed directly to IssueMainContent instead of using !is_archived && isEditable, while IssueDetailsSidebar still uses the combined condition on line 382. This inconsistency might lead to confusion.

Please verify that the IssueMainContent component now properly handles the archived state internally. If not, this change might inadvertently allow editing archived issues.


🏁 Script executed:

#!/bin/bash
# Check how IssueMainContent handles the archive state

# Look for is_archived or isArchived in the IssueMainContent component
rg "isArchived|is_archived" "web/core/components/issues/issue-detail/main-content.tsx"

# Check the props and any conditions related to editability
rg -A 5 -B 5 "isEditable.*prop|interface.*IssueMainContent" "web/core/components/issues/issue-detail/main-content.tsx"

Length of output: 1194


Verify archive state handling across components.

The verification confirms that IssueMainContent now receives both isEditable and isArchived as separate props and enforces edit restrictions internally (e.g., via conditions like disabled={isArchived || !isEditable}), while IssueDetailsSidebar still gets the combined condition (!is_archived && isEditable). Please verify that this design difference is intentional and that archived issues remain properly non-editable where expected.

packages/i18n/src/locales/id/translations.json (1)

2373-2377: New Description Versions Section Added

The new "description_versions" block has been correctly introduced with the keys "last_edited_by", "previously_edited_by", and "edited_by". The translations ("Terakhir disunting oleh", "Sebelumnya disunting oleh", "Disunting oleh") follow the same naming convention and style as the rest of the file. Please ensure the same section is consistently added to other locale files if not already present.

packages/i18n/src/locales/zh-CN/translations.json (1)

2379-2383: Translation section for version history looks good.

The new "description_versions" section adds translations for displaying edit history information - last editor, previous editors, and general editing attribution. These translations align with the PR objective of introducing version history for work items.

packages/editor/src/core/types/editor.ts (1)

87-87: API enhancement looks good.

The method signature for setEditorValue has been updated to include an optional emitUpdate parameter, allowing control over whether updates are emitted when setting editor content. This provides better flexibility for displaying version history without triggering unnecessary updates.

packages/i18n/src/locales/es/translations.json (1)

2381-2385: New Translation Keys for Description Versions Added
The new "description_versions" section with keys "last_edited_by", "previously_edited_by", and "edited_by" is correctly added and follows the naming and stylistic conventions used throughout the file. Ensure that these keys are referenced appropriately in the corresponding UI components or view logic.

packages/i18n/src/locales/fr/translations.json (1)

2377-2383: New "description_versions" Localization Section Added

The new section introduces the keys "last_edited_by", "previously_edited_by", and "edited_by" with clear French translations. Their naming and values are consistent with the existing JSON structure and naming conventions, ensuring a smooth integration with the rest of the localization file.

packages/i18n/src/locales/pt-BR/translations.json (1)

2374-2378: Translation additions look good for describing edit history.

The new "description_versions" section with appropriate Portuguese translations for edit history labels aligns perfectly with the PR objective of introducing version history for work items.

packages/types/src/issues/issue.d.ts (1)

123-123: Type extension correctly supports the new work item versions feature.

This change appropriately extends the TIssueServiceType to include EIssueServiceType.WORK_ITEMS, which is necessary for implementing the version history feature for work items as described in the PR objectives.

packages/i18n/src/locales/sk/translations.json (1)

2378-2382: Good addition of translation keys for description version history.

This addition of the "description_versions" section with translation keys for showing edit history information is a great enhancement for internationalization support. It aligns well with the version history feature being introduced.

apiserver/plane/app/views/__init__.py (2)

147-147: LGTM: Replacing IssueDescriptionVersionEndpoint with WorkItemDescriptionVersionEndpoint.

The import statement properly maintains both old and new endpoints, supporting a transition to the work-item-based approach.


187-191: LGTM: Added IntakeWorkItemDescriptionVersionEndpoint import.

The addition of IntakeWorkItemDescriptionVersionEndpoint to the imports from .intake.base is consistent with the PR's objective of introducing version history for intake work items.

packages/editor/src/core/hooks/use-editor.ts (1)

148-149: Added flexibility with optional emitUpdate parameter.

This change enhances the setEditorValue method by making it more flexible with the optional emitUpdate parameter. This is useful for controlling whether setting content should trigger update events, which is important when loading version history.

apiserver/plane/app/views/issue/base.py (1)

634-636: Enhanced serialization for issue activity tracking

The serializer has been upgraded from IssueSerializer to IssueDetailSerializer for storing the current instance state. This adds two important fields to the tracked data:

  • description_html: HTML formatted version of the description
  • is_subscribed: Subscription status of the user

This change is directly aligned with the work item version history feature, as it ensures the HTML description is properly captured for version comparison.

web/core/components/issues/peek-overview/view.tsx (2)

189-191: Allow description interaction for archived issues

The code now enables interaction with issue descriptions for archived issues by removing the is_archived condition from the disabled prop. This supports the new version history feature by allowing users to view historical versions of descriptions even for archived work items.


229-230: Consistent behavior for all view modes

This change maintains consistency with the earlier instance by removing the is_archived check from the disabled prop in the full-screen view mode as well, ensuring consistent behavior across all view modes.

packages/i18n/src/locales/ja/translations.json (1)

2379-2383: Looks good - appropriate translations added for the new version history feature.

The Japanese translations for the new "description_versions" section are correctly implemented, providing proper localization for the version history feature. This aligns with the PR's objective of tracking changes made to work items over time.

packages/editor/src/core/hooks/use-read-only-editor.ts (1)

80-81: Good addition of the emitUpdate parameter.

The modification to the setEditorValue method signature enhances flexibility by allowing callers to specify whether to emit an update when setting content. This maintains backward compatibility through the default value while supporting the new version history functionality.

packages/i18n/src/locales/pl/translations.json (1)

2327-2342: New Translation Section "description_versions" Added
A new section named "description_versions" has been introduced with the keys "last_edited_by", "previously_edited_by", and "edited_by", each accompanied by appropriate Polish translations. This enhancement clearly supports the version history feature within the application’s localization strategy.
Please verify that similar updates have been applied consistently across the other locale files to maintain uniformity in naming and structure.

packages/i18n/src/locales/ko/translations.json (1)

2379-2385: New "description_versions" Section for Version History

The new section with keys "last_edited_by", "previously_edited_by", and "edited_by" is clear, follows the existing naming conventions, and integrates well with the rest of the JSON structure. This addition will support the new version history feature by providing localized labels in Korean.

packages/i18n/src/locales/en/translations.json (2)

633-634: LGTM: Added translation for copying markdown

The addition of the "copy_markdown" translation key aligns with the new functionality for copying markdown content from work item descriptions. This is a useful feature for users who want to share or reuse content.


2212-2216: LGTM: Good addition of description version history translations

These translation keys provide the necessary text for the new version history feature, allowing users to see who edited a description and when. This is an essential part of implementing the work item version history feature mentioned in the PR objectives.

web/core/components/issues/description-input.tsx (6)

8-8: LGTM: Added necessary editor API imports

The import of EditorReadOnlyRefApi and EditorRefApi types from the editor package is necessary for implementing the refs that will enable version history functionality.


26-27: LGTM: Added editor ref properties to support version history

These new properties allow parent components to access the editor instances, which is essential for implementing features like:

  1. Capturing version history of descriptions
  2. Supporting markdown copying functionality
  3. Exposing editor methods to parent components

The optional nature of these props ensures backward compatibility.


42-43: LGTM: Properly destructured the new ref properties

The new editor ref properties are correctly destructured from the props object, following the component's existing pattern.


61-63: LGTM: Improved code organization for workspaceId derivation

Moving the workspace lookup earlier in the component and deriving the workspaceId as soon as possible is a good practice. This ensures the ID is available throughout the component and makes the code more maintainable.


157-157: LGTM: Added ref to the editor component

The editorRef is correctly passed to the RichTextEditor component, allowing parent components to access the editor instance and its methods like getMarkDown(), which would be necessary for version history and copy markdown functionality.


167-167: LGTM: Added ref to the read-only editor component

The editorReadOnlyRef is correctly passed to the RichTextReadOnlyEditor component, ensuring that even in read-only mode, parent components can access the editor's content for version history purposes.

packages/i18n/src/locales/cs/translations.json (1)

2377-2381: Translation strings added for version history feature.

The new section for "description_versions" adds three translation strings related to the version history feature for work items. The translations are clear and consistent with the rest of the file's format.

packages/types/src/description_version.d.ts (3)

1-10: Well-defined base type for description versions.

The TDescriptionVersion type defines the core properties needed for tracking version history of work item descriptions. The type correctly uses string for date values and makes nullable fields explicit.


12-17: Comprehensive extension type for version content details.

The TDescriptionVersionDetails type properly extends the base version type to include different representations of the description content. This allows flexible rendering options depending on the context.


19-29: Complete pagination-aware response type for version lists.

The TDescriptionVersionsListResponse type includes all necessary pagination information alongside the results array, supporting efficient scrolling through version history. The type is well-structured for API responses.

web/core/components/core/description-versions/dropdown.tsx (3)

14-19: Clear and descriptive props interface.

The props interface is well-defined with appropriate types for each property. The descriptive naming makes the component's requirements clear to users.


21-31: Effective use of MobX observer pattern and hooks.

The component uses the observer pattern for efficient re-rendering and properly extracts necessary information from provided props and hooks. The derivation of the latest version information is clean and handles potential undefined cases.


32-59: Well-structured dropdown UI with proper i18n support.

The component renders a dropdown that:

  1. Shows the last editor with their display name
  2. Formats time using a utility function
  3. Provides access to previous versions through a submenu
  4. Properly uses translation keys for internationalization

The CustomMenu implementation includes appropriate placement and accessibility options.

packages/i18n/src/locales/zh-TW/translations.json (1)

2381-2384: Good addition of localization support for the description versions feature.

These translations for the Chinese (Taiwan) locale will properly support the new version history feature for work items, maintaining consistency with the application's internationalization approach.

apiserver/plane/app/views/issue/version.py (1)

138-139: Good addition of ordering for version history

Adding the ordering by -created_at ensures that the most recent versions are displayed first, which improves the user experience when viewing version history.

web/core/services/issue/work_item_version.service.ts (1)

13-49: Service implementation looks clean and well-structured

The implementation of WorkItemVersionService follows good patterns with proper API endpoint construction and promise handling. The design allows for flexibility with the serviceType parameter.

web/core/components/issues/issue-detail/main-content.tsx (2)

130-139: Good addition of editor reference

Adding the editorRef to the IssueDescriptionInput component allows for programmatic control of the editor, which is essential for the version history restore functionality.


153-170: Well-structured version history component integration

The integration of the DescriptionVersionsRoot component with proper entity information, fetch handlers, and restore functionality is well-designed and cleanly implemented.

web/core/services/inbox/intake-work_item_version.service.ts (1)

13-25:

Details

✅ Verification successful

Ensure consistent error handling with other services

Make sure that error handling approach in this service is consistent with other API services in the application. This will help maintain a predictable error handling pattern throughout the codebase.


🏁 Script executed:

#!/bin/bash
# Check how other API services handle errors in the codebase

echo "Checking error handling patterns in other API services..."
rg -A 3 "\.catch\(" --type ts web/core/services/

Length of output: 67052


Error handling consistency verified

The error handling in listDescriptionVersions() is consistent with the patterns used throughout other API services (e.g., workspace.service.ts and others), where errors are caught and re-thrown as error?.response?.data. No changes are required.

web/core/components/inbox/content/issue-root.tsx (4)

51-51: Properly type and access the EditorRefApi.

Good use of useRef for accessing the editor's API. This enables the new version restore functionality.


208-208: Good implementation of editor reference passing.

Passing the editor reference to the IssueDescriptionInput component enables the version restoration feature. The implementation is clean and follows React best practices.


245-245: Excellent implementation of version restoration.

The handler for restoring versions uses the editor ref properly to update the editor value. The second parameter true ensures the change is propagated, which is important for triggering any necessary UI updates.


241-243:

Details

❓ Verification inconclusive

Verify error handling for version fetching.

The service calls for fetching versions don't have error handling. Ensure errors are properly handled at the service level or consider adding error boundaries.


🏁 Script executed:

#!/bin/bash
# Check for error handling in the IntakeWorkItemVersionService

echo "Searching for IntakeWorkItemVersionService implementation..."
rg -A 20 "class IntakeWorkItemVersionService" --type=typescript

Length of output: 231


I'm updating our investigation with a new shell script to search for the service implementation and any associated error handling in the retrieveDescriptionVersion method. This script uses file globs instead of the unsupported file type to increase our chances of locating the relevant code. Please run the script below and share its output for further analysis:

#!/bin/bash
echo "Searching for IntakeWorkItemVersionService implementation..."
rg -A 30 "class IntakeWorkItemVersionService" -g "*.ts" -g "*.tsx"

echo "Searching for error handling in the retrieveDescriptionVersion method in IntakeWorkItemVersionService..."
rg -A 30 "retrieveDescriptionVersion" -g "*.ts" -g "*.tsx"

Once we have this output, we can better verify whether error handling is incorporated for version fetching and advise on whether additional error boundaries are necessary.

web/core/components/issues/peek-overview/issue-detail.tsx (3)

43-43: Properly type and access the EditorRefApi.

Good use of useRef for accessing the editor's API. This enables the new version restore functionality.


118-118: Improved conditional disabling for issue input fields.

The code now properly disables the title and description inputs when the issue is archived, which is a good improvement for user experience.

Also applies to: 129-129


135-174: Well-structured implementation of version history UI.

The component structure effectively integrates the version history feature. The conditional rendering based on !disabled ensures appropriate access control, and the proper props are passed to the DescriptionVersionsRoot component.

The implementation properly handles the isRestoreDisabled flag based on both disabled and isArchived props, ensuring consistent behavior.

web/core/components/core/description-versions/root.tsx (5)

11-16: Well-defined entity information type.

The TDescriptionVersionEntityInformation type is clearly defined with all necessary properties for handling version history. The isRestoreDisabled flag is particularly useful for controlling access to version restoration.


18-28: Well-structured component props with flexible design.

The component props are well-designed, allowing for different implementations of version services through the fetchHandlers prop. This makes the component reusable across different entity types.


38-46: Efficient data fetching with SWR.

Good use of SWR for data fetching with proper cache keys based on entity ID. The conditional fetching ensures resources are not wasted when data is not needed.


52-62: Robust navigation function with proper dependency tracking.

The handleNavigation function is well-implemented with proper bounds checking. The dependency array correctly includes all necessary variables, ensuring the function is updated when these dependencies change.


77-80: Clear and efficient conditional disabling logic.

The props passed to control navigation and restoration disabling are clear and efficient, using array indices and entity information appropriately.

apiserver/plane/app/urls/issue.py (4)

27-28: Good update to import the new endpoint class.

The import has been correctly updated to use WorkItemDescriptionVersionEndpoint instead of IssueDescriptionVersionEndpoint.


266-266: Improved naming convention for URL patterns.

Updating the URL pattern names from "page-versions" to "issue-versions" improves clarity and consistency.

Also applies to: 271-271


274-282: Proper renaming of endpoints for work item versions.

The endpoint paths have been correctly updated to use "work-items" instead of "issues" for description version endpoints, with appropriate parameter name changes. This aligns with the architectural shift to focus on work items rather than just issues.


274-282:

Details

❓ Verification inconclusive

Verify backwards compatibility with existing code.

The change from issue_id to work_item_id in the URL paths is significant. Ensure that all client code that depends on these endpoints has been updated accordingly.


🏁 Script executed:

#!/bin/bash
# Search for any remaining references to the old endpoint URLs

echo "Searching for references to old URL patterns..."
rg "issues/[^/]+/description-versions" --type=typescript --type=javascript

Length of output: 216


Ensure backward compatibility with the URL endpoint changes

The URL paths have been updated from using issue_id to work_item_id. To fully guarantee backward compatibility, it's critical to confirm that no client code or frontend references the old pattern exists. Please execute the adapted script above to verify that no references to the legacy endpoint pattern remain in the TypeScript or JavaScript source files. If any occurrences are found, update those client references accordingly.


---

🏁 Script executed:

```shell

#!/bin/bash
# Re-run search for any remaining references to the old endpoint URLs in TypeScript and JavaScript files
echo "Searching for references to old URL patterns in .ts files..."
rg "issues/[^/]+/description-versions" -g "*.ts"
echo "Searching for references to old URL patterns in .js files..."
rg "issues/[^/]+/description-versions" -g "*.js"

Length of output: 485


Action: Manually Verify Backward Compatibility of URL Changes

The automated search did not reveal any references to the legacy pattern (i.e., URLs containing "issues/…/description-versions") in TypeScript or JavaScript files. However, because the search results were minimal (and the output quality is low), please manually verify that all client and external integrations have been updated to use work_item_id instead of the deprecated issue_id in the endpoint URLs.

  • Confirm that no client/consumer code (including external integrations) still references endpoints using the old issues/… pattern.
  • Verify that any related API documentation or shared client libraries reflect this updated URL structure.
  • Double-check that there are no hidden dependency scripts or frameworks that might still rely on the old pattern.
apiserver/plane/app/views/intake/base.py (6)

30-45: Good addition of relevant imports.

Importing IssueDescriptionVersion, IssueDetailSerializer, IssueDescriptionVersionDetailSerializer, issue_description_version_task, BaseAPIView, user_timezone_converter, and paginate aligns well with the new feature requirements and appears logically consistent.


95-95: Fixed filterset_fields from 'statulls' to 'status'.

This correction ensures proper filtering by status. No further issues found.


410-430: Potential mismatch between 'updated_issue' and the final updated state.

In partial_update, the code calls issue_description_version_task.delay(updated_issue=current_instance, ...) before issue_serializer.save(). As a result, current_instance might represent the old state, not the new updated version. Verify whether you actually want the pre-update data or if the intended behavior is to store the post-update description.


609-615: Neat approach for paginated result post-processing.

The process_paginated_result method converts datetime fields to the user’s timezone in a clean, modular way. No issues found.


642-653: Handle potential 'DoesNotExist' errors gracefully.

IssueDescriptionVersion.objects.get(...) may raise an exception if no record is found. A try/except block returning a 404 would prevent unhandled errors.


655-682: Pagination logic is clear and straightforward.

Using paginate with a processing callback is a clean approach. If performance becomes a concern with larger datasets, consider streaming or more advanced pagination strategies.

@pushya22 pushya22 requested a review from dheeru0198 April 3, 2025 08:07
@dheeru0198
Copy link
Member

Add type hints

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

🧹 Nitpick comments (2)
apiserver/plane/app/views/intake/base.py (2)

609-684: Add type hints to the new class methods.

The new IntakeWorkItemDescriptionVersionEndpoint class looks good but is missing type hints, which would improve code clarity and maintainability as suggested in the PR comments.

Consider adding type hints to method parameters and return values:

-    def process_paginated_result(self, fields, results, timezone):
+    def process_paginated_result(self, fields: list, results: QuerySet, timezone: str) -> dict:
         paginated_data = results.values(*fields)

         datetime_fields = ["created_at", "updated_at"]
         paginated_data = user_timezone_converter(
             paginated_data, datetime_fields, timezone
         )

         return paginated_data

-    def get(self, request, slug, project_id, work_item_id, pk=None):
+    def get(self, request, slug: str, project_id: str, work_item_id: str, pk: str = None) -> Response:

621-642: Consider refactoring permission check logic.

The permission check logic is duplicated across multiple methods in this file. Consider extracting it into a helper method to improve maintainability.

You could create a helper method like:

def _check_guest_permissions(self, request, slug, project_id, issue, project):
    """Check if a guest user has permission to view the issue."""
    if (
        ProjectMember.objects.filter(
            workspace__slug=slug,
            project_id=project_id,
            member=request.user,
            role=ROLE.GUEST.value,
            is_active=True,
        ).exists()
        and not project.guest_view_all_features
        and not issue.created_by == request.user
    ):
        return False
    return True

Then use it in your methods to simplify the code.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e965580 and 8424522.

📒 Files selected for processing (4)
  • apiserver/plane/app/views/intake/base.py (8 hunks)
  • apiserver/plane/app/views/issue/base.py (3 hunks)
  • apiserver/plane/app/views/issue/version.py (4 hunks)
  • apiserver/plane/app/views/view/base.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apiserver/plane/app/views/issue/version.py
🧰 Additional context used
🧬 Code Definitions (2)
apiserver/plane/app/views/issue/base.py (1)
apiserver/plane/app/serializers/issue.py (1)
  • IssueDetailSerializer (713-719)
apiserver/plane/app/views/intake/base.py (2)
apiserver/plane/app/serializers/issue.py (2)
  • IssueDetailSerializer (713-719)
  • IssueDescriptionVersionDetailSerializer (796-815)
apiserver/plane/app/views/issue/version.py (2)
  • process_paginated_result (24-32)
  • process_paginated_result (76-84)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (13)
apiserver/plane/app/views/view/base.py (1)

435-435: Improved HTTP status code for permission errors.

Changing from HTTP_400_BAD_REQUEST to HTTP_403_FORBIDDEN is the correct approach here, as this is a permission issue rather than a malformed request. This change aligns with REST API best practices, providing clearer error semantics to API consumers.

apiserver/plane/app/views/issue/base.py (3)

568-568: Improved HTTP status code for permission errors.

Changing from HTTP_400_BAD_REQUEST to HTTP_403_FORBIDDEN is the correct approach here, as this is a permission issue rather than a malformed request. This change aligns with REST API best practices and maintains consistency with similar changes throughout the codebase.


635-635: Using appropriate serializer for detailed issue representation.

Using IssueDetailSerializer instead of IssueSerializer ensures that the serialized data includes the description_html and is_subscribed fields, which is particularly important for the description version history feature being implemented.


1281-1281: Improved HTTP status code for permission errors.

Changing from HTTP_400_BAD_REQUEST to HTTP_403_FORBIDDEN is the correct approach here, as this is a permission issue rather than a malformed request. This maintains consistency with the other similar changes in the codebase.

apiserver/plane/app/views/intake/base.py (9)

30-30: Good updates to imports and model references.

The necessary imports have been added to support the new description version history functionality. The imports are properly organized by category.

Also applies to: 34-34, 38-38, 42-45


97-97: Fixed typo in filterset_fields.

The field name has been corrected from "statulls" to "status", which will properly enable filtering by status.


228-228: Using ROLE enum is better than magic numbers.

Good change using ROLE.GUEST.value instead of hardcoded numeric values, which improves code readability and maintainability.

Also applies to: 572-572


297-302: Description version tracking added to issue creation.

The implementation correctly triggers an async task to manage description versions when creating a new issue. This supports the new version history feature.


402-404: Updated serializer to IssueDetailSerializer.

Using IssueDetailSerializer instead of IssueSerializer provides more comprehensive data for the issue activity tracking.


428-432: Description version tracking added to issue updates.

The implementation correctly triggers an async task to manage description versions when updating an existing issue. This supports the new version history feature.


580-581: Using HTTP status constant instead of hardcoded value.

Using status.HTTP_403_FORBIDDEN is cleaner and more maintainable than using the numeric value directly.


644-656: LGTM: Single version retrieval logic is well implemented.

The logic for retrieving a single description version is correctly implemented with proper permission checks.


657-684: LGTM: Version history pagination is properly implemented.

The pagination logic for retrieving a list of description versions uses the appropriate utilities and handles timezone conversion correctly.

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

🧹 Nitpick comments (6)
web/core/components/core/description-versions/modal.tsx (6)

16-27: Consider refining type definitions for optional props.

Several props, such as activeVersionDescription and projectId, are declared as string | undefined, whereas workspaceSlug is mandated as a non-optional string. If there's a possibility that workspaceSlug could be missing, consider aligning the type strategy across props to reduce potential confusion.


49-52: Verify the fallback scenario for undefined user details.

getUserDetails(owned_by) may return undefined if the user can't be found, but the code uses versionCreator?.display_name without a default fallback label. Consider displaying a placeholder or label (such as "Unknown User") for unclear ownership.


55-64: Add error handling for the copy-to-clipboard flow.

handleCopyMarkdown only handles the success case. If copying fails (e.g., lack of permissions), the user receives no feedback. Consider catching the error and displaying a warning toast.

    copyTextToClipboard(editorRef.current.getMarkDown())
+     .catch(() => {
+       setToast({
+         type: TOAST_TYPE.ERROR,
+         title: t("toast.failure"),
+         message: "Failed to copy markdown.",
+       });
+     })
      .then(() => {
        setToast({
          type: TOAST_TYPE.SUCCESS,
          ...
        })
      });

62-62: Externalize success message for translation.

The success message "Markdown copied to clipboard." is hard-coded. Consider using t("...") to ensure consistent localization.


100-110: Enhance keyboard accessibility for navigation buttons.

The ChevronRight and ChevronLeft buttons rely on click events. For improved accessibility, consider making them keyboard-focusable (e.g., tabIndex={0}) and handling the Enter key event to support keyboard users.


170-173: Provide feedback if restore operation fails.

After calling handleRestore, the modal closes immediately. If handleRestore throws or cannot restore, the user sees no error. Consider awaiting the restore operation or capturing errors before closing the modal and showing an appropriate toast if restoration fails.

Do you want me to propose an updated implementation to handle potential restore errors gracefully?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8424522 and 69f972a.

📒 Files selected for processing (2)
  • web/core/components/core/description-versions/modal.tsx (1 hunks)
  • web/core/components/issues/description-input.tsx (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/core/components/issues/description-input.tsx
🧰 Additional context used
🧬 Code Definitions (1)
web/core/components/core/description-versions/modal.tsx (2)
packages/types/src/description_version.d.ts (1)
  • TDescriptionVersion (1-10)
packages/editor/src/core/types/editor.ts (1)
  • EditorReadOnlyRefApi (79-94)

@sriramveeraghanta sriramveeraghanta merged commit 34337f9 into preview Apr 4, 2025
5 of 6 checks passed
@sriramveeraghanta sriramveeraghanta deleted the feat/work-item-versions branch April 4, 2025 14:39
lifeiscontent pushed a commit that referenced this pull request Aug 18, 2025
* chore: work item description versions

* chore: intake issue description

* chore: intake work item description versions

* chore: add missing translations

* chore: endpoint for intake description version

* chore: renamed key to work item

* chore: changed the paginator class

* chore: authorization added

* chore: added the enum validation

* chore: removed extra validations

* chore: added extra validations

* chore: modal position

---------

Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com>
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.

5 participants