mirror of
https://github.com/makeplane/plane.git
synced 2026-02-04 21:21:03 -06:00
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:
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
4
packages/types/src/inbox.d.ts
vendored
4
packages/types/src/inbox.d.ts
vendored
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
36
web/ce/components/issues/issue-details/issue-creator.tsx
Normal file
36
web/ce/components/issues/issue-details/issue-creator.tsx
Normal 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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
1
web/ce/store/project-inbox.store.ts
Normal file
1
web/ce/store/project-inbox.store.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "@/store/inbox/project-inbox.store";
|
||||
@@ -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>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
1
web/ee/components/issues/issue-details/issue-creator.tsx
Normal file
1
web/ee/components/issues/issue-details/issue-creator.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export * from "ce/components/issues/issue-details/issue-creator";
|
||||
1
web/ee/store/project-inbox.store.ts
Normal file
1
web/ee/store/project-inbox.store.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "ce/store/project-inbox.store";
|
||||
Reference in New Issue
Block a user