diff --git a/apiserver/plane/app/views/issue/activity.py b/apiserver/plane/app/views/issue/activity.py
index f46cb87f39..685486cc0a 100644
--- a/apiserver/plane/app/views/issue/activity.py
+++ b/apiserver/plane/app/views/issue/activity.py
@@ -29,6 +29,8 @@ from plane.db.models import (
IssueComment,
CommentReaction,
)
+from plane.payment.flags.flag_decorator import check_workspace_feature_flag
+from plane.payment.flags.flag import FeatureFlag
class IssueActivityEndpoint(BaseAPIView):
@@ -61,6 +63,13 @@ class IssueActivityEndpoint(BaseAPIView):
.filter(**filters)
.select_related("actor", "workspace", "issue", "project")
).order_by("created_at")
+
+ if not check_workspace_feature_flag(
+ feature_key=FeatureFlag.ISSUE_TYPE_SETTINGS,
+ slug=slug,
+ ):
+ issue_activities = issue_activities.filter(field="type")
+
issue_comments = (
IssueComment.objects.filter(issue_id=issue_id)
.filter(
diff --git a/apiserver/plane/bgtasks/issue_activities_task.py b/apiserver/plane/bgtasks/issue_activities_task.py
index ae112fa63e..43a790f58a 100644
--- a/apiserver/plane/bgtasks/issue_activities_task.py
+++ b/apiserver/plane/bgtasks/issue_activities_task.py
@@ -27,6 +27,7 @@ from plane.db.models import (
State,
User,
EstimatePoint,
+ IssueType,
)
from plane.settings.redis import redis_instance
from plane.utils.exception_logger import log_exception
@@ -570,6 +571,51 @@ def track_closed_to(
)
+def track_type(
+ requested_data,
+ current_instance,
+ issue_id,
+ project_id,
+ workspace_id,
+ actor_id,
+ issue_activities,
+ epoch,
+):
+
+ new_type_id = requested_data.get("type_id")
+ old_type_id = current_instance.get("type_id")
+
+ if new_type_id != old_type_id:
+ verb = "updated" if new_type_id else "deleted"
+ old_type_id = (
+ IssueType.objects.filter(pk=old_type_id).first()
+ if old_type_id
+ else None
+ )
+ new_type_id = (
+ IssueType.objects.filter(pk=new_type_id).first()
+ if new_type_id
+ else None
+ )
+
+ issue_activities.append(
+ IssueActivity(
+ issue_id=issue_id,
+ actor_id=actor_id,
+ verb=verb,
+ old_value=old_type_id.name if old_type_id else None,
+ new_value=new_type_id.name if new_type_id else None,
+ field="type",
+ project_id=project_id,
+ workspace_id=workspace_id,
+ comment="",
+ old_identifier=old_type_id.id if old_type_id else None,
+ new_identifier=new_type_id.id if new_type_id else None,
+ epoch=epoch,
+ )
+ )
+
+
def create_issue_activity(
requested_data,
current_instance,
@@ -632,6 +678,7 @@ def update_issue_activity(
"estimate_point": track_estimate_points,
"archived_at": track_archive_at,
"closed_to": track_closed_to,
+ "type_id": track_type,
}
requested_data = (
@@ -1700,16 +1747,12 @@ def issue_activity(
event=(
"issue_comment"
if activity.field == "comment"
- else "inbox_issue"
- if inbox
- else "issue"
+ else "inbox_issue" if inbox else "issue"
),
event_id=(
activity.issue_comment_id
if activity.field == "comment"
- else inbox
- if inbox
- else activity.issue_id
+ else inbox if inbox else activity.issue_id
),
verb=activity.verb,
field=(
@@ -1755,4 +1798,3 @@ def issue_activity(
except Exception as e:
log_exception(e)
return
-
diff --git a/web/ee/components/issue-types/common/issue-type-logo.tsx b/web/ee/components/issue-types/common/issue-type-logo.tsx
index 52bd11791a..616c01eca8 100644
--- a/web/ee/components/issue-types/common/issue-type-logo.tsx
+++ b/web/ee/components/issue-types/common/issue-type-logo.tsx
@@ -6,7 +6,7 @@ import { LayersIcon, LUCIDE_ICONS_LIST } from "@plane/ui";
// helpers
import { cn } from "@/helpers/common.helper";
-export type TIssueTypeLogoSize = "sm" | "md" | "lg" | "xl";
+export type TIssueTypeLogoSize = "xs" | "sm" | "md" | "lg" | "xl";
type Props = {
icon_props: TLogoProps["icon"];
@@ -16,6 +16,7 @@ type Props = {
};
const iconSizeMap = {
+ xs: 11,
sm: 12.5,
md: 14.5,
lg: 18,
@@ -23,6 +24,7 @@ const iconSizeMap = {
};
const containerSizeMap = {
+ xs: 15.5,
sm: 18,
md: 21,
lg: 25.5,
diff --git a/web/ee/components/issue-types/dropdowns/issue-type.tsx b/web/ee/components/issue-types/dropdowns/issue-type.tsx
index d0e1df4128..6eb96a28b0 100644
--- a/web/ee/components/issue-types/dropdowns/issue-type.tsx
+++ b/web/ee/components/issue-types/dropdowns/issue-type.tsx
@@ -18,18 +18,16 @@ export const IssueTypeDropdown = observer((props: TIssueTypeDropdownProps) => {
// store hooks
const { loader: issueTypesLoader, getProjectIssueTypes } = useIssueTypes();
// derived values
- const issueTypes = getProjectIssueTypes(projectId, true);
+ const allIssueTypes = getProjectIssueTypes(projectId, false);
+ const activeIssueTypes = getProjectIssueTypes(projectId, true);
// Can be used with CustomSearchSelect as well
- const issueTypeOptions = Object.entries(issueTypes).map(([issueTypeId, issueTypeDetail]) => ({
+ const issueTypeOptions = Object.entries(activeIssueTypes).map(([issueTypeId, issueTypeDetail]) => ({
value: issueTypeId,
query: issueTypeDetail.name ?? "",
content: (
-
+
{issueTypeDetail.name}
),
@@ -49,10 +47,10 @@ export const IssueTypeDropdown = observer((props: TIssueTypeDropdownProps) => {
label={
-
{issueTypes[issueTypeId]?.name}
+
{allIssueTypes[issueTypeId]?.name}
}
options={issueTypeOptions}
diff --git a/web/ee/components/issues/issue-details/issue-type-activity.tsx b/web/ee/components/issues/issue-details/issue-type-activity.tsx
index 1e2cb90c25..8d06d30c65 100644
--- a/web/ee/components/issues/issue-details/issue-type-activity.tsx
+++ b/web/ee/components/issues/issue-details/issue-type-activity.tsx
@@ -1 +1,79 @@
-export * from "ce/components/issues/issue-details";
+"use client";
+
+import { FC } from "react";
+import { observer } from "mobx-react";
+import { useParams } from "next/navigation";
+// icons
+import { ArrowRightLeft } from "lucide-react";
+// ce imports
+import { IssueTypeActivity as BaseIssueTypeActivity, TIssueTypeActivity } from "@/ce/components/issues";
+// components
+import {
+ IssueActivityBlockComponent,
+ IssueLink,
+} from "@/components/issues/issue-detail/issue-activity/activity/actions";
+// helpers
+import { cn } from "@/helpers/common.helper";
+// hooks
+import { useIssueDetail } from "@/hooks/store";
+// plane web components
+import { IssueTypeLogo } from "@/plane-web/components/issue-types";
+// plane web hooks
+import { useIssueTypes } from "@/plane-web/hooks/store";
+
+type TIssueTypeDetail = {
+ issueTypeId: string;
+ className?: string;
+};
+
+const IssueTypeDetail: FC = observer((props) => {
+ const { issueTypeId, className = "" } = props;
+ // hooks
+ const { getIssueTypeById } = useIssueTypes();
+ // derived values
+ const issueTypeDetail = getIssueTypeById(issueTypeId);
+
+ return (
+
+
+ {issueTypeDetail?.name}
+
+ );
+});
+
+export const IssueTypeActivity: FC = observer((props) => {
+ const { activityId, showIssue = false, ends } = props;
+ // router
+ const { workspaceSlug } = useParams();
+ // hooks
+ const {
+ activity: { getActivityById },
+ } = useIssueDetail();
+ const { isIssueTypeEnabledForProject } = useIssueTypes();
+ // derived values
+ const activity = getActivityById(activityId);
+ if (!activity) return <>>;
+
+ const isIssueTypeDisplayEnabled =
+ workspaceSlug && activity?.project
+ ? isIssueTypeEnabledForProject(workspaceSlug?.toString(), activity?.project, "ISSUE_TYPE_DISPLAY")
+ : false;
+
+ if (!isIssueTypeDisplayEnabled) return ;
+
+ return (
+ }
+ activityId={activityId}
+ ends={ends}
+ >
+
+ changed issue type to{" "}
+ {activity.new_identifier && }
+ from {activity.old_identifier && }
+ {showIssue ? ` for ` : ``}
+ {showIssue && }.
+
+
+ );
+});
diff --git a/web/ee/components/issues/issue-details/issue-type-switcher.tsx b/web/ee/components/issues/issue-details/issue-type-switcher.tsx
index 3b9c704a8a..b54d973e0a 100644
--- a/web/ee/components/issues/issue-details/issue-type-switcher.tsx
+++ b/web/ee/components/issues/issue-details/issue-type-switcher.tsx
@@ -28,6 +28,7 @@ export const IssueTypeSwitcher: React.FC = observer((pr
const {
issue: { getIssueById },
toggleCreateIssueModal,
+ fetchActivities,
} = useIssueDetail();
const { isIssueTypeEnabledForProject } = useIssueTypes();
// derived values
@@ -54,6 +55,11 @@ export const IssueTypeSwitcher: React.FC = observer((pr
<>
{
+ if (workspaceSlug && issue.project_id) {
+ await fetchActivities(workspaceSlug.toString(), issue.project_id, issueId);
+ }
+ }}
onClose={() => {
setIsCreateUpdateIssueModalOpen(false);
toggleCreateIssueModal(false);
@@ -72,7 +78,7 @@ export const IssueTypeSwitcher: React.FC = observer((pr
{!disabled && (
- Switch Issue Type
+ Switch issue type
)}
diff --git a/web/ee/components/issues/issue-modal/provider.tsx b/web/ee/components/issues/issue-modal/provider.tsx
index 125c89ee48..406e8c191f 100644
--- a/web/ee/components/issues/issue-modal/provider.tsx
+++ b/web/ee/components/issues/issue-modal/provider.tsx
@@ -15,7 +15,7 @@ import { useIssueDetail } from "@/hooks/store";
// plane web helpers
import { getPropertiesDefaultValues } from "@/plane-web/helpers/issue-properties.helper";
// plane web hooks
-import { useIssueTypes } from "@/plane-web/hooks/store";
+import { useIssuePropertiesActivity, useIssueTypes } from "@/plane-web/hooks/store";
// plane web services
import { IssuePropertyValuesService } from "@/plane-web/services/issue-types";
// plane web types
@@ -44,7 +44,7 @@ export const IssueModalProvider = observer((props: TIssueModalProviderProps) =>
getProjectIssueTypes,
getProjectDefaultIssueType,
} = useIssueTypes();
-
+ const { fetchPropertyActivities } = useIssuePropertiesActivity();
// helpers
const getIssueTypeIdOnProjectChange = (projectId: string) => {
// get active issue types for the project
@@ -135,7 +135,10 @@ export const IssueModalProvider = observer((props: TIssueModalProviderProps) =>
await issuePropertyValuesService
.create(workspaceSlug, projectId, issueId, filteredIssuePropertyValues)
.then(() => {
+ // mutate issue property values
mutate(`ISSUE_PROPERTY_VALUES_${workspaceSlug}_${projectId}_${issueId}_${isIssueTypeDisplayEnabled}`);
+ // fetch property activities
+ fetchPropertyActivities(workspaceSlug, projectId, issueId);
// reset issue property values
setIssuePropertyValues({
...getPropertiesDefaultValues(issueType?.activeProperties ?? []),