fix: intake refactor (#6698)

* fix: refactor

* fix: refactor

* fix: type

* chore: added source data in intake

* fix: css

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
This commit is contained in:
Akshita Goyal
2025-03-20 14:06:36 +05:30
committed by GitHub
parent 075c234385
commit e22265dc93
18 changed files with 104 additions and 94 deletions

View File

@@ -268,6 +268,20 @@ class IssueActivitySerializer(BaseSerializer):
issue_detail = IssueFlatSerializer(read_only=True, source="issue")
project_detail = ProjectLiteSerializer(read_only=True, source="project")
workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace")
source_data = serializers.SerializerMethodField()
def get_source_data(self, obj):
if (
hasattr(obj, "issue")
and hasattr(obj.issue, "source_data")
and obj.issue.source_data
):
return {
"source": obj.issue.source_data[0].source,
"source_email": obj.issue.source_data[0].source_email,
"extra": obj.issue.source_data[0].extra,
}
return None
class Meta:
model = IssueActivity

View File

@@ -14,7 +14,7 @@ from rest_framework import status
from .. import BaseAPIView
from plane.app.serializers import IssueActivitySerializer, IssueCommentSerializer
from plane.app.permissions import ProjectEntityPermission, allow_permission, ROLE
from plane.db.models import IssueActivity, IssueComment, CommentReaction
from plane.db.models import IssueActivity, IssueComment, CommentReaction, IntakeIssue
class IssueActivityEndpoint(BaseAPIView):
@@ -57,13 +57,22 @@ class IssueActivityEndpoint(BaseAPIView):
)
)
)
issue_activities = IssueActivitySerializer(issue_activities, many=True).data
issue_comments = IssueCommentSerializer(issue_comments, many=True).data
if request.GET.get("activity_type", None) == "issue-property":
issue_activities = issue_activities.prefetch_related(
Prefetch(
"issue__issue_intake",
queryset=IntakeIssue.objects.only(
"source_email", "source", "extra"
),
to_attr="source_data",
)
)
issue_activities = IssueActivitySerializer(issue_activities, many=True).data
return Response(issue_activities, status=status.HTTP_200_OK)
if request.GET.get("activity_type", None) == "issue-comment":
issue_comments = IssueCommentSerializer(issue_comments, many=True).data
return Response(issue_comments, status=status.HTTP_200_OK)
result_list = sorted(

View File

@@ -66,8 +66,10 @@ export type TInboxIssueWithPagination = TInboxIssuePaginationInfo & {
results: TInboxIssue[];
};
export type TAnchors = { [key: string]: string };
export type TInboxForm = {
anchor: string;
anchors: TAnchors;
id: string;
is_in_app_enabled: boolean;
is_form_enabled: boolean;

View File

@@ -30,6 +30,13 @@ export type TIssueActivity = {
new_identifier: string | undefined;
epoch: number;
issue_comment: string | null;
source_data: {
source: "IN_APP" | "FORM" | "EMAIL";
source_email?: string;
extra: {
username?: string;
};
};
};
export type TIssueActivityMap = {

View File

@@ -31,7 +31,7 @@ export const RichTextEditor = forwardRef<EditorRefApi, RichTextEditorWrapperProp
})}
{...rest}
containerClassName={containerClassName}
editorClassName="min-h-[100px] max-h-[50vh] border border-gray-100 rounded-md pl-3 pb-3 overflow-y-scroll"
editorClassName="min-h-[100px] max-h-[50vh] border-[0.5px] border-custom-border-200 rounded-md pl-3 py-2 overflow-y-scroll"
/>
);
});

View File

@@ -3,3 +3,4 @@ export * from "./issue-properties-activity";
export * from "./issue-type-switcher";
export * from "./issue-type-activity";
export * from "./parent-select-root";
export * from "./issue-creator";

View File

@@ -0,0 +1,36 @@
import { FC } from "react";
import Link from "next/link";
// hooks
import { useIssueDetail } from "@/hooks/store";
type TIssueUser = {
activityId: string;
customUserName?: string;
};
export const IssueCreatorDisplay: FC<TIssueUser> = (props) => {
const { activityId, customUserName } = props;
// hooks
const {
activity: { getActivityById },
} = useIssueDetail();
const activity = getActivityById(activityId);
if (!activity) return <></>;
return (
<>
{customUserName ? (
<span className="text-custom-text-100 font-medium">{customUserName || "Plane"}</span>
) : (
<Link
href={`/${activity?.workspace_detail?.slug}/profile/${activity?.actor_detail?.id}`}
className="hover:underline text-custom-text-100 font-medium"
>
{activity.actor_detail?.display_name}
</Link>
)}
</>
);
};

