fix: merge conflicts resolved

This commit is contained in:
sriram veeraghanta 2023-12-11 22:49:47 +05:30
commit 71d24c1c8c
40 changed files with 297 additions and 287 deletions

View File

@ -343,13 +343,6 @@ class MagicSignInEndpoint(BaseAPIView):
if str(token) == str(user_token): if str(token) == str(user_token):
user = User.objects.get(email=email) user = User.objects.get(email=email)
if not user.is_active:
return Response(
{
"error": "Your account has been deactivated. Please contact your site administrator."
},
status=status.HTTP_403_FORBIDDEN,
)
# Send event # Send event
auth_events.delay( auth_events.delay(
user=user.id, user=user.id,

View File

@ -14,8 +14,8 @@ type Props = {
handleClose: () => void; handleClose: () => void;
data?: ILinkDetails | null; data?: ILinkDetails | null;
status: boolean; status: boolean;
createIssueLink: (formData: IIssueLink | ModuleLink) => Promise<void>; createIssueLink: (formData: IIssueLink | ModuleLink) => Promise<ILinkDetails> | Promise<void> | void;
updateIssueLink: (formData: IIssueLink | ModuleLink, linkId: string) => Promise<void>; updateIssueLink: (formData: IIssueLink | ModuleLink, linkId: string) => Promise<ILinkDetails> | Promise<void> | void;
}; };
const defaultValues: IIssueLink | ModuleLink = { const defaultValues: IIssueLink | ModuleLink = {
@ -31,7 +31,7 @@ export const LinkModal: FC<Props> = (props) => {
handleSubmit, handleSubmit,
control, control,
reset, reset,
} = useForm<ModuleLink>({ } = useForm<IIssueLink | ModuleLink>({
defaultValues, defaultValues,
}); });

View File

@ -468,7 +468,7 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
<div className="flex items-center gap-2.5"> <div className="flex items-center gap-2.5">
{progressPercentage ? ( {progressPercentage ? (
<span className="flex h-5 w-9 items-center justify-center rounded bg-amber-50 text-xs font-medium text-amber-500"> <span className="flex h-5 w-9 items-center justify-center rounded bg-amber-500/20 text-xs font-medium text-amber-500">
{progressPercentage ? `${progressPercentage}%` : ""} {progressPercentage ? `${progressPercentage}%` : ""}
</span> </span>
) : ( ) : (

View File

@ -84,10 +84,10 @@ export const IssueAttachmentUpload: React.FC<Props> = observer((props) => {
disabled: isLoading || disabled, disabled: isLoading || disabled,
}); });
const maxFileSize = envConfig?.file_size_limit ?? MAX_FILE_SIZE;
const fileError = const fileError =
fileRejections.length > 0 fileRejections.length > 0 ? `Invalid file type or size (max ${maxFileSize / 1024 / 1024} MB)` : null;
? `Invalid file type or size (max ${envConfig?.file_size_limit ?? MAX_FILE_SIZE / 1024 / 1024} MB)`
: null;
return ( return (
<div <div

View File

@ -3,6 +3,8 @@ import { Dialog, Transition } from "@headlessui/react";
import { AlertTriangle } from "lucide-react"; import { AlertTriangle } from "lucide-react";
// ui // ui
import { Button } from "@plane/ui"; import { Button } from "@plane/ui";
// hooks
import useToast from "hooks/use-toast";
// types // types
import type { IIssue } from "types"; import type { IIssue } from "types";
@ -18,6 +20,8 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
const [isDeleteLoading, setIsDeleteLoading] = useState(false); const [isDeleteLoading, setIsDeleteLoading] = useState(false);
const { setToastAlert } = useToast();
useEffect(() => { useEffect(() => {
setIsDeleteLoading(false); setIsDeleteLoading(false);
}, [isOpen]); }, [isOpen]);
@ -31,7 +35,21 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
if (onSubmit) if (onSubmit)
await onSubmit() await onSubmit()
.then(() => onClose()) .then(() => {
setToastAlert({
title: "Success",
type: "success",
message: "Issue deleted successfully",
});
onClose();
})
.catch(() => {
setToastAlert({
title: "Error",
type: "error",
message: "Failed to delete issue",
});
})
.finally(() => setIsDeleteLoading(false)); .finally(() => setIsDeleteLoading(false));
}; };

View File

@ -54,20 +54,19 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
const issues = issueIds.map((id) => issuesResponse?.[id]); const issues = issueIds.map((id) => issuesResponse?.[id]);
const updateIssue = async (issue: IIssue, payload: IBlockUpdateData) => { const updateIssueBlockStructure = async (issue: IIssue, data: IBlockUpdateData) => {
if (!workspaceSlug) return; if (!workspaceSlug) return;
//Todo fix sort order in the structure const payload: any = { ...data };
await issueStore.updateIssue( if (data.sort_order) payload.sort_order = data.sort_order.newSortOrder;
workspaceSlug.toString(),
issue.project, await issueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, payload, viewId);
issue.id, };
{
start_date: payload.start_date, const updateIssue = async (projectId: string, issueId: string, payload: Partial<IIssue>) => {
target_date: payload.target_date, if (!workspaceSlug) return;
},
viewId await issueStore.updateIssue(workspaceSlug.toString(), projectId, issueId, payload, viewId);
);
}; };
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
@ -80,7 +79,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
title="Issues" title="Issues"
loaderTitle="Issues" loaderTitle="Issues"
blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null} blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null}
blockUpdateHandler={updateIssue} blockUpdateHandler={updateIssueBlockStructure}
blockToRender={(data: IIssue) => <IssueGanttBlock data={data} />} blockToRender={(data: IIssue) => <IssueGanttBlock data={data} />}
sidebarToRender={(props) => ( sidebarToRender={(props) => (
<IssueGanttSidebar <IssueGanttSidebar
@ -102,8 +101,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
projectId={peekProjectId.toString()} projectId={peekProjectId.toString()}
issueId={peekIssueId.toString()} issueId={peekIssueId.toString()}
handleIssue={async (issueToUpdate) => { handleIssue={async (issueToUpdate) => {
// TODO: update the logic here await updateIssue(peekProjectId.toString(), peekIssueId.toString(), issueToUpdate);
await updateIssue(issueToUpdate as IIssue, {});
}} }}
/> />
)} )}

View File

@ -7,9 +7,15 @@ import { useRouter } from "next/router";
export const CycleGanttLayout: React.FC = observer(() => { export const CycleGanttLayout: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { cycleId } = router.query as { cycleId: string }; const { cycleId } = router.query;
const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore(); const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore();
return <BaseGanttRoot issueFiltersStore={cycleIssueFilterStore} issueStore={cycleIssueStore} viewId={cycleId} />; return (
<BaseGanttRoot
issueFiltersStore={cycleIssueFilterStore}
issueStore={cycleIssueStore}
viewId={cycleId?.toString()}
/>
);
}); });

