diff --git a/apiserver/plane/app/views/issue.py b/apiserver/plane/app/views/issue.py index 0b5c612d39..34bce8a0a9 100644 --- a/apiserver/plane/app/views/issue.py +++ b/apiserver/plane/app/views/issue.py @@ -1668,15 +1668,9 @@ class IssueDraftViewSet(BaseViewSet): def get_queryset(self): return ( - Issue.objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) - .order_by() - .annotate(count=Func(F("id"), function="Count")) - .values("count") + Issue.objects.filter( + project_id=self.kwargs.get("project_id") ) - .filter(project_id=self.kwargs.get("project_id")) .filter(workspace__slug=self.kwargs.get("slug")) .filter(is_draft=True) .select_related("workspace", "project", "state", "parent") @@ -1710,7 +1704,7 @@ class IssueDraftViewSet(BaseViewSet): .annotate(count=Func(F("id"), function="Count")) .values("count") ) - ) + ).distinct() @method_decorator(gzip_page) def list(self, request, slug, project_id): @@ -1832,7 +1826,10 @@ class IssueDraftViewSet(BaseViewSet): notification=True, origin=request.META.get("HTTP_ORIGIN"), ) - return Response(serializer.data, status=status.HTTP_201_CREATED) + issue = ( + self.get_queryset().filter(pk=serializer.data["id"]).first() + ) + return Response(IssueSerializer(issue).data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def partial_update(self, request, slug, project_id, pk): @@ -1868,10 +1865,13 @@ class IssueDraftViewSet(BaseViewSet): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request, slug, project_id, pk=None): - issue = Issue.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk, is_draft=True + issue = self.get_queryset().filter(pk=pk).first() + return Response( + IssueSerializer( + issue, fields=self.fields, expand=self.expand + ).data, + status=status.HTTP_200_OK, ) - return Response(IssueSerializer(issue).data, status=status.HTTP_200_OK) def destroy(self, request, slug, project_id, pk=None): issue = Issue.objects.get( diff --git a/web/components/command-palette/command-palette.tsx b/web/components/command-palette/command-palette.tsx index e6349e0b4a..396003589a 100644 --- a/web/components/command-palette/command-palette.tsx +++ b/web/components/command-palette/command-palette.tsx @@ -163,6 +163,8 @@ export const CommandPalette: FC = observer(() => { return () => document.removeEventListener("keydown", handleKeyDown); }, [handleKeyDown]); + const isDraftIssue = router?.asPath?.includes("draft-issues") || false; + if (!currentUser) return null; return ( @@ -217,6 +219,7 @@ export const CommandPalette: FC = observer(() => { onClose={() => toggleCreateIssueModal(false)} data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_ids: [moduleId.toString()] } : undefined} storeType={createIssueStoreType} + isDraft={isDraftIssue} /> {workspaceSlug && projectId && issueId && issueDetails && ( diff --git a/web/components/headers/project-draft-issues.tsx b/web/components/headers/project-draft-issues.tsx index 0fe6a74c5b..139ec02579 100644 --- a/web/components/headers/project-draft-issues.tsx +++ b/web/components/headers/project-draft-issues.tsx @@ -103,7 +103,7 @@ export const ProjectDraftIssueHeader: FC = observer(() => { } /> + } /> } /> diff --git a/web/components/headers/project-issues.tsx b/web/components/headers/project-issues.tsx index c819b25c34..81e2d2d76b 100644 --- a/web/components/headers/project-issues.tsx +++ b/web/components/headers/project-issues.tsx @@ -200,8 +200,8 @@ export const ProjectIssuesHeader: React.FC = observer(() => { handleDisplayPropertiesUpdate={handleDisplayProperties} /> - - {currentProjectDetails?.inbox_view && inboxDetails && ( + + {currentProjectDetails?.inbox_view && inboxDetails && ( - )} - - + )} {canUserCreateIssue && ( <> - + ) : ( + + )} + + )} + + diff --git a/web/components/issues/issue-modal/modal.tsx b/web/components/issues/issue-modal/modal.tsx index 3b5b35ceac..02a0873148 100644 --- a/web/components/issues/issue-modal/modal.tsx +++ b/web/components/issues/issue-modal/modal.tsx @@ -20,10 +20,19 @@ export interface IssuesModalProps { onSubmit?: (res: TIssue) => Promise; withDraftIssueWrapper?: boolean; storeType?: TCreateModalStoreTypes; + isDraft?: boolean; } export const CreateUpdateIssueModal: React.FC = observer((props) => { - const { data, isOpen, onClose, onSubmit, withDraftIssueWrapper = true, storeType = EIssuesStoreType.PROJECT } = props; + const { + data, + isOpen, + onClose, + onSubmit, + withDraftIssueWrapper = true, + storeType = EIssuesStoreType.PROJECT, + isDraft = false, + } = props; // states const [changesMade, setChangesMade] = useState | null>(null); const [createMore, setCreateMore] = useState(false); @@ -42,6 +51,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop const { issues: cycleIssues } = useIssues(EIssuesStoreType.CYCLE); const { issues: viewIssues } = useIssues(EIssuesStoreType.PROJECT_VIEW); const { issues: profileIssues } = useIssues(EIssuesStoreType.PROFILE); + const { issues: draftIssueStore } = useIssues(EIssuesStoreType.DRAFT); // store mapping based on current store const issueStores = { [EIssuesStoreType.PROJECT]: { @@ -122,11 +132,16 @@ export const CreateUpdateIssueModal: React.FC = observer((prop onClose(); }; - const handleCreateIssue = async (payload: Partial): Promise => { + const handleCreateIssue = async ( + payload: Partial, + is_draft_issue: boolean = false + ): Promise => { if (!workspaceSlug || !payload.project_id) return; try { - const response = await currentIssueStore.createIssue(workspaceSlug, payload.project_id, payload, viewId); + const response = is_draft_issue + ? await draftIssueStore.createIssue(workspaceSlug, payload.project_id, payload) + : await currentIssueStore.createIssue(workspaceSlug, payload.project_id, payload, viewId); if (!response) throw new Error(); currentIssueStore.fetchIssues(workspaceSlug, payload.project_id, "mutation", viewId); @@ -213,7 +228,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop } }; - const handleFormSubmit = async (formData: Partial) => { + const handleFormSubmit = async (formData: Partial, is_draft_issue: boolean = false) => { if (!workspaceSlug || !formData.project_id || !storeType) return; const payload: Partial = { @@ -222,7 +237,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop }; let response: TIssue | undefined = undefined; - if (!data?.id) response = await handleCreateIssue(payload); + if (!data?.id) response = await handleCreateIssue(payload, is_draft_issue); else response = await handleUpdateIssue(payload); if (response != undefined && onSubmit) await onSubmit(response); @@ -274,6 +289,7 @@ export const CreateUpdateIssueModal: React.FC = observer((prop projectId={activeProjectId} isCreateMoreToggleEnabled={createMore} onCreateMoreToggleChange={handleCreateMoreToggleChange} + isDraft={isDraft} /> ) : ( = observer((prop onCreateMoreToggleChange={handleCreateMoreToggleChange} onSubmit={handleFormSubmit} projectId={activeProjectId} + isDraft={isDraft} /> )} diff --git a/web/components/modules/module-mobile-header.tsx b/web/components/modules/module-mobile-header.tsx index b08c443d2c..e9ed56a8de 100644 --- a/web/components/modules/module-mobile-header.tsx +++ b/web/components/modules/module-mobile-header.tsx @@ -77,7 +77,7 @@ export const ModuleMobileHeader = () => { ); return ( - <> +
setAnalyticsModal(false)} @@ -157,6 +157,6 @@ export const ModuleMobileHeader = () => { Analytics
- + ); }; diff --git a/web/store/issue/draft/issue.store.ts b/web/store/issue/draft/issue.store.ts index 13d14484a4..dc0f601eb6 100644 --- a/web/store/issue/draft/issue.store.ts +++ b/web/store/issue/draft/issue.store.ts @@ -1,5 +1,9 @@ import { action, observable, makeObservable, computed, runInAction } from "mobx"; import set from "lodash/set"; +import update from "lodash/update"; +import uniq from "lodash/uniq"; +import concat from "lodash/concat"; +import pull from "lodash/pull"; // base class import { IssueHelperStore } from "../helpers/issue-helper.store"; // services @@ -123,7 +127,7 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues { const response = await this.issueDraftService.createDraftIssue(workspaceSlug, projectId, data); runInAction(() => { - this.issues[projectId].push(response.id); + update(this.issues, [projectId], (issueIds = []) => uniq(concat(issueIds, response.id))); }); this.rootStore.issues.addIssue([response]); @@ -136,8 +140,17 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues { updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial) => { try { - this.rootStore.issues.updateIssue(issueId, data); - const response = await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data); + const response = await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data); + + if (data.hasOwnProperty("is_draft") && data?.is_draft === false) { + runInAction(() => { + update(this.issues, [projectId], (issueIds = []) => { + if (issueIds.includes(issueId)) pull(issueIds, issueId); + return issueIds; + }); + }); + } + return response; } catch (error) { this.fetchIssues(workspaceSlug, projectId, "mutation"); @@ -147,15 +160,14 @@ export class DraftIssues extends IssueHelperStore implements IDraftIssues { removeIssue = async (workspaceSlug: string, projectId: string, issueId: string) => { try { - const response = await this.issueDraftService.deleteDraftIssue(workspaceSlug, projectId, issueId); + const response = await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId); - const issueIndex = this.issues[projectId].findIndex((_issueId) => _issueId === issueId); - if (issueIndex >= 0) - runInAction(() => { - this.issues[projectId].splice(issueIndex, 1); + runInAction(() => { + update(this.issues, [projectId], (issueIds = []) => { + if (issueIds.includes(issueId)) pull(issueIds, issueId); + return issueIds; }); - - this.rootStore.issues.removeIssue(issueId); + }); return response; } catch (error) {