mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: peek overview bugs (#2043)
* fix: side peek modal shaking * refactor: peek overview layout * fix: date selector, activity mutation * fix: delete issue handler * fix: assignees mutation
This commit is contained in:
parent
c6d9ace6a2
commit
4ba3ef5c24
@ -33,10 +33,17 @@ type Props = {
|
|||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
data: IIssue | null;
|
data: IIssue | null;
|
||||||
|
onSubmit?: () => Promise<void>;
|
||||||
user: ICurrentUserResponse | undefined;
|
user: ICurrentUserResponse | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data, user }) => {
|
export const DeleteIssueModal: React.FC<Props> = ({
|
||||||
|
isOpen,
|
||||||
|
handleClose,
|
||||||
|
data,
|
||||||
|
onSubmit,
|
||||||
|
user,
|
||||||
|
}) => {
|
||||||
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
const [isDeleteLoading, setIsDeleteLoading] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -116,6 +123,8 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data, u
|
|||||||
else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(data.project, params));
|
else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(data.project, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (onSubmit) onSubmit();
|
||||||
|
|
||||||
handleClose();
|
handleClose();
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
title: "Success",
|
title: "Success",
|
||||||
|
@ -103,7 +103,7 @@ export const PeekOverviewIssueProperties: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="w-3/4">
|
<div className="w-3/4">
|
||||||
<SidebarAssigneeSelect
|
<SidebarAssigneeSelect
|
||||||
value={issue.assignees_list}
|
value={issue.assignees}
|
||||||
onChange={(val: string[]) => handleUpdateIssue({ assignees_list: val })}
|
onChange={(val: string[]) => handleUpdateIssue({ assignees_list: val })}
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
/>
|
/>
|
||||||
@ -128,23 +128,18 @@ export const PeekOverviewIssueProperties: React.FC<Props> = ({
|
|||||||
<span className="flex-grow truncate">Start date</span>
|
<span className="flex-grow truncate">Start date</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{issue.start_date ? (
|
<CustomDatePicker
|
||||||
<CustomDatePicker
|
placeholder="Select start date"
|
||||||
placeholder="Start date"
|
value={issue.start_date}
|
||||||
value={issue.start_date}
|
onChange={(val) =>
|
||||||
onChange={(val) =>
|
handleUpdateIssue({
|
||||||
handleUpdateIssue({
|
start_date: val,
|
||||||
start_date: val,
|
})
|
||||||
})
|
}
|
||||||
}
|
className="bg-custom-background-80 border-none"
|
||||||
className="bg-custom-background-100"
|
maxDate={maxDate ?? undefined}
|
||||||
wrapperClassName="w-full"
|
disabled={readOnly}
|
||||||
maxDate={maxDate ?? undefined}
|
/>
|
||||||
disabled={readOnly}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span className="text-custom-text-200">Empty</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm">
|
<div className="flex items-center gap-2 text-sm">
|
||||||
@ -153,23 +148,18 @@ export const PeekOverviewIssueProperties: React.FC<Props> = ({
|
|||||||
<span className="flex-grow truncate">Due date</span>
|
<span className="flex-grow truncate">Due date</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{issue.target_date ? (
|
<CustomDatePicker
|
||||||
<CustomDatePicker
|
placeholder="Select due date"
|
||||||
placeholder="Due date"
|
value={issue.target_date}
|
||||||
value={issue.target_date}
|
onChange={(val) =>
|
||||||
onChange={(val) =>
|
handleUpdateIssue({
|
||||||
handleUpdateIssue({
|
target_date: val,
|
||||||
target_date: val,
|
})
|
||||||
})
|
}
|
||||||
}
|
className="bg-custom-background-80 border-none"
|
||||||
className="bg-custom-background-100"
|
minDate={minDate ?? undefined}
|
||||||
wrapperClassName="w-full"
|
disabled={readOnly}
|
||||||
minDate={minDate ?? undefined}
|
/>
|
||||||
disabled={readOnly}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<span className="text-custom-text-200">Empty</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* <div className="flex items-center gap-2 text-sm">
|
{/* <div className="flex items-center gap-2 text-sm">
|
||||||
|
@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { mutate } from "swr";
|
||||||
// mobx
|
// mobx
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
@ -10,9 +11,11 @@ import { Dialog, Transition } from "@headlessui/react";
|
|||||||
// hooks
|
// hooks
|
||||||
import useUser from "hooks/use-user";
|
import useUser from "hooks/use-user";
|
||||||
// components
|
// components
|
||||||
import { FullScreenPeekView, SidePeekView } from "components/issues";
|
import { DeleteIssueModal, FullScreenPeekView, SidePeekView } from "components/issues";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
|
// fetch-keys
|
||||||
|
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
handleMutation: () => void;
|
handleMutation: () => void;
|
||||||
@ -28,6 +31,7 @@ export const IssuePeekOverview: React.FC<Props> = observer(
|
|||||||
const [isSidePeekOpen, setIsSidePeekOpen] = useState(false);
|
const [isSidePeekOpen, setIsSidePeekOpen] = useState(false);
|
||||||
const [isModalPeekOpen, setIsModalPeekOpen] = useState(false);
|
const [isModalPeekOpen, setIsModalPeekOpen] = useState(false);
|
||||||
const [peekOverviewMode, setPeekOverviewMode] = useState<TPeekOverviewModes>("side");
|
const [peekOverviewMode, setPeekOverviewMode] = useState<TPeekOverviewModes>("side");
|
||||||
|
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { peekIssue } = router.query;
|
const { peekIssue } = router.query;
|
||||||
@ -53,6 +57,7 @@ export const IssuePeekOverview: React.FC<Props> = observer(
|
|||||||
if (!issue || !user) return;
|
if (!issue || !user) return;
|
||||||
|
|
||||||
await updateIssue(workspaceSlug, projectId, issue.id, formData, user);
|
await updateIssue(workspaceSlug, projectId, issue.id, formData, user);
|
||||||
|
mutate(PROJECT_ISSUES_ACTIVITY(issue.id));
|
||||||
handleMutation();
|
handleMutation();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,7 +86,6 @@ export const IssuePeekOverview: React.FC<Props> = observer(
|
|||||||
setIsSidePeekOpen(false);
|
setIsSidePeekOpen(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("Triggered");
|
|
||||||
setIsSidePeekOpen(false);
|
setIsSidePeekOpen(false);
|
||||||
setIsModalPeekOpen(false);
|
setIsModalPeekOpen(false);
|
||||||
}
|
}
|
||||||
@ -89,33 +93,38 @@ export const IssuePeekOverview: React.FC<Props> = observer(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<DeleteIssueModal
|
||||||
|
isOpen={deleteIssueModal}
|
||||||
|
handleClose={() => setDeleteIssueModal(false)}
|
||||||
|
data={issue ? { ...issue } : null}
|
||||||
|
onSubmit={handleDeleteIssue}
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
<Transition.Root appear show={isSidePeekOpen} as={React.Fragment}>
|
<Transition.Root appear show={isSidePeekOpen} as={React.Fragment}>
|
||||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||||
<div className="fixed inset-0 z-20 overflow-y-auto">
|
<div className="fixed inset-0 z-20 h-full w-full overflow-y-auto">
|
||||||
<div className="relative h-full w-full">
|
<Transition.Child
|
||||||
<Transition.Child
|
as={React.Fragment}
|
||||||
as={React.Fragment}
|
enter="transition-transform duration-300"
|
||||||
enter="transition-transform duration-300"
|
enterFrom="translate-x-full"
|
||||||
enterFrom="translate-x-full"
|
enterTo="translate-x-0"
|
||||||
enterTo="translate-x-0"
|
leave="transition-transform duration-200"
|
||||||
leave="transition-transform duration-200"
|
leaveFrom="translate-x-0"
|
||||||
leaveFrom="translate-x-0"
|
leaveTo="translate-x-full"
|
||||||
leaveTo="translate-x-full"
|
>
|
||||||
>
|
<Dialog.Panel className="fixed z-20 bg-custom-background-100 top-0 right-0 h-full w-1/2 shadow-custom-shadow-md">
|
||||||
<Dialog.Panel className="absolute z-20 bg-custom-background-100 top-0 right-0 h-full w-1/2 shadow-custom-shadow-md">
|
<SidePeekView
|
||||||
<SidePeekView
|
handleClose={handleClose}
|
||||||
handleClose={handleClose}
|
handleDeleteIssue={() => setDeleteIssueModal(true)}
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleUpdateIssue={handleUpdateIssue}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
issue={issue}
|
||||||
issue={issue}
|
mode={peekOverviewMode}
|
||||||
mode={peekOverviewMode}
|
readOnly={readOnly}
|
||||||
readOnly={readOnly}
|
setMode={(mode) => setPeekOverviewMode(mode)}
|
||||||
setMode={(mode) => setPeekOverviewMode(mode)}
|
workspaceSlug={workspaceSlug}
|
||||||
workspaceSlug={workspaceSlug}
|
/>
|
||||||
/>
|
</Dialog.Panel>
|
||||||
</Dialog.Panel>
|
</Transition.Child>
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</Transition.Root>
|
</Transition.Root>
|
||||||
@ -132,49 +141,47 @@ export const IssuePeekOverview: React.FC<Props> = observer(
|
|||||||
>
|
>
|
||||||
<div className="fixed inset-0 bg-custom-backdrop bg-opacity-50 transition-opacity" />
|
<div className="fixed inset-0 bg-custom-backdrop bg-opacity-50 transition-opacity" />
|
||||||
</Transition.Child>
|
</Transition.Child>
|
||||||
<div className="fixed inset-0 z-20 overflow-y-auto">
|
<div className="fixed inset-0 z-20 h-full w-full overflow-y-auto">
|
||||||
<div className="relative h-full w-full">
|
<Transition.Child
|
||||||
<Transition.Child
|
as={React.Fragment}
|
||||||
as={React.Fragment}
|
enter="ease-out duration-300"
|
||||||
enter="ease-out duration-300"
|
enterFrom="opacity-0"
|
||||||
enterFrom="opacity-0"
|
enterTo="opacity-100"
|
||||||
enterTo="opacity-100"
|
leave="ease-in duration-200"
|
||||||
leave="ease-in duration-200"
|
leaveFrom="opacity-100"
|
||||||
leaveFrom="opacity-100"
|
leaveTo="opacity-0"
|
||||||
leaveTo="opacity-0"
|
>
|
||||||
|
<Dialog.Panel
|
||||||
|
className={`fixed z-20 bg-custom-background-100 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg shadow-custom-shadow-xl transition-all duration-300 ${
|
||||||
|
peekOverviewMode === "modal" ? "h-[70%] w-3/5" : "h-[95%] w-[95%]"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<Dialog.Panel
|
{peekOverviewMode === "modal" && (
|
||||||
className={`absolute z-20 bg-custom-background-100 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg shadow-custom-shadow-xl transition-all duration-300 ${
|
<SidePeekView
|
||||||
peekOverviewMode === "modal" ? "h-[70%] w-3/5" : "h-[95%] w-[95%]"
|
handleClose={handleClose}
|
||||||
}`}
|
handleDeleteIssue={() => setDeleteIssueModal(true)}
|
||||||
>
|
handleUpdateIssue={handleUpdateIssue}
|
||||||
{peekOverviewMode === "modal" && (
|
issue={issue}
|
||||||
<SidePeekView
|
mode={peekOverviewMode}
|
||||||
handleClose={handleClose}
|
readOnly={readOnly}
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
setMode={(mode) => setPeekOverviewMode(mode)}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
workspaceSlug={workspaceSlug}
|
||||||
issue={issue}
|
/>
|
||||||
mode={peekOverviewMode}
|
)}
|
||||||
readOnly={readOnly}
|
{peekOverviewMode === "full" && (
|
||||||
setMode={(mode) => setPeekOverviewMode(mode)}
|
<FullScreenPeekView
|
||||||
workspaceSlug={workspaceSlug}
|
handleClose={handleClose}
|
||||||
/>
|
handleDeleteIssue={() => setDeleteIssueModal(true)}
|
||||||
)}
|
handleUpdateIssue={handleUpdateIssue}
|
||||||
{peekOverviewMode === "full" && (
|
issue={issue}
|
||||||
<FullScreenPeekView
|
mode={peekOverviewMode}
|
||||||
handleClose={handleClose}
|
readOnly={readOnly}
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
setMode={(mode) => setPeekOverviewMode(mode)}
|
||||||
handleUpdateIssue={handleUpdateIssue}
|
workspaceSlug={workspaceSlug}
|
||||||
issue={issue}
|
/>
|
||||||
mode={peekOverviewMode}
|
)}
|
||||||
readOnly={readOnly}
|
</Dialog.Panel>
|
||||||
setMode={(mode) => setPeekOverviewMode(mode)}
|
</Transition.Child>
|
||||||
workspaceSlug={workspaceSlug}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</Transition.Root>
|
</Transition.Root>
|
||||||
|
@ -51,7 +51,10 @@ export const SidebarAssigneeSelect: React.FC<Props> = ({ value, onChange, disabl
|
|||||||
<span className="text-custom-text-100 text-xs">{value.length} Assignees</span>
|
<span className="text-custom-text-100 text-xs">{value.length} Assignees</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<button type="button" className="bg-custom-background-80 px-2.5 py-0.5 text-xs rounded">
|
<button
|
||||||
|
type="button"
|
||||||
|
className="bg-custom-background-80 px-2.5 py-0.5 text-xs rounded text-custom-text-200"
|
||||||
|
>
|
||||||
No assignees
|
No assignees
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
@ -27,7 +27,7 @@ export const SidebarPrioritySelect: React.FC<Props> = ({ value, onChange, disabl
|
|||||||
? "border-yellow-500/20 bg-yellow-500/20 text-yellow-500"
|
? "border-yellow-500/20 bg-yellow-500/20 text-yellow-500"
|
||||||
: value === "low"
|
: value === "low"
|
||||||
? "border-green-500/20 bg-green-500/20 text-green-500"
|
? "border-green-500/20 bg-green-500/20 text-green-500"
|
||||||
: "bg-custom-background-80 border-custom-border-200"
|
: "bg-custom-background-80 border-custom-border-200 text-custom-text-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="grid place-items-center -my-1">
|
<span className="grid place-items-center -my-1">
|
||||||
|
@ -41,7 +41,7 @@ const CustomSelect = ({
|
|||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{customButton ? (
|
{customButton ? (
|
||||||
<Listbox.Button as="div">{customButton}</Listbox.Button>
|
<Listbox.Button as={React.Fragment}>{customButton}</Listbox.Button>
|
||||||
) : (
|
) : (
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -116,11 +116,12 @@ class IssuesStore {
|
|||||||
const originalIssue = { ...this.issues[issueId] };
|
const originalIssue = { ...this.issues[issueId] };
|
||||||
|
|
||||||
// immediately update the issue in the store
|
// immediately update the issue in the store
|
||||||
const updatedIssue = { ...originalIssue, ...issueForm };
|
const updatedIssue = { ...this.issues[issueId], ...issueForm };
|
||||||
|
if (updatedIssue.assignees_list) updatedIssue.assignees = updatedIssue.assignees_list;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issues[issueId] = updatedIssue;
|
this.issues[issueId] = { ...updatedIssue };
|
||||||
});
|
});
|
||||||
|
|
||||||
// make a patch request to update the issue
|
// make a patch request to update the issue
|
||||||
|
Loading…
Reference in New Issue
Block a user