View File

@@ -11,12 +11,7 @@ export type TProperties = {
icon: ReactNode;
isPro: boolean;
isEnabled: boolean;
renderChildren?: (
currentProjectDetails: IProject,
isAdmin: boolean,
handleSubmit: (featureKey: string, featureProperty: string) => Promise<void>,
workspaceSlug: string
) => ReactNode;
renderChildren?: (currentProjectDetails: IProject, workspaceSlug: string) => ReactNode;
};
export type TFeatureList = {
[key: string]: TProperties;

View File

@@ -0,0 +1 @@
export * from "@/store/inbox/project-inbox.store";

View File

@@ -21,13 +21,27 @@ export const IssueDefaultActivity: FC<TIssueDefaultActivity> = observer((props)
const activity = getActivityById(activityId);
if (!activity) return <></>;
const source = activity.source_data?.source;
return (
<IssueActivityBlockComponent
activityId={activityId}
icon={<LayersIcon width={14} height={14} className="text-custom-text-200" aria-hidden="true" />}
ends={ends}
>
<>{activity.verb === "created" ? " created the work item." : " deleted a work item."}</>
<>
{activity.verb === "created" ? (
source && source !== "IN_APP" ? (
<span>
created the work item via <span className="font-medium">{source.toLowerCase()}</span>.
</span>
) : (
<span> created the work item.</span>
)
) : (
<span> deleted a work item.</span>
)}
</>
</IssueActivityBlockComponent>
);
});

View File