View File

@ -7,9 +7,15 @@ import { useRouter } from "next/router";
export const ModuleGanttLayout: React.FC = observer(() => { export const ModuleGanttLayout: React.FC = observer(() => {
const router = useRouter(); const router = useRouter();
const { moduleId } = router.query as { moduleId: string }; const { moduleId } = router.query;
const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore(); const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore();
return <BaseGanttRoot issueFiltersStore={moduleIssueFilterStore} issueStore={moduleIssueStore} viewId={moduleId} />; return (
<BaseGanttRoot
issueFiltersStore={moduleIssueFilterStore}
issueStore={moduleIssueStore}
viewId={moduleId?.toString()}
/>
);
}); });

View File

@ -195,25 +195,12 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
const handleDeleteIssue = async () => { const handleDeleteIssue = async () => {
if (!handleDragDrop) return; if (!handleDragDrop) return;
await handleDragDrop(dragState.source, dragState.destination, sub_group_by, group_by, issues, issueIds) await handleDragDrop(dragState.source, dragState.destination, sub_group_by, group_by, issues, issueIds).finally(
.then(() => { () => {
setToastAlert({
title: "Success",
type: "success",
message: "Issue deleted successfully",
});
})
.catch(() => {
setToastAlert({
title: "Error",
type: "error",
message: "Failed to delete issue",
});
})
.finally(() => {
setDeleteIssueModal(false); setDeleteIssueModal(false);
setDragState({}); setDragState({});
}); }
);
}; };
const handleKanBanToggle = (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => { const handleKanBanToggle = (toggle: "groupByHeaderMinMax" | "subgroupByIssuesVisibility", value: string) => {

View File

@ -17,7 +17,7 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
const { issue, handleDelete, handleUpdate, handleRemoveFromView, customActionButton } = props; const { issue, handleDelete, handleUpdate, handleRemoveFromView, customActionButton } = props;
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug, cycleId } = router.query;
// states // states
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false); const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
@ -75,7 +75,10 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setIssueToEdit(issue); setIssueToEdit({
...issue,
cycle: cycleId?.toString() ?? null,
});
setCreateUpdateIssueModal(true); setCreateUpdateIssueModal(true);
}} }}
> >

View File

@ -17,7 +17,7 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
const { issue, handleDelete, handleUpdate, handleRemoveFromView, customActionButton } = props; const { issue, handleDelete, handleUpdate, handleRemoveFromView, customActionButton } = props;
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug, moduleId } = router.query;
// states // states
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false); const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
@ -75,7 +75,7 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = (props) => {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
setIssueToEdit(issue); setIssueToEdit({ ...issue, module: moduleId?.toString() ?? null });
setCreateUpdateIssueModal(true); setCreateUpdateIssueModal(true);
}} }}
> >

View File

