mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: quick-add placement in spreadsheet and gantt (#2259)
* feat: sticking quick-add at the bottom of the screen fix: opening create issue modal instead of quick-add in draft-issues, my-issue and profile page * fix: build error due to dynamic import
This commit is contained in:
parent
de7a672b79
commit
5e8d523ed4
@ -63,6 +63,10 @@ export const SingleBoard: React.FC<Props> = (props) => {
|
||||
const router = useRouter();
|
||||
const { cycleId, moduleId } = router.query;
|
||||
|
||||
const isMyIssuesPage = router.pathname.split("/")[3] === "my-issues";
|
||||
const isProfileIssuesPage = router.pathname.split("/")[2] === "profile";
|
||||
const isDraftIssuesPage = router.pathname.split("/")[4] === "draft-issues";
|
||||
|
||||
const type = cycleId ? "cycle" : moduleId ? "module" : "issue";
|
||||
|
||||
// Check if it has at least 4 tickets since it is enough to accommodate the Calendar height
|
||||
@ -214,7 +218,11 @@ export const SingleBoard: React.FC<Props> = (props) => {
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 font-medium text-custom-primary outline-none p-1"
|
||||
onClick={() => onCreateClick()}
|
||||
onClick={() => {
|
||||
if (isDraftIssuesPage || isMyIssuesPage || isProfileIssuesPage) {
|
||||
addIssueToGroup();
|
||||
} else onCreateClick();
|
||||
}}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add Issue
|
||||
|
@ -154,15 +154,6 @@ export const InlineCreateIssueFormWrapper: React.FC<Props> = (props) => {
|
||||
if (!isOpen) reset({ ...defaultValues });
|
||||
}, [isOpen, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSubmitting)
|
||||
setToastAlert({
|
||||
type: "info",
|
||||
title: "Creating issue...",
|
||||
message: "Please wait while we create your issue.",
|
||||
});
|
||||
}, [isSubmitting, setToastAlert]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!errors) return;
|
||||
|
||||
|
@ -46,10 +46,17 @@ const InlineInput = () => {
|
||||
};
|
||||
|
||||
export const ListInlineCreateIssueForm: React.FC<Props> = (props) => (
|
||||
<>
|
||||
<InlineCreateIssueFormWrapper
|
||||
className="flex py-3 px-4 items-center gap-x-5 bg-custom-background-100 shadow-custom-shadow-md"
|
||||
{...props}
|
||||
>
|
||||
<InlineInput />
|
||||
</InlineCreateIssueFormWrapper>
|
||||
{props.isOpen && (
|
||||
<p className="text-xs ml-3 mt-3 italic text-custom-text-200">
|
||||
Press {"'"}Enter{"'"} to add another issue
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@ -62,6 +62,7 @@ export const SingleList: React.FC<Props> = (props) => {
|
||||
openIssuesListModal,
|
||||
handleDraftIssueAction,
|
||||
handleMyIssueOpen,
|
||||
addIssueToGroup,
|
||||
removeIssue,
|
||||
disableUserActions,
|
||||
disableAddIssueOption = false,
|
||||
@ -75,6 +76,10 @@ export const SingleList: React.FC<Props> = (props) => {
|
||||
|
||||
const [isCreateIssueFormOpen, setIsCreateIssueFormOpen] = useState(false);
|
||||
|
||||
const isMyIssuesPage = router.pathname.split("/")[3] === "my-issues";
|
||||
const isProfileIssuesPage = router.pathname.split("/")[2] === "profile";
|
||||
const isDraftIssuesPage = router.pathname.split("/")[4] === "draft-issues";
|
||||
|
||||
const isArchivedIssues = router.pathname.includes("archived-issues");
|
||||
|
||||
const type = cycleId ? "cycle" : moduleId ? "module" : "issue";
|
||||
@ -295,7 +300,7 @@ export const SingleList: React.FC<Props> = (props) => {
|
||||
)}
|
||||
|
||||
<ListInlineCreateIssueForm
|
||||
isOpen={isCreateIssueFormOpen}
|
||||
isOpen={isCreateIssueFormOpen && !disableAddIssueOption}
|
||||
handleClose={() => setIsCreateIssueFormOpen(false)}
|
||||
prePopulatedData={{
|
||||
...(cycleId && { cycle: cycleId.toString() }),
|
||||
@ -304,11 +309,15 @@ export const SingleList: React.FC<Props> = (props) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
{!isCreateIssueFormOpen && (
|
||||
{!disableAddIssueOption && !isCreateIssueFormOpen && (
|
||||
<div className="w-full bg-custom-background-100 px-6 py-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsCreateIssueFormOpen(true)}
|
||||
onClick={() => {
|
||||
if (isDraftIssuesPage || isMyIssuesPage || isProfileIssuesPage) {
|
||||
addIssueToGroup();
|
||||
} else setIsCreateIssueFormOpen(true);
|
||||
}}
|
||||
className="flex items-center gap-x-[6px] text-custom-primary-100 px-2 py-1 rounded-md"
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
|
@ -90,6 +90,7 @@ export const SpreadsheetView: React.FC<Props> = ({
|
||||
/>
|
||||
))}
|
||||
|
||||
<div className="absolute bottom-0 left-0 z-10 group pb-2 hover:rounded-sm bg-custom-background-100 hover:bg-custom-background-80 border-b border-custom-border-200 w-full min-w-max">
|
||||
<ListInlineCreateIssueForm
|
||||
isOpen={isInlineCreateIssueFormOpen}
|
||||
handleClose={() => setIsInlineCreateIssueFormOpen(false)}
|
||||
@ -99,27 +100,24 @@ export const SpreadsheetView: React.FC<Props> = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="relative group grid auto-rows-[minmax(44px,1fr)] hover:rounded-sm hover:bg-custom-background-80 border-b border-custom-border-200 w-full min-w-max"
|
||||
style={{ gridTemplateColumns }}
|
||||
>
|
||||
{!isInlineCreateIssueFormOpen && (
|
||||
<>
|
||||
{type === "issue" ? (
|
||||
{type === "issue"
|
||||
? !disableUserActions &&
|
||||
!isInlineCreateIssueFormOpen && (
|
||||
<button
|
||||
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-200 w-full"
|
||||
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 border-custom-border-200 w-full"
|
||||
onClick={() => setIsInlineCreateIssueFormOpen(true)}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add Issue
|
||||
</button>
|
||||
) : (
|
||||
!disableUserActions && (
|
||||
)
|
||||
: !disableUserActions &&
|
||||
!isInlineCreateIssueFormOpen && (
|
||||
<CustomMenu
|
||||
className="sticky left-0 z-[1]"
|
||||
customButton={
|
||||
<button
|
||||
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 bg-custom-background-100 group-hover:text-custom-text-100 group-hover:bg-custom-background-80 border-custom-border-200 w-full"
|
||||
className="flex gap-1.5 items-center pl-7 py-2.5 text-sm sticky left-0 z-[1] text-custom-text-200 border-custom-border-200 w-full"
|
||||
type="button"
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
@ -127,6 +125,7 @@ export const SpreadsheetView: React.FC<Props> = ({
|
||||
</button>
|
||||
}
|
||||
position="left"
|
||||
verticalPosition="top"
|
||||
optionsClassName="left-5 !w-36"
|
||||
noBorder
|
||||
>
|
||||
@ -139,9 +138,6 @@ export const SpreadsheetView: React.FC<Props> = ({
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -155,7 +155,10 @@ export const GanttSidebar: React.FC<Props> = (props) => {
|
||||
)}
|
||||
{droppableProvided.placeholder}
|
||||
</>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</StrictModeDroppable>
|
||||
<div className="pl-2.5">
|
||||
<GanttInlineCreateIssueForm
|
||||
isOpen={isCreateIssueFormOpen}
|
||||
handleClose={() => setIsCreateIssueFormOpen(false)}
|
||||
@ -178,8 +181,6 @@ export const GanttSidebar: React.FC<Props> = (props) => {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</StrictModeDroppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
};
|
||||
|
@ -227,7 +227,8 @@ export const ProfileIssuesView = () => {
|
||||
router.pathname.includes("my-issues")) ??
|
||||
false;
|
||||
|
||||
const disableAddIssueOption = isSubscribedIssuesRoute || isMySubscribedIssues;
|
||||
const disableAddIssueOption =
|
||||
isSubscribedIssuesRoute || isMySubscribedIssues || user?.id !== userId;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import dynamic from "next/dynamic";
|
||||
// hooks
|
||||
import useTheme from "hooks/use-theme";
|
||||
// components
|
||||
@ -5,8 +6,18 @@ import {
|
||||
WorkspaceHelpSection,
|
||||
WorkspaceSidebarDropdown,
|
||||
WorkspaceSidebarMenu,
|
||||
WorkspaceSidebarQuickAction,
|
||||
} from "components/workspace";
|
||||
|
||||
const WorkspaceSidebarQuickAction = dynamic<{}>(
|
||||
() =>
|
||||
import("components/workspace/sidebar-quick-action").then(
|
||||
(mod) => mod.WorkspaceSidebarQuickAction
|
||||
),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
import { ProjectSidebarList } from "components/project";
|
||||
import { PublishProjectModal } from "components/project/publish-project/modal";
|
||||
import { ConfirmProjectLeaveModal } from "components/project/confirm-project-leave-modal";
|
||||
|
@ -189,11 +189,13 @@ const SingleCycle: React.FC = () => {
|
||||
{cycleStatus === "completed" && (
|
||||
<TransferIssues handleClick={() => setTransferIssuesModal(true)} />
|
||||
)}
|
||||
<div className="relative overflow-y-auto">
|
||||
<IssuesView
|
||||
openIssuesListModal={openIssuesListModal}
|
||||
disableUserActions={cycleStatus === "completed" ?? false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<CycleDetailsSidebar
|
||||
cycleStatus={cycleStatus}
|
||||
cycle={cycleDetails}
|
||||
|
@ -181,9 +181,9 @@ const SingleModule: React.FC = () => {
|
||||
onClose={() => setAnalyticsModal(false)}
|
||||
/>
|
||||
<div
|
||||
className={`h-full flex flex-col ${moduleSidebar ? "mr-[24rem]" : ""} ${
|
||||
analyticsModal ? "mr-[50%]" : ""
|
||||
} duration-300`}
|
||||
className={`relative overflow-y-auto h-full flex flex-col ${
|
||||
moduleSidebar ? "mr-[24rem]" : ""
|
||||
} ${analyticsModal ? "mr-[50%]" : ""} duration-300`}
|
||||
>
|
||||
<IssuesView openIssuesListModal={openIssuesListModal} />
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user