@@ -9,6 +9,7 @@ import { useIssueDetail } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
// ui
// components
import { IssueCreatorDisplay } from "@/plane-web/components/issues";
import { IssueUser } from "../";
// helpers
@@ -41,7 +42,11 @@ export const IssueActivityBlockComponent: FC<TIssueActivityBlockComponent> = (pr
{icon ? icon : <Network className="w-3.5 h-3.5" />}
</div>
<div className="w-full truncate text-custom-text-200">
<IssueUser activityId={activityId} customUserName={customUserName} />
{!activity?.field && activity?.verb === "created" ? (
<IssueCreatorDisplay activityId={activityId} customUserName={customUserName} />
) : (
<IssueUser activityId={activityId} customUserName={customUserName} />
)}
<span> {children} </span>
<span>
<Tooltip

View File

@@ -84,7 +84,7 @@ export const ProjectFeaturesList: FC<Props> = observer((props) => {
<h4 className="text-sm font-medium leading-5">{t(featureItem.key)}</h4>
{featureItem.isPro && (
<Tooltip tooltipContent="Pro feature" position="top">
<UpgradeBadge />
<UpgradeBadge className="rounded" />
</Tooltip>
)}
</div>
@@ -103,8 +103,7 @@ export const ProjectFeaturesList: FC<Props> = observer((props) => {
</div>
<div className="pl-14">
{currentProjectDetails?.[featureItem.property as keyof IProject] &&
featureItem.renderChildren &&
featureItem.renderChildren(currentProjectDetails, isAdmin, handleSubmit, workspaceSlug)}
featureItem.renderChildren?.(currentProjectDetails, workspaceSlug)}
</div>
</div>
);

View File

@@ -1,7 +1,7 @@
import { useContext } from "react";
// mobx store
import { StoreContext } from "@/lib/store-context";
import { IProjectInboxStore } from "@/store/inbox/project-inbox.store";
import { IProjectInboxStore } from "@/plane-web/store/project-inbox.store";
export const useProjectInbox = (): IProjectInboxStore => {
const context = useContext(StoreContext);

View File

@@ -1,6 +1,6 @@
// types
import { TInboxIssue } from "@plane/constants";
import type { TIssue, TInboxIssueWithPagination, TInboxForm } from "@plane/types";
import type { TIssue, TInboxIssueWithPagination } from "@plane/types";
import { API_BASE_URL } from "@/helpers/common.helper";
import { APIService } from "@/services/api.service";
// helpers
@@ -76,28 +76,4 @@ export class InboxIssueService extends APIService {
throw error?.response?.data;
});
}
async retrievePublishForm(workspaceSlug: string, projectId: string): Promise<TInboxForm> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/intake-settings/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async updatePublishForm(workspaceSlug: string, projectId: string, data: Partial<TInboxForm>): Promise<TInboxForm> {
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/intake-settings/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async regeneratePublishForm(workspaceSlug: string, projectId: string): Promise<TInboxForm> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/publish-intake-regenerate/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -11,7 +11,6 @@ import {
TInboxIssueSorting,
TInboxIssuePaginationInfo,
TInboxIssueSortingOrderByQueryParam,
TInboxForm,
} from "@plane/types";
// helpers
import { EInboxIssueCurrentTab, EInboxIssueStatus, EPastDurationFilters, getCustomDates } from "@/helpers/inbox.helper";
@@ -39,7 +38,6 @@ export interface IProjectInboxStore {
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined;
inboxIssues: Record<string, IInboxIssueStore>; // issue_id -> IInboxIssueStore
inboxIssueIds: string[];
intakeForms: Record<string, TInboxForm>;
// computed
inboxFilters: Partial<TInboxIssueFilter>; // computed project inbox filters
inboxSorting: Partial<TInboxIssueSorting>; // computed project inbox sorting
@@ -69,9 +67,6 @@ export interface IProjectInboxStore {
) => Promise<void>;
fetchInboxPaginationIssues: (workspaceSlug: string, projectId: string) => Promise<void>;
fetchInboxIssueById: (workspaceSlug: string, projectId: string, inboxIssueId: string) => Promise<TInboxIssue>;
fetchIntakeForms: (workspaceSlug: string, projectId: string) => Promise<void>;
toggleIntakeForms: (workspaceSlug: string, projectId: string, data: Partial<TInboxForm>) => Promise<void>;
regenerateIntakeForms: (workspaceSlug: string, projectId: string) => Promise<void>;
createInboxIssue: (
workspaceSlug: string,
projectId: string,
@@ -93,7 +88,6 @@ export class ProjectInboxStore implements IProjectInboxStore {
inboxIssuePaginationInfo: TInboxIssuePaginationInfo | undefined = undefined;
inboxIssues: Record<string, IInboxIssueStore> = {};
inboxIssueIds: string[] = [];
intakeForms: Record<string, TInboxForm> = {};
// services
inboxIssueService;
@@ -108,7 +102,6 @@ export class ProjectInboxStore implements IProjectInboxStore {
inboxIssuePaginationInfo: observable,
inboxIssues: observable,
inboxIssueIds: observable,
intakeForms: observable,
// computed
inboxFilters: computed,
inboxSorting: computed,
@@ -316,51 +309,6 @@ export class ProjectInboxStore implements IProjectInboxStore {
}
};
fetchIntakeForms = async (workspaceSlug: string, projectId: string) => {
try {
const intakeForms = await this.inboxIssueService.retrievePublishForm(workspaceSlug, projectId);
if (intakeForms)
runInAction(() => {
set(this.intakeForms, projectId, intakeForms);
});
} catch {
console.error("Error fetching the publish forms");
}
};
toggleIntakeForms = async (workspaceSlug: string, projectId: string, data: Partial<TInboxForm>) => {
const initialData = this.intakeForms[projectId];
try {
runInAction(() => {
set(this.intakeForms, projectId, { ...this.intakeForms[projectId], ...data });
});
const result = await this.inboxIssueService.updatePublishForm(workspaceSlug, projectId, data);
runInAction(() => {
set(this.intakeForms, projectId, { ...this.intakeForms[projectId], anchor: result?.anchor });
});
} catch {
console.error("Error fetching the publish forms");
runInAction(() => {
set(this.intakeForms, projectId, initialData);
});
}
};
regenerateIntakeForms = async (workspaceSlug: string, projectId: string) => {
try {
const form = await this.inboxIssueService.regeneratePublishForm(workspaceSlug, projectId);
if (form) {
runInAction(() => {
set(this.intakeForms, projectId, {
...this.intakeForms[projectId],
anchor: form?.anchor,
});
});
}
} catch {
console.error("Error fetching the publish forms");
}
};
/**
* @description fetch intake issues with paginated data
* @param workspaceSlug

View File

@@ -3,3 +3,4 @@ export * from "./issue-properties-activity";
export * from "./issue-type-switcher";
export * from "./issue-type-activity";
export * from "./parent-select-root";
export * from "./issue-creator";

View File

@@ -0,0 +1 @@
export * from "ce/components/issues/issue-details/issue-creator";

View File

@@ -0,0 +1 @@
export * from "ce/store/project-inbox.store";