@ -27,7 +27,7 @@ export const SpreadsheetAssigneeColumn: React.FC<Props> = ({ issue, members, onC
value={issue.assignees} value={issue.assignees}
defaultOptions={issue?.assignee_details ? issue.assignee_details : []} defaultOptions={issue?.assignee_details ? issue.assignee_details : []}
onChange={(data) => onChange({ assignees: data })} onChange={(data) => onChange({ assignees: data })}
className="h-11 w-full" className="h-11 w-full border-b-[0.5px] border-custom-border-200"
buttonClassName="!shadow-none !border-0 h-full w-full px-2.5 py-1 " buttonClassName="!shadow-none !border-0 h-full w-full px-2.5 py-1 "
noLabelBorder noLabelBorder
hideDropdownArrow hideDropdownArrow

View File

@ -18,7 +18,7 @@ export const SpreadsheetAttachmentColumn: React.FC<Props> = (props) => {
return ( return (
<> <>
<div className="flex h-11 w-full items-center px-2.5 py-1 text-xs"> <div className="flex h-11 w-full items-center px-2.5 py-1 text-xs border-b-[0.5px] border-custom-border-200">
{issue.attachment_count} {issue.attachment_count === 1 ? "attachment" : "attachments"} {issue.attachment_count} {issue.attachment_count === 1 ? "attachment" : "attachments"}
</div> </div>

View File

@ -19,7 +19,7 @@ export const SpreadsheetCreatedOnColumn: React.FC<Props> = ({ issue, expandedIss
return ( return (
<> <>
<div className="flex h-11 w-full items-center justify-center text-xs"> <div className="flex h-11 w-full items-center justify-center text-xs border-b-[0.5px] border-custom-border-200">
{renderLongDetailDateFormat(issue.created_at)} {renderLongDetailDateFormat(issue.created_at)}
</div> </div>

View File

@ -24,7 +24,7 @@ export const SpreadsheetDueDateColumn: React.FC<Props> = ({ issue, onChange, exp
<ViewDueDateSelect <ViewDueDateSelect
issue={issue} issue={issue}
onChange={(val) => onChange({ target_date: val })} onChange={(val) => onChange({ target_date: val })}
className="flex !h-11 !w-full max-w-full items-center px-2.5 py-1" className="flex !h-11 !w-full max-w-full items-center px-2.5 py-1 border-b-[0.5px] border-custom-border-200"
noBorder noBorder
disabled={disabled} disabled={disabled}
/> />

View File

@ -25,7 +25,7 @@ export const SpreadsheetEstimateColumn: React.FC<Props> = (props) => {
projectId={issue.project_detail?.id ?? null} projectId={issue.project_detail?.id ?? null}
value={issue.estimate_point} value={issue.estimate_point}
onChange={(data) => onChange({ estimate_point: data })} onChange={(data) => onChange({ estimate_point: data })}
className="h-11 w-full" className="h-11 w-full border-b-[0.5px] border-custom-border-200"
buttonClassName="h-full w-full px-2.5 py-1 !shadow-none !border-0" buttonClassName="h-full w-full px-2.5 py-1 !shadow-none !border-0"
hideDropdownArrow hideDropdownArrow
disabled={disabled} disabled={disabled}

View File

@ -29,7 +29,7 @@ export const SpreadsheetLabelColumn: React.FC<Props> = (props) => {
value={issue.labels} value={issue.labels}
defaultOptions={issue?.label_details ? issue.label_details : []} defaultOptions={issue?.label_details ? issue.label_details : []}
onChange={(data) => onChange({ labels: data })} onChange={(data) => onChange({ labels: data })}
className="h-11 w-full" className="h-11 w-full border-b-[0.5px] border-custom-border-200"
buttonClassName="px-2.5 h-full" buttonClassName="px-2.5 h-full"
hideDropdownArrow hideDropdownArrow
maxRender={1} maxRender={1}

View File

@ -18,7 +18,7 @@ export const SpreadsheetLinkColumn: React.FC<Props> = (props) => {
return ( return (
<> <>
<div className="flex h-11 w-full items-center px-2.5 py-1 text-xs"> <div className="flex h-11 w-full items-center px-2.5 py-1 text-xs border-b-[0.5px] border-custom-border-200">
{issue.link_count} {issue.link_count === 1 ? "link" : "links"} {issue.link_count} {issue.link_count === 1 ? "link" : "links"}
</div> </div>

View File

@ -24,7 +24,7 @@ export const SpreadsheetPriorityColumn: React.FC<Props> = ({ issue, onChange, ex
<PrioritySelect <PrioritySelect
value={issue.priority} value={issue.priority}
onChange={(data) => onChange({ priority: data })} onChange={(data) => onChange({ priority: data })}
className="h-11 w-full" className="h-11 w-full border-b-[0.5px] border-custom-border-200"
buttonClassName="!shadow-none !border-0 h-full w-full px-2.5 py-1" buttonClassName="!shadow-none !border-0 h-full w-full px-2.5 py-1"
showTitle showTitle
highlightUrgentPriority={false} highlightUrgentPriority={false}

View File

@ -24,7 +24,7 @@ export const SpreadsheetStartDateColumn: React.FC<Props> = ({ issue, onChange, e
<ViewStartDateSelect <ViewStartDateSelect
issue={issue} issue={issue}
onChange={(val) => onChange({ start_date: val })} onChange={(val) => onChange({ start_date: val })}
className="flex !h-11 !w-full max-w-full items-center px-2.5 py-1" className="flex !h-11 !w-full max-w-full items-center px-2.5 py-1 border-b-[0.5px] border-custom-border-200"
noBorder noBorder
disabled={disabled} disabled={disabled}
/> />

View File

@ -29,7 +29,7 @@ export const SpreadsheetStateColumn: React.FC<Props> = (props) => {
value={issue.state} value={issue.state}
defaultOptions={issue?.state_detail ? [issue.state_detail] : []} defaultOptions={issue?.state_detail ? [issue.state_detail] : []}
onChange={(data) => onChange({ state: data.id, state_detail: data })} onChange={(data) => onChange({ state: data.id, state_detail: data })}
className="w-full !h-11" className="w-full !h-11 border-b-[0.5px] border-custom-border-200"
buttonClassName="!shadow-none !border-0 h-full w-full" buttonClassName="!shadow-none !border-0 h-full w-full"
hideDropdownArrow hideDropdownArrow
disabled={disabled} disabled={disabled}

View File

@ -18,7 +18,7 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = (props) => {
return ( return (
<> <>
<div className="flex h-11 w-full items-center px-2.5 py-1 text-xs"> <div className="flex h-11 w-full items-center px-2.5 py-1 text-xs border-b-[0.5px] border-custom-border-200">
{issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"} {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
</div> </div>

View File

@ -21,7 +21,7 @@ export const SpreadsheetUpdatedOnColumn: React.FC<Props> = (props) => {
return ( return (
<> <>
<div className="flex h-11 w-full items-center justify-center text-xs"> <div className="flex h-11 w-full items-center justify-center text-xs border-b-[0.5px] border-custom-border-200">
{renderLongDetailDateFormat(issue.updated_at)} {renderLongDetailDateFormat(issue.updated_at)}
</div> </div>

View File

@ -159,13 +159,13 @@ export const SpreadsheetColumn: React.FC<Props> = (props) => {
</CustomMenu> </CustomMenu>
</div> </div>
<div className="h-full w-full divide-y-[0.5px] border-b-[0.5px] min-w-[8rem]"> <div className="h-full w-full min-w-[8rem]">
{issues?.map((issue) => { {issues?.map((issue) => {
const disableUserActions = !canEditProperties(issue.project); const disableUserActions = !canEditProperties(issue.project);
return ( return (
<div <div
key={`${property}-${issue.id}`} key={`${property}-${issue.id}`}
className={`h-fit divide-y-[0.5px] border-custom-border-200 ${ className={`h-fit ${
disableUserActions ? "" : "cursor-pointer hover:bg-custom-background-80" disableUserActions ? "" : "cursor-pointer hover:bg-custom-background-80"
}`} }`}
> >

View File

@ -77,6 +77,13 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
}; };
}, []); }, []);
if (!issues || issues.length === 0)
return (
<div className="grid h-full w-full place-items-center">
<Spinner />
</div>
);
return ( return (
<div className="relative flex h-full w-full overflow-x-auto whitespace-nowrap rounded-lg bg-custom-background-200 text-custom-text-200"> <div className="relative flex h-full w-full overflow-x-auto whitespace-nowrap rounded-lg bg-custom-background-200 text-custom-text-200">
<div className="flex h-full w-full flex-col"> <div className="flex h-full w-full flex-col">
@ -84,7 +91,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
ref={containerRef} ref={containerRef}
className="horizontal-scroll-enable flex divide-x-[0.5px] divide-custom-border-200 overflow-y-auto" className="horizontal-scroll-enable flex divide-x-[0.5px] divide-custom-border-200 overflow-y-auto"
> >
{issues && issues.length > 0 ? ( {issues && issues.length > 0 && (
<> <>
<div className="sticky left-0 z-[2] w-[28rem]"> <div className="sticky left-0 z-[2] w-[28rem]">
<div <div
@ -134,10 +141,6 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
states={states} states={states}
/> />
</> </>
) : (
<div className="grid h-full w-full place-items-center">
<Spinner />
</div>
)} )}
<div /> {/* empty div to show right most border */} <div /> {/* empty div to show right most border */}
</div> </div>

View File

@ -1,5 +1,5 @@
import { FC, useState } from "react"; import { FC, useState } from "react";
import { mutate } from "swr";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
// mobx store // mobx store
@ -17,30 +17,25 @@ import {
SidebarPrioritySelect, SidebarPrioritySelect,
SidebarStateSelect, SidebarStateSelect,
} from "../sidebar-select"; } from "../sidebar-select";
// services
import { IssueService } from "services/issue";
// hooks
import useToast from "hooks/use-toast";
// components // components
import { CustomDatePicker } from "components/ui"; import { CustomDatePicker } from "components/ui";
import { LinkModal, LinksList } from "components/core"; import { LinkModal, LinksList } from "components/core";
// types // types
import { IIssue, IIssueLink, TIssuePriorities, ILinkDetails } from "types"; import { IIssue, TIssuePriorities, ILinkDetails, IIssueLink } from "types";
// fetch-keys
import { ISSUE_DETAILS } from "constants/fetch-keys";
// constants // constants
import { EUserWorkspaceRoles } from "constants/workspace"; import { EUserWorkspaceRoles } from "constants/workspace";
interface IPeekOverviewProperties { interface IPeekOverviewProperties {
issue: IIssue; issue: IIssue;
issueUpdate: (issue: Partial<IIssue>) => void; issueUpdate: (issue: Partial<IIssue>) => void;
issueLinkCreate: (data: IIssueLink) => Promise<ILinkDetails>;
issueLinkUpdate: (data: IIssueLink, linkId: string) => Promise<ILinkDetails>;
issueLinkDelete: (linkId: string) => Promise<void>;
disableUserActions: boolean; disableUserActions: boolean;
} }
const issueService = new IssueService();
export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((props) => { export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((props) => {
const { issue, issueUpdate, disableUserActions } = props; const { issue, issueUpdate, issueLinkCreate, issueLinkUpdate, issueLinkDelete, disableUserActions } = props;
// states // states
const [linkModal, setLinkModal] = useState(false); const [linkModal, setLinkModal] = useState(false);
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<ILinkDetails | null>(null); const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<ILinkDetails | null>(null);
@ -54,8 +49,6 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast();
const handleState = (_state: string) => { const handleState = (_state: string) => {
issueUpdate({ ...issue, state: _state }); issueUpdate({ ...issue, state: _state });
}; };
@ -81,61 +74,6 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
issueUpdate({ ...issue, ...formData }); issueUpdate({ ...issue, ...formData });
}; };
const handleCreateLink = async (formData: IIssueLink) => {
if (!workspaceSlug || !projectId || !issue) return;
const payload = { metadata: {}, ...formData };
await issueService
.createIssueLink(workspaceSlug as string, projectId as string, issue.id, payload)
.then(() => mutate(ISSUE_DETAILS(issue.id)))
.catch((err) => {
if (err.status === 400)
setToastAlert({
type: "error",
title: "Error!",
message: "This URL already exists for this issue.",
});
else
setToastAlert({
type: "error",
title: "Error!",
message: "Something went wrong. Please try again.",
});
});
};
const handleUpdateLink = async (formData: IIssueLink, linkId: string) => {
if (!workspaceSlug || !projectId || !issue) return;
const payload = { metadata: {}, ...formData };
const updatedLinks = issue.issue_link.map((l) =>
l.id === linkId
? {
...l,
title: formData.title,
url: formData.url,
}
: l
);
mutate<IIssue>(
ISSUE_DETAILS(issue.id),
(prevData) => ({ ...(prevData as IIssue), issue_link: updatedLinks }),
false
);
await issueService
.updateIssueLink(workspaceSlug as string, projectId as string, issue.id, linkId, payload)
.then(() => {
mutate(ISSUE_DETAILS(issue.id));
})
.catch((err) => {
console.log(err);
});
};
const handleCycleOrModuleChange = async () => { const handleCycleOrModuleChange = async () => {
if (!workspaceSlug || !projectId) return; if (!workspaceSlug || !projectId) return;
@ -147,27 +85,6 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
setLinkModal(true); setLinkModal(true);
}; };
const handleDeleteLink = async (linkId: string) => {
if (!workspaceSlug || !projectId || !issue) return;
const updatedLinks = issue.issue_link.filter((l) => l.id !== linkId);
mutate<IIssue>(
ISSUE_DETAILS(issue.id),
(prevData) => ({ ...(prevData as IIssue), issue_link: updatedLinks }),
false
);
await issueService
.deleteIssueLink(workspaceSlug as string, projectId as string, issue.id, linkId)
.then(() => {
mutate(ISSUE_DETAILS(issue.id));
})
.catch((err) => {
console.log(err);
});
};
const projectDetails = workspaceSlug ? getProjectById(workspaceSlug.toString(), issue.project) : null; const projectDetails = workspaceSlug ? getProjectById(workspaceSlug.toString(), issue.project) : null;
const isEstimateEnabled = projectDetails?.estimate; const isEstimateEnabled = projectDetails?.estimate;
@ -187,8 +104,8 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
}} }}
data={selectedLinkToUpdate} data={selectedLinkToUpdate}
status={selectedLinkToUpdate ? true : false} status={selectedLinkToUpdate ? true : false}
createIssueLink={handleCreateLink} createIssueLink={issueLinkCreate}
updateIssueLink={handleUpdateLink} updateIssueLink={issueLinkUpdate}
/> />
<div className="flex flex-col"> <div className="flex flex-col">
<div className="flex w-full flex-col gap-5 py-5"> <div className="flex w-full flex-col gap-5 py-5">
@ -373,7 +290,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
{issue?.issue_link && issue.issue_link.length > 0 ? ( {issue?.issue_link && issue.issue_link.length > 0 ? (
<LinksList <LinksList
links={issue.issue_link} links={issue.issue_link}
handleDeleteLink={handleDeleteLink} handleDeleteLink={issueLinkDelete}
handleEditLink={handleEditLink} handleEditLink={handleEditLink}
userAuth={{ userAuth={{
isGuest: currentProjectRole === EUserWorkspaceRoles.GUEST, isGuest: currentProjectRole === EUserWorkspaceRoles.GUEST,

View File

@ -10,7 +10,7 @@ import { IssueView } from "components/issues";
// helpers // helpers
import { copyUrlToClipboard } from "helpers/string.helper"; import { copyUrlToClipboard } from "helpers/string.helper";
// types // types
import { IIssue } from "types"; import { IIssue, IIssueLink } from "types";
// constants // constants
import { EUserWorkspaceRoles } from "constants/workspace"; import { EUserWorkspaceRoles } from "constants/workspace";
@ -42,6 +42,9 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
removeIssueReaction, removeIssueReaction,
createIssueSubscription, createIssueSubscription,
removeIssueSubscription, removeIssueSubscription,
createIssueLink,
updateIssueLink,
deleteIssueLink,
getIssue, getIssue,
loader, loader,
fetchPeekIssueDetails, fetchPeekIssueDetails,
@ -121,6 +124,13 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
const issueSubscriptionRemove = () => removeIssueSubscription(workspaceSlug, projectId, issueId); const issueSubscriptionRemove = () => removeIssueSubscription(workspaceSlug, projectId, issueId);
const issueLinkCreate = (formData: IIssueLink) => createIssueLink(workspaceSlug, projectId, issueId, formData);
const issueLinkUpdate = (formData: IIssueLink, linkId: string) =>
updateIssueLink(workspaceSlug, projectId, issueId, linkId, formData);
const issueLinkDelete = (linkId: string) => deleteIssueLink(workspaceSlug, projectId, issueId, linkId);
const handleDeleteIssue = async () => { const handleDeleteIssue = async () => {
if (isArchived) await deleteArchivedIssue(workspaceSlug, projectId, issue!); if (isArchived) await deleteArchivedIssue(workspaceSlug, projectId, issue!);
else removeIssueFromStructure(workspaceSlug, projectId, issue!); else removeIssueFromStructure(workspaceSlug, projectId, issue!);
@ -159,6 +169,9 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
issueCommentReactionRemove={issueCommentReactionRemove} issueCommentReactionRemove={issueCommentReactionRemove}
issueSubscriptionCreate={issueSubscriptionCreate} issueSubscriptionCreate={issueSubscriptionCreate}
issueSubscriptionRemove={issueSubscriptionRemove} issueSubscriptionRemove={issueSubscriptionRemove}
issueLinkCreate={issueLinkCreate}
issueLinkUpdate={issueLinkUpdate}
issueLinkDelete={issueLinkDelete}
handleDeleteIssue={handleDeleteIssue} handleDeleteIssue={handleDeleteIssue}
disableUserActions={[5, 10].includes(userRole)} disableUserActions={[5, 10].includes(userRole)}
showCommentAccessSpecifier={currentProjectDetails?.is_deployed} showCommentAccessSpecifier={currentProjectDetails?.is_deployed}

View File

@ -17,7 +17,7 @@ import {
// ui // ui
import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon, Spinner } from "@plane/ui"; import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon, Spinner } from "@plane/ui";
// types // types
import { IIssue } from "types"; import { IIssue, IIssueLink, ILinkDetails } from "types";
interface IIssueView { interface IIssueView {
workspaceSlug: string; workspaceSlug: string;
@ -38,6 +38,9 @@ interface IIssueView {
issueCommentReactionRemove: (commentId: string, reaction: string) => void; issueCommentReactionRemove: (commentId: string, reaction: string) => void;
issueSubscriptionCreate: () => void; issueSubscriptionCreate: () => void;
issueSubscriptionRemove: () => void; issueSubscriptionRemove: () => void;
issueLinkCreate: (formData: IIssueLink) => Promise<ILinkDetails>;
issueLinkUpdate: (formData: IIssueLink, linkId: string) => Promise<ILinkDetails>;
issueLinkDelete: (linkId: string) => Promise<void>;
handleDeleteIssue: () => Promise<void>; handleDeleteIssue: () => Promise<void>;
children: ReactNode; children: ReactNode;
disableUserActions?: boolean; disableUserActions?: boolean;
@ -84,6 +87,9 @@ export const IssueView: FC<IIssueView> = observer((props) => {
issueCommentReactionRemove, issueCommentReactionRemove,
issueSubscriptionCreate, issueSubscriptionCreate,
issueSubscriptionRemove, issueSubscriptionRemove,
issueLinkCreate,
issueLinkUpdate,
issueLinkDelete,
handleDeleteIssue, handleDeleteIssue,
children, children,
disableUserActions = false, disableUserActions = false,
@ -286,6 +292,9 @@ export const IssueView: FC<IIssueView> = observer((props) => {
<PeekOverviewProperties <PeekOverviewProperties
issue={issue} issue={issue}
issueUpdate={issueUpdate} issueUpdate={issueUpdate}
issueLinkCreate={issueLinkCreate}
issueLinkUpdate={issueLinkUpdate}
issueLinkDelete={issueLinkDelete}
disableUserActions={disableUserActions} disableUserActions={disableUserActions}
/> />
@ -342,6 +351,9 @@ export const IssueView: FC<IIssueView> = observer((props) => {
<PeekOverviewProperties <PeekOverviewProperties
issue={issue} issue={issue}
issueUpdate={issueUpdate} issueUpdate={issueUpdate}
issueLinkCreate={issueLinkCreate}
issueLinkUpdate={issueLinkUpdate}
issueLinkDelete={issueLinkDelete}
disableUserActions={disableUserActions} disableUserActions={disableUserActions}
/> />
</div> </div>

View File

@ -84,6 +84,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
user: { currentUser, currentProjectRole }, user: { currentUser, currentProjectRole },
projectState: { states }, projectState: { states },
projectIssues: { removeIssue }, projectIssues: { removeIssue },
issueDetail: { createIssueLink, updateIssueLink, deleteIssueLink },
} = useMobxStore(); } = useMobxStore();
const router = useRouter(); const router = useRouter();
@ -129,80 +130,19 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
[workspaceSlug, projectId, issueId, issueDetail, currentUser] [workspaceSlug, projectId, issueId, issueDetail, currentUser]
); );
const handleCreateLink = async (formData: IIssueLink) => { const issueLinkCreate = (formData: IIssueLink) => {
if (!workspaceSlug || !projectId || !issueDetail) return; if (!workspaceSlug || !projectId || !issueId) return;
createIssueLink(workspaceSlug.toString(), projectId.toString(), issueId.toString(), formData);
const payload = { metadata: {}, ...formData };
await issueService
.createIssueLink(workspaceSlug as string, projectId as string, issueDetail.id, payload)
.then(() => mutate(ISSUE_DETAILS(issueDetail.id)))
.catch((err) => {
if (err.status === 400)
setToastAlert({
type: "error",
title: "Error!",
message: "This URL already exists for this issue.",
});
else
setToastAlert({
type: "error",
title: "Error!",
message: "Something went wrong. Please try again.",
});
});
}; };
const handleUpdateLink = async (formData: IIssueLink, linkId: string) => { const issueLinkUpdate = (formData: IIssueLink, linkId: string) => {
if (!workspaceSlug || !projectId || !issueDetail) return; if (!workspaceSlug || !projectId || !issueId) return;
updateIssueLink(workspaceSlug.toString(), projectId.toString(), issueId.toString(), linkId, formData);
const payload = { metadata: {}, ...formData };
const updatedLinks = issueDetail.issue_link.map((l) =>
l.id === linkId
? {
...l,
title: formData.title,
url: formData.url,
}
: l
);
mutate<IIssue>(
ISSUE_DETAILS(issueDetail.id),
(prevData) => ({ ...(prevData as IIssue), issue_link: updatedLinks }),
false
);
await issueService
.updateIssueLink(workspaceSlug as string, projectId as string, issueDetail.id, linkId, payload)
.then(() => {
mutate(ISSUE_DETAILS(issueDetail.id));
})
.catch((err) => {
console.log(err);
});
}; };
const handleDeleteLink = async (linkId: string) => { const issueLinkDelete = (linkId: string) => {
if (!workspaceSlug || !projectId || !issueDetail) return; if (!workspaceSlug || !projectId || !issueId) return;
deleteIssueLink(workspaceSlug.toString(), projectId.toString(), issueId.toString(), linkId);
const updatedLinks = issueDetail.issue_link.filter((l) => l.id !== linkId);
mutate<IIssue>(
ISSUE_DETAILS(issueDetail.id),
(prevData) => ({ ...(prevData as IIssue), issue_link: updatedLinks }),
false
);
await issueService
.deleteIssueLink(workspaceSlug as string, projectId as string, issueDetail.id, linkId)
.then(() => {
mutate(ISSUE_DETAILS(issueDetail.id));
})
.catch((err) => {
console.log(err);
});
}; };
const handleCopyText = () => { const handleCopyText = () => {
@ -264,8 +204,8 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
}} }}
data={selectedLinkToUpdate} data={selectedLinkToUpdate}
status={selectedLinkToUpdate ? true : false} status={selectedLinkToUpdate ? true : false}
createIssueLink={handleCreateLink} createIssueLink={issueLinkCreate}
updateIssueLink={handleUpdateLink} updateIssueLink={issueLinkUpdate}
/> />
{workspaceSlug && projectId && issueDetail && ( {workspaceSlug && projectId && issueDetail && (
<DeleteIssueModal <DeleteIssueModal
@ -659,7 +599,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
{ {
<LinksList <LinksList
links={issueDetail.issue_link} links={issueDetail.issue_link}
handleDeleteLink={handleDeleteLink} handleDeleteLink={issueLinkDelete}
handleEditLink={handleEditLink} handleEditLink={handleEditLink}
userAuth={{ userAuth={{
isGuest: currentProjectRole === 5, isGuest: currentProjectRole === 5,

View File

@ -311,7 +311,11 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<CustomSelect <CustomSelect
customButton={ customButton={
<span <span
className={`flex h-6 w-20 cursor-default items-center justify-center rounded-sm text-sm ${moduleStatus?.textColor} ${moduleStatus?.bgColor}`} className="flex h-6 w-20 cursor-default items-center justify-center rounded-sm text-center text-xs"
style={{
color: moduleStatus ? moduleStatus.color : "#a3a3a2",
backgroundColor: moduleStatus ? `${moduleStatus.color}20` : "#a3a3a220",
}}
> >
{moduleStatus?.label ?? "Backlog"} {moduleStatus?.label ?? "Backlog"}
</span> </span>
@ -459,7 +463,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
<div className="flex items-center gap-2.5"> <div className="flex items-center gap-2.5">
{progressPercentage ? ( {progressPercentage ? (
<span className="flex h-5 w-9 items-center justify-center rounded bg-amber-50 text-xs font-medium text-amber-500"> <span className="flex h-5 w-9 items-center justify-center rounded bg-amber-500/20 text-xs font-medium text-amber-500">
{progressPercentage ? `${progressPercentage}%` : ""} {progressPercentage ? `${progressPercentage}%` : ""}
</span> </span>
) : ( ) : (

View File

@ -54,7 +54,14 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
// for removing filters from a key // for removing filters from a key
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => { const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
if (!value) return; // If value is null then remove all the filters of that key
if (!value) {
setValue("query_data", {
...selectedFilters,
[key]: null,
});
return;
}
const newValues = selectedFilters?.[key] ?? []; const newValues = selectedFilters?.[key] ?? [];

View File

@ -100,7 +100,9 @@ const ProfileActivityPage: NextPageWithLayout = () => {
activityItem.field !== "estimate" ? ( activityItem.field !== "estimate" ? (
<span className="text-custom-text-200"> <span className="text-custom-text-200">
created{" "} created{" "}
<Link href={`/${workspaceSlug}/projects/${activityItem.project}/issues/${activityItem.issue}`}> <Link
href={`/${activityItem.workspace_detail.slug}/projects/${activityItem.project}/issues/${activityItem.issue}`}
>
<span className="inline-flex items-center hover:underline"> <span className="inline-flex items-center hover:underline">
this issue. <ExternalLinkIcon className="ml-1 h-3.5 w-3.5" /> this issue. <ExternalLinkIcon className="ml-1 h-3.5 w-3.5" />
</span> </span>

View File

@ -10,7 +10,7 @@ export abstract class APIService {
} }
setRefreshToken(token: string) { setRefreshToken(token: string) {
Cookies.set("refreshToken", token); Cookies.set("refreshToken", token, { expires: 30 });
} }
getRefreshToken() { getRefreshToken() {
@ -22,7 +22,7 @@ export abstract class APIService {
} }
setAccessToken(token: string) { setAccessToken(token: string) {
Cookies.set("accessToken", token); Cookies.set("accessToken", token, { expires: 30 });
} }
getAccessToken() { getAccessToken() {

View File

@ -1,7 +1,14 @@
// services // services
import { APIService } from "services/api.service"; import { APIService } from "services/api.service";
// type // type
import type { IUser, IIssue, IIssueActivity, ISubIssueResponse, IIssueDisplayProperties } from "types"; import type {
IIssue,
IIssueActivity,
ISubIssueResponse,
IIssueDisplayProperties,
ILinkDetails,
IIssueLink,
} from "types";
import { IIssueResponse } from "store_legacy/issues/types"; import { IIssueResponse } from "store_legacy/issues/types";
// helper // helper
import { API_BASE_URL } from "helpers/common.helper"; import { API_BASE_URL } from "helpers/common.helper";
@ -184,12 +191,8 @@ export class IssueService extends APIService {
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
issueId: string, issueId: string,
data: { data: IIssueLink
metadata: any; ): Promise<ILinkDetails> {
title: string;
url: string;
}
): Promise<any> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/`, data) return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/`, data)
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {
@ -202,12 +205,8 @@ export class IssueService extends APIService {
projectId: string, projectId: string,
issueId: string, issueId: string,
linkId: string, linkId: string,
data: { data: IIssueLink
metadata: any; ): Promise<ILinkDetails> {
title: string;
url: string;
}
): Promise<any> {
return this.patch( return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/${linkId}/`, `/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-links/${linkId}/`,
data data

View File

@ -1,7 +1,7 @@
// services // services
import { APIService } from "services/api.service"; import { APIService } from "services/api.service";
// types // types
import type { IModule, IIssue, ILinkDetails } from "types"; import type { IModule, IIssue, ILinkDetails, ModuleLink } from "types";
import { IIssueResponse } from "store_legacy/issues/types"; import { IIssueResponse } from "store_legacy/issues/types";
import { API_BASE_URL } from "helpers/common.helper"; import { API_BASE_URL } from "helpers/common.helper";
@ -132,7 +132,7 @@ export class ModuleService extends APIService {
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
moduleId: string, moduleId: string,
data: Partial<ILinkDetails> data: ModuleLink
): Promise<ILinkDetails> { ): Promise<ILinkDetails> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-links/`, data) return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-links/`, data)
.then((response) => response?.data) .then((response) => response?.data)
@ -146,7 +146,7 @@ export class ModuleService extends APIService {
projectId: string, projectId: string,
moduleId: string, moduleId: string,
linkId: string, linkId: string,
data: Partial<ILinkDetails> data: ModuleLink
): Promise<ILinkDetails> { ): Promise<ILinkDetails> {
return this.patch( return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-links/${linkId}/`, `/api/workspaces/${workspaceSlug}/projects/${projectId}/modules/${moduleId}/module-links/${linkId}/`,

View File

@ -4,7 +4,7 @@ import { IssueService, IssueReactionService, IssueCommentService } from "service
import { NotificationService } from "services/notification.service"; import { NotificationService } from "services/notification.service";
// types // types
import { RootStore } from "../root"; import { RootStore } from "../root";
import type { IIssue, IIssueActivity } from "types"; import type { IIssue, IIssueActivity, IIssueLink, ILinkDetails } from "types";
// constants // constants
import { groupReactionEmojis } from "constants/issue"; import { groupReactionEmojis } from "constants/issue";
@ -51,6 +51,21 @@ export interface IIssueDetailStore {
createIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>; createIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>;
removeIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>; removeIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>;
createIssueLink: (
workspaceSlug: string,
projectId: string,
issueId: string,
data: IIssueLink
) => Promise<ILinkDetails>;
updateIssueLink: (
workspaceSlug: string,
projectId: string,
issueId: string,
linkId: string,
data: IIssueLink
) => Promise<ILinkDetails>;
deleteIssueLink: (workspaceSlug: string, projectId: string, issueId: string, linkId: string) => Promise<void>;
fetchIssueActivity: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>; fetchIssueActivity: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
createIssueComment: (workspaceSlug: string, projectId: string, issueId: string, data: any) => Promise<void>; createIssueComment: (workspaceSlug: string, projectId: string, issueId: string, data: any) => Promise<void>;
updateIssueComment: ( updateIssueComment: (
@ -147,6 +162,10 @@ export class IssueDetailStore implements IIssueDetailStore {
createIssueReaction: action, createIssueReaction: action,
removeIssueReaction: action, removeIssueReaction: action,
createIssueLink: action,
updateIssueLink: action,
deleteIssueLink: action,
fetchIssueActivity: action, fetchIssueActivity: action,
createIssueComment: action, createIssueComment: action,
updateIssueComment: action, updateIssueComment: action,
@ -590,6 +609,91 @@ export class IssueDetailStore implements IIssueDetailStore {
} }
}; };
createIssueLink = async (workspaceSlug: string, projectId: string, issueId: string, data: IIssueLink) => {
try {
const response = await this.issueService.createIssueLink(workspaceSlug, projectId, issueId, data);
runInAction(() => {
this.issues = {
...this.issues,
[issueId]: {
...this.issues[issueId],
issue_link: [response, ...this.issues[issueId].issue_link],
},
};
});
return response;
} catch (error) {
console.error("Failed to create link in store", error);
this.fetchIssueDetails(workspaceSlug, projectId, issueId);
runInAction(() => {
this.error = error;
});
throw error;
}
};
updateIssueLink = async (
workspaceSlug: string,
projectId: string,
issueId: string,
linkId: string,
data: IIssueLink
) => {
try {
const response = await this.issueService.updateIssueLink(workspaceSlug, projectId, issueId, linkId, data);
runInAction(() => {
this.issues = {
...this.issues,
[issueId]: {
...this.issues[issueId],
issue_link: this.issues[issueId].issue_link.map((link) => (link.id === linkId ? response : link)),
},
};
});
return response;
} catch (error) {
console.error("Failed to update link in issue store", error);
this.fetchIssueDetails(workspaceSlug, projectId, issueId);
runInAction(() => {
this.error = error;
});
throw error;
}
};
deleteIssueLink = async (workspaceSlug: string, projectId: string, issueId: string, linkId: string) => {
try {
runInAction(() => {
this.issues = {
...this.issues,
[issueId]: {
...this.issues[issueId],
issue_link: this.issues[issueId].issue_link.filter((link) => link.id !== linkId),
},
};
});
await this.issueService.deleteIssueLink(workspaceSlug, projectId, issueId, linkId);
} catch (error) {
console.error("Failed to delete link in issue store", error);
runInAction(() => {
this.error = error;
});
throw error;
}
};
// subscriptions // subscriptions
fetchIssueSubscription = async (workspaceSlug: string, projectId: string, issueId: string) => { fetchIssueSubscription = async (workspaceSlug: string, projectId: string, issueId: string) => {
try { try {

View File

@ -257,6 +257,7 @@ export class CycleIssuesStore extends IssueBaseStore implements ICycleIssuesStor
if (!_issues) _issues = {}; if (!_issues) _issues = {};
if (!_issues[cycleId]) _issues[cycleId] = {}; if (!_issues[cycleId]) _issues[cycleId] = {};
delete _issues?.[cycleId]?.[issueId]; delete _issues?.[cycleId]?.[issueId];
_issues[cycleId] = { ..._issues[cycleId] };
runInAction(() => { runInAction(() => {
this.issues = _issues; this.issues = _issues;

View File

@ -250,6 +250,7 @@ export class ModuleIssuesStore extends IssueBaseStore implements IModuleIssuesSt
if (!_issues) _issues = {}; if (!_issues) _issues = {};
if (!_issues[moduleId]) _issues[moduleId] = {}; if (!_issues[moduleId]) _issues[moduleId] = {};
delete _issues?.[moduleId]?.[issueId]; delete _issues?.[moduleId]?.[issueId];
_issues[moduleId] = { ..._issues[moduleId] };
runInAction(() => { runInAction(() => {
this.issues = _issues; this.issues = _issues;

View File

@ -175,6 +175,7 @@ export class ProjectIssuesStore extends IssueBaseStore implements IProjectIssues
if (!_issues) _issues = {}; if (!_issues) _issues = {};
if (!_issues[projectId]) _issues[projectId] = {}; if (!_issues[projectId]) _issues[projectId] = {};
delete _issues?.[projectId]?.[issueId]; delete _issues?.[projectId]?.[issueId];
_issues[projectId] = { ..._issues[projectId] };
runInAction(() => { runInAction(() => {
this.issues = _issues; this.issues = _issues;

View File

@ -3,15 +3,15 @@ import { action, computed, observable, makeObservable, runInAction } from "mobx"
import { ProjectService } from "services/project"; import { ProjectService } from "services/project";
import { ModuleService } from "services/module.service"; import { ModuleService } from "services/module.service";
// types // types
import { RootStore } from "../root";
import { IIssue, IModule, ILinkDetails } from "types"; import { IIssue, IModule, ILinkDetails, ModuleLink } from "types";
import { import {
IIssueGroupWithSubGroupsStructure, IIssueGroupWithSubGroupsStructure,
IIssueGroupedStructure, IIssueGroupedStructure,
IIssueUnGroupedStructure, IIssueUnGroupedStructure,
} from "../issue/issue.store"; } from "../issue/issue.store";
import { IBlockUpdateData } from "components/gantt-chart"; import { IBlockUpdateData } from "components/gantt-chart";
import { RootStore } from "store/root.store"; // import { RootStore } from "store/root.store";
export interface IModuleStore { export interface IModuleStore {
// states // states
@ -55,14 +55,14 @@ export interface IModuleStore {
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
moduleId: string, moduleId: string,
data: Partial<ILinkDetails> data: ModuleLink
) => Promise<ILinkDetails>; ) => Promise<ILinkDetails>;
updateModuleLink: ( updateModuleLink: (
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
moduleId: string, moduleId: string,
linkId: string, linkId: string,
data: Partial<ILinkDetails> data: ModuleLink
) => Promise<ILinkDetails>; ) => Promise<ILinkDetails>;
deleteModuleLink: (workspaceSlug: string, projectId: string, moduleId: string, linkId: string) => Promise<void>; deleteModuleLink: (workspaceSlug: string, projectId: string, moduleId: string, linkId: string) => Promise<void>;
@ -310,12 +310,7 @@ export class ModuleStore implements IModuleStore {
} }
}; };
createModuleLink = async ( createModuleLink = async (workspaceSlug: string, projectId: string, moduleId: string, data: ModuleLink) => {
workspaceSlug: string,
projectId: string,
moduleId: string,
data: Partial<ILinkDetails>
) => {
try { try {
const response = await this.moduleService.createModuleLink(workspaceSlug, projectId, moduleId, data); const response = await this.moduleService.createModuleLink(workspaceSlug, projectId, moduleId, data);
@ -355,7 +350,7 @@ export class ModuleStore implements IModuleStore {
projectId: string, projectId: string,
moduleId: string, moduleId: string,
linkId: string, linkId: string,
data: Partial<ILinkDetails> data: ModuleLink
) => { ) => {
try { try {
const response = await this.moduleService.updateModuleLink(workspaceSlug, projectId, moduleId, linkId, data); const response = await this.moduleService.updateModuleLink(workspaceSlug, projectId, moduleId, linkId, data);