[WEB-1925] dev: issue detail widget enhancement (#5101)
* chore: collapsible button border color updated * chore: TIssueDetailWidget type added * chore: issue link modal onClose updated * chore: issue detail widgets collapse state added to store * chore: issue detail widget interaction added * chore: issue detail widget interaction added
This commit is contained in:
parent
4d484577b5
commit
15b0a448ee
15 changed files with 135 additions and 46 deletions
8
packages/types/src/issues/issue.d.ts
vendored
8
packages/types/src/issues/issue.d.ts
vendored
|
|
@ -84,7 +84,7 @@ export type TIssuesResponse = {
|
||||||
total_pages: number;
|
total_pages: number;
|
||||||
extra_stats: null;
|
extra_stats: null;
|
||||||
results: TIssueResponseResults;
|
results: TIssueResponseResults;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type TBulkIssueProperties = Pick<
|
export type TBulkIssueProperties = Pick<
|
||||||
TIssue,
|
TIssue,
|
||||||
|
|
@ -100,3 +100,9 @@ export type TBulkOperationsPayload = {
|
||||||
issue_ids: string[];
|
issue_ids: string[];
|
||||||
properties: Partial<TBulkIssueProperties>;
|
properties: Partial<TBulkIssueProperties>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TIssueDetailWidget =
|
||||||
|
| "sub-issues"
|
||||||
|
| "relations"
|
||||||
|
| "links"
|
||||||
|
| "attachments";
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ type Props = {
|
||||||
export const CollapsibleButton: FC<Props> = (props) => {
|
export const CollapsibleButton: FC<Props> = (props) => {
|
||||||
const { isOpen, title, hideChevron = false, indicatorElement, actionItemElement } = props;
|
const { isOpen, title, hideChevron = false, indicatorElement, actionItemElement } = props;
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between gap-3 h-12 px-2.5 py-3 border-b border-custom-border-100">
|
<div className="flex items-center justify-between gap-3 h-12 px-2.5 py-3 border-b border-custom-border-200">
|
||||||
<div className="flex items-center gap-3.5">
|
<div className="flex items-center gap-3.5">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
{!hideChevron && (
|
{!hideChevron && (
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { MAX_FILE_SIZE } from "@/constants/common";
|
||||||
// helper
|
// helper
|
||||||
import { generateFileName } from "@/helpers/attachment.helper";
|
import { generateFileName } from "@/helpers/attachment.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useInstance } from "@/hooks/store";
|
import { useInstance, useIssueDetail } from "@/hooks/store";
|
||||||
|
|
||||||
import { useAttachmentOperations } from "./helper";
|
import { useAttachmentOperations } from "./helper";
|
||||||
|
|
||||||
|
|
@ -22,11 +22,16 @@ type Props = {
|
||||||
|
|
||||||
export const IssueAttachmentActionButton: FC<Props> = observer((props) => {
|
export const IssueAttachmentActionButton: FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, issueId, customButton, disabled = false } = props;
|
const { workspaceSlug, projectId, issueId, customButton, disabled = false } = props;
|
||||||
// helper
|
// state
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
// store hooks
|
||||||
const { config } = useInstance();
|
const { config } = useInstance();
|
||||||
|
const { setLastWidgetAction } = useIssueDetail();
|
||||||
|
|
||||||
|
// operations
|
||||||
const handleAttachmentOperations = useAttachmentOperations(workspaceSlug, projectId, issueId);
|
const handleAttachmentOperations = useAttachmentOperations(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
|
// handlers
|
||||||
const onDrop = useCallback(
|
const onDrop = useCallback(
|
||||||
(acceptedFiles: File[]) => {
|
(acceptedFiles: File[]) => {
|
||||||
const currentFile: File = acceptedFiles[0];
|
const currentFile: File = acceptedFiles[0];
|
||||||
|
|
@ -45,7 +50,10 @@ export const IssueAttachmentActionButton: FC<Props> = observer((props) => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
handleAttachmentOperations.create(formData).finally(() => setIsLoading(false));
|
handleAttachmentOperations.create(formData).finally(() => {
|
||||||
|
setLastWidgetAction("attachments");
|
||||||
|
setIsLoading(false);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[handleAttachmentOperations, workspaceSlug]
|
[handleAttachmentOperations, workspaceSlug]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { FC, useState } from "react";
|
import React, { FC } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
import { Collapsible } from "@plane/ui";
|
import { Collapsible } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
IssueAttachmentsCollapsibleContent,
|
IssueAttachmentsCollapsibleContent,
|
||||||
IssueAttachmentsCollapsibleTitle,
|
IssueAttachmentsCollapsibleTitle,
|
||||||
} from "@/components/issues/issue-detail-widgets";
|
} from "@/components/issues/issue-detail-widgets";
|
||||||
|
// hooks
|
||||||
|
import { useIssueDetail } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -14,17 +17,21 @@ type Props = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AttachmentsCollapsible: FC<Props> = (props) => {
|
export const AttachmentsCollapsible: FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
||||||
// state
|
// store hooks
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const { activeIssueDetailWidgets, toggleActiveIssueDetailWidget } = useIssueDetail();
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const isCollapsibleOpen = activeIssueDetailWidgets.includes("attachments");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<Collapsible
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
onToggle={() => setIsOpen((prev) => !prev)}
|
onToggle={() => toggleActiveIssueDetailWidget("attachments")}
|
||||||
title={
|
title={
|
||||||
<IssueAttachmentsCollapsibleTitle
|
<IssueAttachmentsCollapsibleTitle
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
|
|
@ -40,4 +47,4 @@ export const AttachmentsCollapsible: FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export const IssueLinksActionButton: FC<Props> = observer((props) => {
|
||||||
const [isIssueLinkModal, setIsIssueLinkModal] = useState(false);
|
const [isIssueLinkModal, setIsIssueLinkModal] = useState(false);
|
||||||
|
|
||||||
// store hooks
|
// store hooks
|
||||||
const { toggleIssueLinkModal: toggleIssueLinkModalStore } = useIssueDetail();
|
const { toggleIssueLinkModal: toggleIssueLinkModalStore, setLastWidgetAction } = useIssueDetail();
|
||||||
|
|
||||||
// helper
|
// helper
|
||||||
const handleLinkOperations = useLinkOperations(workspaceSlug, projectId, issueId);
|
const handleLinkOperations = useLinkOperations(workspaceSlug, projectId, issueId);
|
||||||
|
|
@ -43,11 +43,16 @@ export const IssueLinksActionButton: FC<Props> = observer((props) => {
|
||||||
toggleIssueLinkModal(true);
|
toggleIssueLinkModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnClose = () => {
|
||||||
|
toggleIssueLinkModal(false);
|
||||||
|
setLastWidgetAction("links");
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IssueLinkCreateUpdateModal
|
<IssueLinkCreateUpdateModal
|
||||||
isModalOpen={isIssueLinkModal}
|
isModalOpen={isIssueLinkModal}
|
||||||
handleModal={toggleIssueLinkModal}
|
handleOnClose={handleOnClose}
|
||||||
linkOperations={handleLinkOperations}
|
linkOperations={handleLinkOperations}
|
||||||
/>
|
/>
|
||||||
<button type="button" onClick={handleOnClick} disabled={disabled}>
|
<button type="button" onClick={handleOnClick} disabled={disabled}>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { FC, useState } from "react";
|
import React, { FC } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
import { Collapsible } from "@plane/ui";
|
import { Collapsible } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { IssueLinksCollapsibleContent, IssueLinksCollapsibleTitle } from "@/components/issues/issue-detail-widgets";
|
import { IssueLinksCollapsibleContent, IssueLinksCollapsibleTitle } from "@/components/issues/issue-detail-widgets";
|
||||||
|
// hooks
|
||||||
|
import { useIssueDetail } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -11,17 +14,21 @@ type Props = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LinksCollapsible: FC<Props> = (props) => {
|
export const LinksCollapsible: FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
||||||
// state
|
// store hooks
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const { activeIssueDetailWidgets, toggleActiveIssueDetailWidget } = useIssueDetail();
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const isCollapsibleOpen = activeIssueDetailWidgets.includes("links");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<Collapsible
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
onToggle={() => setIsOpen((prev) => !prev)}
|
onToggle={() => toggleActiveIssueDetailWidget("links")}
|
||||||
title={
|
title={
|
||||||
<IssueLinksCollapsibleTitle
|
<IssueLinksCollapsibleTitle
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
|
|
@ -37,4 +44,4 @@ export const LinksCollapsible: FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ export const RelationActionButton: FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, customButton, issueId, disabled = false } = props;
|
const { workspaceSlug, projectId, customButton, issueId, disabled = false } = props;
|
||||||
// state
|
// state
|
||||||
const [relationKey, setRelationKey] = useState<TIssueRelationTypes | null>(null);
|
const [relationKey, setRelationKey] = useState<TIssueRelationTypes | null>(null);
|
||||||
const { createRelation, isRelationModalOpen, toggleRelationModal } = useIssueDetail();
|
// store hooks
|
||||||
|
const { createRelation, isRelationModalOpen, toggleRelationModal, setLastWidgetAction } = useIssueDetail();
|
||||||
|
|
||||||
// handlers
|
// handlers
|
||||||
const handleOnClick = (relationKey: TIssueRelationTypes) => {
|
const handleOnClick = (relationKey: TIssueRelationTypes) => {
|
||||||
|
|
@ -57,6 +58,7 @@ export const RelationActionButton: FC<Props> = observer((props) => {
|
||||||
const handleOnClose = () => {
|
const handleOnClose = () => {
|
||||||
setRelationKey(null);
|
setRelationKey(null);
|
||||||
toggleRelationModal(null, null);
|
toggleRelationModal(null, null);
|
||||||
|
setLastWidgetAction("relations");
|
||||||
};
|
};
|
||||||
|
|
||||||
// button element
|
// button element
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { FC, useState } from "react";
|
import React, { FC } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
import { Collapsible } from "@plane/ui";
|
import { Collapsible } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { RelationsCollapsibleContent, RelationsCollapsibleTitle } from "@/components/issues/issue-detail-widgets";
|
import { RelationsCollapsibleContent, RelationsCollapsibleTitle } from "@/components/issues/issue-detail-widgets";
|
||||||
|
// hooks
|
||||||
|
import { useIssueDetail } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -11,17 +14,21 @@ type Props = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RelationsCollapsible: FC<Props> = (props) => {
|
export const RelationsCollapsible: FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
||||||
// state
|
// store hooks
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const { activeIssueDetailWidgets, toggleActiveIssueDetailWidget } = useIssueDetail();
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const isCollapsibleOpen = activeIssueDetailWidgets.includes("relations");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<Collapsible
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
onToggle={() => setIsOpen((prev) => !prev)}
|
onToggle={() => toggleActiveIssueDetailWidget("relations")}
|
||||||
title={
|
title={
|
||||||
<RelationsCollapsibleTitle
|
<RelationsCollapsibleTitle
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
|
|
@ -37,4 +44,4 @@ export const RelationsCollapsible: FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ export const SubIssuesActionButton: FC<Props> = observer((props) => {
|
||||||
toggleCreateIssueModal,
|
toggleCreateIssueModal,
|
||||||
isSubIssuesModalOpen,
|
isSubIssuesModalOpen,
|
||||||
toggleSubIssuesModal,
|
toggleSubIssuesModal,
|
||||||
|
setLastWidgetAction,
|
||||||
} = useIssueDetail();
|
} = useIssueDetail();
|
||||||
const { setTrackElement } = useEventTracker();
|
const { setTrackElement } = useEventTracker();
|
||||||
|
|
||||||
|
|
@ -88,6 +89,7 @@ export const SubIssuesActionButton: FC<Props> = observer((props) => {
|
||||||
|
|
||||||
const handleExistingIssuesModalClose = () => {
|
const handleExistingIssuesModalClose = () => {
|
||||||
handleIssueCrudState("existing", null, null);
|
handleIssueCrudState("existing", null, null);
|
||||||
|
setLastWidgetAction("sub-issues");
|
||||||
toggleSubIssuesModal(null);
|
toggleSubIssuesModal(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -102,6 +104,7 @@ export const SubIssuesActionButton: FC<Props> = observer((props) => {
|
||||||
const handleCreateUpdateModalClose = () => {
|
const handleCreateUpdateModalClose = () => {
|
||||||
handleIssueCrudState("create", null, null);
|
handleIssueCrudState("create", null, null);
|
||||||
toggleCreateIssueModal(false);
|
toggleCreateIssueModal(false);
|
||||||
|
setLastWidgetAction("sub-issues");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateUpdateModalOnSubmit = async (_issue: TIssue) => {
|
const handleCreateUpdateModalOnSubmit = async (_issue: TIssue) => {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
"use client";
|
"use client";
|
||||||
import React, { FC, useState } from "react";
|
import React, { FC } from "react";
|
||||||
|
import { observer } from "mobx-react";
|
||||||
import { Collapsible } from "@plane/ui";
|
import { Collapsible } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { SubIssuesCollapsibleContent, SubIssuesCollapsibleTitle } from "@/components/issues/issue-detail-widgets";
|
import { SubIssuesCollapsibleContent, SubIssuesCollapsibleTitle } from "@/components/issues/issue-detail-widgets";
|
||||||
|
// hooks
|
||||||
|
import { useIssueDetail } from "@/hooks/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
|
|
@ -11,17 +14,22 @@ type Props = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SubIssuesCollapsible: FC<Props> = (props) => {
|
export const SubIssuesCollapsible: FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
const { workspaceSlug, projectId, issueId, disabled = false } = props;
|
||||||
// state
|
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
// store hooks
|
||||||
|
const { activeIssueDetailWidgets, toggleActiveIssueDetailWidget } = useIssueDetail();
|
||||||
|
|
||||||
|
// derived state
|
||||||
|
const isCollapsibleOpen = activeIssueDetailWidgets.includes("sub-issues");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapsible
|
<Collapsible
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
onToggle={() => setIsOpen((prev) => !prev)}
|
onToggle={() => toggleActiveIssueDetailWidget("sub-issues")}
|
||||||
title={
|
title={
|
||||||
<SubIssuesCollapsibleTitle
|
<SubIssuesCollapsibleTitle
|
||||||
isOpen={isOpen}
|
isOpen={isCollapsibleOpen}
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
parentIssueId={issueId}
|
parentIssueId={issueId}
|
||||||
|
|
@ -37,4 +45,4 @@ export const SubIssuesCollapsible: FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export type TIssueLinkCreateFormFieldOptions = TIssueLinkEditableFields & {
|
||||||
|
|
||||||
export type TIssueLinkCreateEditModal = {
|
export type TIssueLinkCreateEditModal = {
|
||||||
isModalOpen: boolean;
|
isModalOpen: boolean;
|
||||||
handleModal: (modalToggle: boolean) => void;
|
handleOnClose?: () => void;
|
||||||
linkOperations: TLinkOperationsModal;
|
linkOperations: TLinkOperationsModal;
|
||||||
preloadedData?: TIssueLinkCreateFormFieldOptions | null;
|
preloadedData?: TIssueLinkCreateFormFieldOptions | null;
|
||||||
};
|
};
|
||||||
|
|
@ -29,7 +29,7 @@ const defaultValues: TIssueLinkCreateFormFieldOptions = {
|
||||||
|
|
||||||
export const IssueLinkCreateUpdateModal: FC<TIssueLinkCreateEditModal> = (props) => {
|
export const IssueLinkCreateUpdateModal: FC<TIssueLinkCreateEditModal> = (props) => {
|
||||||
// props
|
// props
|
||||||
const { isModalOpen, handleModal, linkOperations, preloadedData } = props;
|
const { isModalOpen, handleOnClose, linkOperations, preloadedData } = props;
|
||||||
|
|
||||||
// react hook form
|
// react hook form
|
||||||
const {
|
const {
|
||||||
|
|
@ -42,7 +42,7 @@ export const IssueLinkCreateUpdateModal: FC<TIssueLinkCreateEditModal> = (props)
|
||||||
});
|
});
|
||||||
|
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
handleModal(false);
|
if (handleOnClose) handleOnClose();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
reset(preloadedData ? preloadedData : defaultValues);
|
reset(preloadedData ? preloadedData : defaultValues);
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,15 @@ export const IssueLinkDetail: FC<TIssueLinkDetail> = (props) => {
|
||||||
|
|
||||||
const createdByDetails = getUserDetails(linkDetail.created_by_id);
|
const createdByDetails = getUserDetails(linkDetail.created_by_id);
|
||||||
|
|
||||||
|
const handleOnClose = () => {
|
||||||
|
toggleIssueLinkModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={linkId}>
|
<div key={linkId}>
|
||||||
<IssueLinkCreateUpdateModal
|
<IssueLinkCreateUpdateModal
|
||||||
isModalOpen={isIssueLinkModalOpen}
|
isModalOpen={isIssueLinkModalOpen}
|
||||||
handleModal={toggleIssueLinkModal}
|
handleOnClose={handleOnClose}
|
||||||
linkOperations={linkOperations}
|
linkOperations={linkOperations}
|
||||||
preloadedData={linkDetail}
|
preloadedData={linkDetail}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,14 @@ export const IssueLinkItem: FC<TIssueLinkItem> = (props) => {
|
||||||
const linkDetail = getLinkById(linkId);
|
const linkDetail = getLinkById(linkId);
|
||||||
if (!linkDetail) return <></>;
|
if (!linkDetail) return <></>;
|
||||||
|
|
||||||
|
const handleOnClose = () => {
|
||||||
|
toggleIssueLinkModal(false);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IssueLinkCreateUpdateModal
|
<IssueLinkCreateUpdateModal
|
||||||
isModalOpen={isIssueLinkModalOpen}
|
isModalOpen={isIssueLinkModalOpen}
|
||||||
handleModal={toggleIssueLinkModal}
|
handleOnClose={handleOnClose}
|
||||||
linkOperations={linkOperations}
|
linkOperations={linkOperations}
|
||||||
preloadedData={linkDetail}
|
preloadedData={linkDetail}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,15 @@ export const IssueLinkRoot: FC<TIssueLinkRoot> = (props) => {
|
||||||
[workspaceSlug, projectId, issueId, createLink, updateLink, removeLink, toggleIssueLinkModal]
|
[workspaceSlug, projectId, issueId, createLink, updateLink, removeLink, toggleIssueLinkModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleOnClose = () => {
|
||||||
|
toggleIssueLinkModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IssueLinkCreateUpdateModal
|
<IssueLinkCreateUpdateModal
|
||||||
isModalOpen={isIssueLinkModal}
|
isModalOpen={isIssueLinkModal}
|
||||||
handleModal={toggleIssueLinkModal}
|
handleOnClose={handleOnClose}
|
||||||
linkOperations={handleLinkOperations}
|
linkOperations={handleLinkOperations}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
||||||
TIssueLink,
|
TIssueLink,
|
||||||
TIssueReaction,
|
TIssueReaction,
|
||||||
TIssueRelationTypes,
|
TIssueRelationTypes,
|
||||||
|
TIssueDetailWidget,
|
||||||
} from "@plane/types";
|
} from "@plane/types";
|
||||||
import { IIssueRootStore } from "../root.store";
|
import { IIssueRootStore } from "../root.store";
|
||||||
import { IIssueActivityStore, IssueActivityStore, IIssueActivityStoreActions, TActivityLoader } from "./activity.store";
|
import { IIssueActivityStore, IssueActivityStore, IIssueActivityStoreActions, TActivityLoader } from "./activity.store";
|
||||||
|
|
@ -50,6 +51,8 @@ export interface IIssueDetail
|
||||||
IIssueCommentReactionStoreActions {
|
IIssueCommentReactionStoreActions {
|
||||||
// observables
|
// observables
|
||||||
peekIssue: TPeekIssue | undefined;
|
peekIssue: TPeekIssue | undefined;
|
||||||
|
activeIssueDetailWidgets: TIssueDetailWidget[];
|
||||||
|
lastWidgetAction: TIssueDetailWidget | null;
|
||||||
isCreateIssueModalOpen: boolean;
|
isCreateIssueModalOpen: boolean;
|
||||||
isIssueLinkModalOpen: boolean;
|
isIssueLinkModalOpen: boolean;
|
||||||
isParentIssueModalOpen: string | null;
|
isParentIssueModalOpen: string | null;
|
||||||
|
|
@ -72,6 +75,9 @@ export interface IIssueDetail
|
||||||
toggleRelationModal: (issueId: string | null, relationType: TIssueRelationTypes | null) => void;
|
toggleRelationModal: (issueId: string | null, relationType: TIssueRelationTypes | null) => void;
|
||||||
toggleSubIssuesModal: (value: string | null) => void;
|
toggleSubIssuesModal: (value: string | null) => void;
|
||||||
toggleDeleteAttachmentModal: (attachmentId: string | null) => void;
|
toggleDeleteAttachmentModal: (attachmentId: string | null) => void;
|
||||||
|
setActiveIssueDetailWidgets: (state: TIssueDetailWidget[]) => void;
|
||||||
|
setLastWidgetAction: (action: TIssueDetailWidget) => void;
|
||||||
|
toggleActiveIssueDetailWidget: (state: TIssueDetailWidget) => void;
|
||||||
// store
|
// store
|
||||||
rootIssueStore: IIssueRootStore;
|
rootIssueStore: IIssueRootStore;
|
||||||
issue: IIssueStore;
|
issue: IIssueStore;
|
||||||
|
|
@ -89,6 +95,8 @@ export interface IIssueDetail
|
||||||
export class IssueDetail implements IIssueDetail {
|
export class IssueDetail implements IIssueDetail {
|
||||||
// observables
|
// observables
|
||||||
peekIssue: TPeekIssue | undefined = undefined;
|
peekIssue: TPeekIssue | undefined = undefined;
|
||||||
|
activeIssueDetailWidgets: TIssueDetailWidget[] = ["sub-issues"];
|
||||||
|
lastWidgetAction: TIssueDetailWidget | null = null;
|
||||||
isCreateIssueModalOpen: boolean = false;
|
isCreateIssueModalOpen: boolean = false;
|
||||||
isIssueLinkModalOpen: boolean = false;
|
isIssueLinkModalOpen: boolean = false;
|
||||||
isParentIssueModalOpen: string | null = null;
|
isParentIssueModalOpen: string | null = null;
|
||||||
|
|
@ -122,6 +130,8 @@ export class IssueDetail implements IIssueDetail {
|
||||||
isRelationModalOpen: observable.ref,
|
isRelationModalOpen: observable.ref,
|
||||||
isSubIssuesModalOpen: observable.ref,
|
isSubIssuesModalOpen: observable.ref,
|
||||||
attachmentDeleteModalId: observable.ref,
|
attachmentDeleteModalId: observable.ref,
|
||||||
|
activeIssueDetailWidgets: observable.ref,
|
||||||
|
lastWidgetAction: observable.ref,
|
||||||
// computed
|
// computed
|
||||||
isAnyModalOpen: computed,
|
isAnyModalOpen: computed,
|
||||||
// action
|
// action
|
||||||
|
|
@ -134,6 +144,9 @@ export class IssueDetail implements IIssueDetail {
|
||||||
toggleRelationModal: action,
|
toggleRelationModal: action,
|
||||||
toggleSubIssuesModal: action,
|
toggleSubIssuesModal: action,
|
||||||
toggleDeleteAttachmentModal: action,
|
toggleDeleteAttachmentModal: action,
|
||||||
|
setActiveIssueDetailWidgets: action,
|
||||||
|
setLastWidgetAction: action,
|
||||||
|
toggleActiveIssueDetailWidget: action,
|
||||||
});
|
});
|
||||||
|
|
||||||
// store
|
// store
|
||||||
|
|
@ -178,6 +191,18 @@ export class IssueDetail implements IIssueDetail {
|
||||||
(this.isRelationModalOpen = { issueId, relationType });
|
(this.isRelationModalOpen = { issueId, relationType });
|
||||||
toggleSubIssuesModal = (issueId: string | null) => (this.isSubIssuesModalOpen = issueId);
|
toggleSubIssuesModal = (issueId: string | null) => (this.isSubIssuesModalOpen = issueId);
|
||||||
toggleDeleteAttachmentModal = (attachmentId: string | null) => (this.attachmentDeleteModalId = attachmentId);
|
toggleDeleteAttachmentModal = (attachmentId: string | null) => (this.attachmentDeleteModalId = attachmentId);
|
||||||
|
setActiveIssueDetailWidgets = (state: TIssueDetailWidget[]) => {
|
||||||
|
this.activeIssueDetailWidgets = state;
|
||||||
|
if (this.lastWidgetAction) this.lastWidgetAction = null;
|
||||||
|
};
|
||||||
|
setLastWidgetAction = (action: TIssueDetailWidget) => {
|
||||||
|
this.activeIssueDetailWidgets = [action];
|
||||||
|
};
|
||||||
|
toggleActiveIssueDetailWidget = (state: TIssueDetailWidget) => {
|
||||||
|
if (this.activeIssueDetailWidgets && this.activeIssueDetailWidgets.includes(state))
|
||||||
|
this.activeIssueDetailWidgets = this.activeIssueDetailWidgets.filter((s) => s !== state);
|
||||||
|
else this.activeIssueDetailWidgets = [state, ...this.activeIssueDetailWidgets];
|
||||||
|
};
|
||||||
|
|
||||||
// issue
|
// issue
|
||||||
fetchIssue = async (
|
fetchIssue = async (
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue