fix: sub-issue properties not rendering and other sub-issue bugs (#3020)

* fix: sub-issue properties not rendering

* fix: delete sub-issue

* fix: delete issue modal on command k and the issue details page
This commit is contained in:
Aaryan Khandelwal 2023-12-07 17:33:38 +05:30 committed by GitHub
parent c6e2effa65
commit 5fdd2ac366
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 107 deletions

View File

@ -34,6 +34,7 @@ export const CommandPalette: FC = observer(() => {
theme: { toggleSidebar }, theme: { toggleSidebar },
user: { currentUser }, user: { currentUser },
trackEvent: { setTrackElement }, trackEvent: { setTrackElement },
projectIssues: { removeIssue },
} = useMobxStore(); } = useMobxStore();
const { const {
toggleCommandPaletteModal, toggleCommandPaletteModal,
@ -218,11 +219,15 @@ export const CommandPalette: FC = observer(() => {
currentStore={createIssueStoreType} currentStore={createIssueStoreType}
/> />
{issueId && issueDetails && ( {workspaceSlug && projectId && issueId && issueDetails && (
<DeleteIssueModal <DeleteIssueModal
handleClose={() => toggleDeleteIssueModal(false)} handleClose={() => toggleDeleteIssueModal(false)}
isOpen={isDeleteIssueModalOpen} isOpen={isDeleteIssueModalOpen}
data={issueDetails} data={issueDetails}
onSubmit={async () => {
await removeIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString());
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
}}
/> />
)} )}

View File

@ -57,12 +57,8 @@ export const UserImageUploadModal: React.FC<Props> = observer((props) => {
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
console.log("Submit triggered");
if (!image) return; if (!image) return;
console.log("Inside submit");
setIsImageUploading(true); setIsImageUploading(true);
const formData = new FormData(); const formData = new FormData();

View File

@ -29,7 +29,10 @@ export const DeleteIssueModal: React.FC<Props> = (props) => {
const handleIssueDelete = async () => { const handleIssueDelete = async () => {
setIsDeleteLoading(true); setIsDeleteLoading(true);
if (onSubmit) await onSubmit().finally(() => setIsDeleteLoading(false)); if (onSubmit)
await onSubmit()
.then(() => onClose())
.finally(() => setIsDeleteLoading(false));
}; };
return ( return (

View File

@ -61,8 +61,6 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = (props) => {
description_html: issue.description_html, description_html: issue.description_html,
}); });
console.log("in form", localIssueDescription);
useEffect(() => { useEffect(() => {
if (issue.id) { if (issue.id) {
setLocalIssueDescription({ id: issue.id, description_html: issue.description_html }); setLocalIssueDescription({ id: issue.id, description_html: issue.description_html });

View File

@ -81,11 +81,10 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<ILinkDetails | null>(null); const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<ILinkDetails | null>(null);
const { const {
user: userStore, user: { currentUser, currentProjectRole },
projectState: { states }, projectState: { states },
projectIssues: { removeIssue },
} = useMobxStore(); } = useMobxStore();
const user = userStore.currentUser;
const userRole = userStore.currentProjectRole;
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, issueId, inboxIssueId } = router.query; const { workspaceSlug, projectId, issueId, inboxIssueId } = router.query;
@ -102,7 +101,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
const handleCycleChange = useCallback( const handleCycleChange = useCallback(
(cycleId: string) => { (cycleId: string) => {
if (!workspaceSlug || !projectId || !issueDetail || !user) return; if (!workspaceSlug || !projectId || !issueDetail || !currentUser) return;
issueService issueService
.addIssueToCycle(workspaceSlug as string, projectId as string, cycleId, { .addIssueToCycle(workspaceSlug as string, projectId as string, cycleId, {
@ -112,12 +111,12 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
mutate(ISSUE_DETAILS(issueId as string)); mutate(ISSUE_DETAILS(issueId as string));
}); });
}, },
[workspaceSlug, projectId, issueId, issueDetail, user] [workspaceSlug, projectId, issueId, issueDetail, currentUser]
); );
const handleModuleChange = useCallback( const handleModuleChange = useCallback(
(moduleId: string) => { (moduleId: string) => {
if (!workspaceSlug || !projectId || !issueDetail || !user) return; if (!workspaceSlug || !projectId || !issueDetail || !currentUser) return;
moduleService moduleService
.addIssuesToModule(workspaceSlug as string, projectId as string, moduleId, { .addIssuesToModule(workspaceSlug as string, projectId as string, moduleId, {
@ -127,7 +126,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
mutate(ISSUE_DETAILS(issueId as string)); mutate(ISSUE_DETAILS(issueId as string));
}); });
}, },
[workspaceSlug, projectId, issueId, issueDetail, user] [workspaceSlug, projectId, issueId, issueDetail, currentUser]
); );
const handleCreateLink = async (formData: IIssueLink) => { const handleCreateLink = async (formData: IIssueLink) => {
@ -249,7 +248,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
setLinkModal(true); setLinkModal(true);
}; };
const isAllowed = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER; const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const currentIssueState = projectId const currentIssueState = projectId
? states[projectId.toString()]?.find((s) => s.id === issueDetail?.state) ? states[projectId.toString()]?.find((s) => s.id === issueDetail?.state)
@ -268,8 +267,16 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
createIssueLink={handleCreateLink} createIssueLink={handleCreateLink}
updateIssueLink={handleUpdateLink} updateIssueLink={handleUpdateLink}
/> />
{issueDetail && ( {workspaceSlug && projectId && issueDetail && (
<DeleteIssueModal handleClose={() => setDeleteIssueModal(false)} isOpen={deleteIssueModal} data={issueDetail} /> <DeleteIssueModal
handleClose={() => setDeleteIssueModal(false)}
isOpen={deleteIssueModal}
data={issueDetail}
onSubmit={async () => {
await removeIssue(workspaceSlug.toString(), projectId.toString(), issueDetail.id);
router.push(`/${workspaceSlug}/projects/${projectId}/issues`);
}}
/>
)} )}
<div className="h-full w-full flex flex-col divide-y-2 divide-custom-border-200 overflow-hidden"> <div className="h-full w-full flex flex-col divide-y-2 divide-custom-border-200 overflow-hidden">
<div className="flex items-center justify-between px-5 pb-3"> <div className="flex items-center justify-between px-5 pb-3">
@ -288,8 +295,8 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
</h4> </h4>
</div> </div>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
{issueDetail?.created_by !== user?.id && {issueDetail?.created_by !== currentUser?.id &&
!issueDetail?.assignees.includes(user?.id ?? "") && !issueDetail?.assignees.includes(currentUser?.id ?? "") &&
!router.pathname.includes("[archivedIssueId]") && !router.pathname.includes("[archivedIssueId]") &&
(fieldsToShow.includes("all") || fieldsToShow.includes("subscribe")) && ( (fieldsToShow.includes("all") || fieldsToShow.includes("subscribe")) && (
<Button <Button
@ -654,10 +661,10 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
handleDeleteLink={handleDeleteLink} handleDeleteLink={handleDeleteLink}
handleEditLink={handleEditLink} handleEditLink={handleEditLink}
userAuth={{ userAuth={{
isGuest: userRole === 5, isGuest: currentProjectRole === 5,
isViewer: userRole === 10, isViewer: currentProjectRole === 10,
isMember: userRole === 15, isMember: currentProjectRole === 15,
isOwner: userRole === 20, isOwner: currentProjectRole === 20,
}} }}
/> />
) : null} ) : null}

View File

@ -117,17 +117,16 @@ export const SubIssues: React.FC<ISubIssues> = ({
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
parentIssue={parentIssue} parentIssue={parentIssue}
issue={issue} issue={issue}
user={user}
editable={editable} editable={editable}
/> />
</div> </div>
<div className="flex-shrink-0 text-sm"> <div className="flex-shrink-0 text-sm">
<CustomMenu width="auto" ellipsis> <CustomMenu width="auto" placement="bottom-end" ellipsis>
{editable && ( {editable && (
<CustomMenu.MenuItem onClick={() => handleIssueCrudOperation("edit", parentIssue?.id, issue)}> <CustomMenu.MenuItem onClick={() => handleIssueCrudOperation("edit", parentIssue?.id, issue)}>
<div className="flex items-center justify-start gap-2"> <div className="flex items-center gap-2">
<Pencil width={14} strokeWidth={2} /> <Pencil className="h-3.5 w-3.5" strokeWidth={2} />
<span>Edit issue</span> <span>Edit issue</span>
</div> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
@ -135,8 +134,8 @@ export const SubIssues: React.FC<ISubIssues> = ({
{editable && ( {editable && (
<CustomMenu.MenuItem onClick={() => handleIssueCrudOperation("delete", parentIssue?.id, issue)}> <CustomMenu.MenuItem onClick={() => handleIssueCrudOperation("delete", parentIssue?.id, issue)}>
<div className="flex items-center justify-start gap-2"> <div className="flex items-center gap-2">
<Trash width={14} strokeWidth={2} /> <Trash className="h-3.5 w-3.5" strokeWidth={2} />
<span>Delete issue</span> <span>Delete issue</span>
</div> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
@ -145,8 +144,8 @@ export const SubIssues: React.FC<ISubIssues> = ({
<CustomMenu.MenuItem <CustomMenu.MenuItem
onClick={() => copyText(`${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`)} onClick={() => copyText(`${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`)}
> >
<div className="flex items-center justify-start gap-2"> <div className="flex items-center gap-2">
<LinkIcon width={14} strokeWidth={2} /> <LinkIcon className="h-3.5 w-3.5" strokeWidth={2} />
<span>Copy issue link</span> <span>Copy issue link</span>
</div> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>

View File

@ -1,15 +1,11 @@
import React from "react"; import React from "react";
import { observer } from "mobx-react-lite";
import { mutate } from "swr"; import { mutate } from "swr";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// services // services
import { IssueService } from "services/issue"; import { IssueService } from "services/issue";
// components // components
import { ViewDueDateSelect, ViewStartDateSelect } from "components/issues";
import { PrioritySelect } from "components/project"; import { PrioritySelect } from "components/project";
// types // types
import { IUser, IIssue, IState } from "types"; import { IIssue, IState } from "types";
// fetch-keys // fetch-keys
import { SUB_ISSUES } from "constants/fetch-keys"; import { SUB_ISSUES } from "constants/fetch-keys";
import { IssuePropertyAssignee, IssuePropertyState } from "../issue-layouts/properties"; import { IssuePropertyAssignee, IssuePropertyState } from "../issue-layouts/properties";
@ -18,19 +14,14 @@ export interface IIssueProperty {
workspaceSlug: string; workspaceSlug: string;
parentIssue: IIssue; parentIssue: IIssue;
issue: IIssue; issue: IIssue;
user: IUser | undefined;
editable: boolean; editable: boolean;
} }
// services // services
const issueService = new IssueService(); const issueService = new IssueService();
export const IssueProperty: React.FC<IIssueProperty> = observer((props) => { export const IssueProperty: React.FC<IIssueProperty> = (props) => {
const { workspaceSlug, parentIssue, issue, user, editable } = props; const { workspaceSlug, parentIssue, issue, editable } = props;
const { issueFilter: issueFilterStore } = useMobxStore();
const displayProperties = issueFilterStore.userDisplayProperties ?? {};
const handlePriorityChange = (data: any) => { const handlePriorityChange = (data: any) => {
partialUpdateIssue({ priority: data }); partialUpdateIssue({ priority: data });
@ -77,63 +68,30 @@ export const IssueProperty: React.FC<IIssueProperty> = observer((props) => {
}; };
return ( return (
<div className="relative flex items-center gap-1"> <div className="relative flex items-center gap-2">
{displayProperties.priority && ( <div className="flex-shrink-0">
<div className="flex-shrink-0"> <PrioritySelect value={issue.priority} onChange={handlePriorityChange} hideDropdownArrow disabled={!editable} />
<PrioritySelect </div>
value={issue.priority}
onChange={handlePriorityChange}
hideDropdownArrow
disabled={!editable}
/>
</div>
)}
{displayProperties.state && ( <div className="flex-shrink-0">
<div className="flex-shrink-0"> <IssuePropertyState
<IssuePropertyState projectId={issue?.project_detail?.id || null}
projectId={issue?.project_detail?.id || null} value={issue?.state || null}
value={issue?.state_detail || null} onChange={(data) => handleStateChange(data)}
onChange={(data) => handleStateChange(data)} disabled={false}
disabled={false} hideDropdownArrow
hideDropdownArrow />
/> </div>
</div>
)}
{displayProperties.start_date && issue.start_date && ( <div className="flex-shrink-0">
<div className="flex-shrink-0 w-[104px]"> <IssuePropertyAssignee
<ViewStartDateSelect projectId={issue?.project_detail?.id || null}
issue={issue} value={issue?.assignees || null}
onChange={(val) => partialUpdateIssue({ start_date: val })} hideDropdownArrow
disabled={!editable} onChange={(val) => handleAssigneeChange(val)}
/> disabled={false}
</div> />
)} </div>
{displayProperties.due_date && issue.target_date && (
<div className="flex-shrink-0 w-[104px]">
{user && (
<ViewDueDateSelect
issue={issue}
onChange={(val) => partialUpdateIssue({ target_date: val })}
disabled={!editable}
/>
)}
</div>
)}
{displayProperties.assignee && (
<div className="flex-shrink-0">
<IssuePropertyAssignee
projectId={issue?.project_detail?.id || null}
value={issue?.assignees || null}
hideDropdownArrow
onChange={(val) => handleAssigneeChange(val)}
disabled={false}
/>
</div>
)}
</div> </div>
); );
}); };

View File

@ -45,11 +45,10 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
const { parentIssue, user } = props; const { parentIssue, user } = props;
const { const {
user: userStore, user: { currentProjectRole },
issue: { updateIssueStructure }, issue: { updateIssueStructure },
projectIssues: { updateIssue }, projectIssues: { updateIssue, removeIssue },
} = useMobxStore(); } = useMobxStore();
const userRole = userStore.currentProjectRole;
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
@ -177,7 +176,7 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
[updateIssueStructure, projectId, updateIssue, user, workspaceSlug] [updateIssueStructure, projectId, updateIssue, user, workspaceSlug]
); );
const isEditable = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER; const isEditable = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const mutateSubIssues = (parentIssueId: string | null) => { const mutateSubIssues = (parentIssueId: string | null) => {
if (parentIssueId) mutate(SUB_ISSUES(parentIssueId)); if (parentIssueId) mutate(SUB_ISSUES(parentIssueId));
@ -261,7 +260,7 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
</> </>
} }
buttonClassName="whitespace-nowrap" buttonClassName="whitespace-nowrap"
// position="left" placement="bottom-end"
noBorder noBorder
noChevron noChevron
> >
@ -297,7 +296,7 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
</> </>
} }
buttonClassName="whitespace-nowrap" buttonClassName="whitespace-nowrap"
// position="left" placement="bottom-end"
noBorder noBorder
noChevron noChevron
> >
@ -356,7 +355,8 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
</> </>
)} )}
{isEditable && {isEditable &&
issueCrudOperation?.delete?.toggle && workspaceSlug &&
projectId &&
issueCrudOperation?.delete?.issueId && issueCrudOperation?.delete?.issueId &&
issueCrudOperation?.delete?.issue && ( issueCrudOperation?.delete?.issue && (
<DeleteIssueModal <DeleteIssueModal
@ -366,6 +366,13 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
handleIssueCrudOperation("delete", null, null); handleIssueCrudOperation("delete", null, null);
}} }}
data={issueCrudOperation?.delete?.issue} data={issueCrudOperation?.delete?.issue}
onSubmit={async () => {
await removeIssue(
workspaceSlug.toString(),
projectId.toString(),
issueCrudOperation?.delete?.issue?.id ?? ""
);
}}
/> />
)} )}
</> </>