mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: bugs in quick-add and draft issues (#2269)
* fix: 'Last Drafted Issue' making sidebar look weird on collapsed * feat: scroll to the bottom when issue is created * fix: 'Add Issue' button overlapping issue card in spreadsheet view * fix: wrong placement of quick-add in calender layout * fix: spacing for issue card in spreadsheet view
This commit is contained in:
parent
6e0999c35a
commit
b317a14983
@ -183,7 +183,10 @@ export const CalendarView: React.FC<Props> = ({
|
|||||||
{calendarIssues ? (
|
{calendarIssues ? (
|
||||||
<div className="h-full overflow-y-auto">
|
<div className="h-full overflow-y-auto">
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
<div className="h-full rounded-lg p-8 text-custom-text-200">
|
<div
|
||||||
|
id={`calendar-view-${cycleId ?? moduleId ?? viewId}`}
|
||||||
|
className="h-full rounded-lg p-8 text-custom-text-200"
|
||||||
|
>
|
||||||
<CalendarHeader
|
<CalendarHeader
|
||||||
isMonthlyView={isMonthlyView}
|
isMonthlyView={isMonthlyView}
|
||||||
setIsMonthlyView={setIsMonthlyView}
|
setIsMonthlyView={setIsMonthlyView}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
// next
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
// react hook form
|
// react hook form
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
@ -16,21 +19,29 @@ type Props = {
|
|||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
onSuccess?: (data: IIssue) => Promise<void> | void;
|
onSuccess?: (data: IIssue) => Promise<void> | void;
|
||||||
prePopulatedData?: Partial<IIssue>;
|
prePopulatedData?: Partial<IIssue>;
|
||||||
|
dependencies: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const useCheckIfThereIsSpaceOnRight = (ref: React.RefObject<HTMLDivElement>) => {
|
const useCheckIfThereIsSpaceOnRight = (ref: React.RefObject<HTMLDivElement>, deps: any[]) => {
|
||||||
const [isThereSpaceOnRight, setIsThereSpaceOnRight] = useState(true);
|
const [isThereSpaceOnRight, setIsThereSpaceOnRight] = useState(true);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const { moduleId, cycleId, viewId } = router.query;
|
||||||
|
|
||||||
|
const container = document.getElementById(`calendar-view-${cycleId ?? moduleId ?? viewId}`);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!ref.current) return;
|
if (!ref.current) return;
|
||||||
|
|
||||||
const { right } = ref.current.getBoundingClientRect();
|
const { right } = ref.current.getBoundingClientRect();
|
||||||
|
|
||||||
const width = right + 250;
|
const width = right;
|
||||||
|
|
||||||
if (width > window.innerWidth) setIsThereSpaceOnRight(false);
|
const innerWidth = container?.getBoundingClientRect().width ?? window.innerWidth;
|
||||||
|
|
||||||
|
if (width > innerWidth) setIsThereSpaceOnRight(false);
|
||||||
else setIsThereSpaceOnRight(true);
|
else setIsThereSpaceOnRight(true);
|
||||||
}, [ref]);
|
}, [ref, deps, container]);
|
||||||
|
|
||||||
return isThereSpaceOnRight;
|
return isThereSpaceOnRight;
|
||||||
};
|
};
|
||||||
@ -63,11 +74,11 @@ const InlineInput = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CalendarInlineCreateIssueForm: React.FC<Props> = (props) => {
|
export const CalendarInlineCreateIssueForm: React.FC<Props> = (props) => {
|
||||||
const { isOpen } = props;
|
const { isOpen, dependencies } = props;
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const isSpaceOnRight = useCheckIfThereIsSpaceOnRight(ref);
|
const isSpaceOnRight = useCheckIfThereIsSpaceOnRight(ref, dependencies);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -83,6 +83,7 @@ export const SingleCalendarDate: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
<CalendarInlineCreateIssueForm
|
<CalendarInlineCreateIssueForm
|
||||||
isOpen={isCreateIssueFormOpen}
|
isOpen={isCreateIssueFormOpen}
|
||||||
|
dependencies={[showWeekEnds]}
|
||||||
handleClose={() => setIsCreateIssueFormOpen(false)}
|
handleClose={() => setIsCreateIssueFormOpen(false)}
|
||||||
prePopulatedData={{
|
prePopulatedData={{
|
||||||
target_date: date.date,
|
target_date: date.date,
|
||||||
|
@ -218,11 +218,11 @@ export const InlineCreateIssueFormWrapper: React.FC<Props> = (props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (isDraftIssues)
|
if (isDraftIssues)
|
||||||
mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(projectId.toString() ?? "", params));
|
await mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(projectId.toString() ?? "", params));
|
||||||
if (displayFilters.layout === "calendar") mutate(calendarFetchKey);
|
if (displayFilters.layout === "calendar") await mutate(calendarFetchKey);
|
||||||
if (displayFilters.layout === "gantt_chart") mutate(ganttFetchKey);
|
if (displayFilters.layout === "gantt_chart") await mutate(ganttFetchKey);
|
||||||
if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey);
|
if (displayFilters.layout === "spreadsheet") await mutate(spreadsheetFetchKey);
|
||||||
if (groupedIssues) mutateMyIssues();
|
if (groupedIssues) await mutateMyIssues();
|
||||||
|
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "success",
|
type: "success",
|
||||||
|
@ -68,7 +68,11 @@ export const SpreadsheetView: React.FC<Props> = ({
|
|||||||
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
workspaceSlug={workspaceSlug?.toString() ?? ""}
|
||||||
readOnly={disableUserActions}
|
readOnly={disableUserActions}
|
||||||
/>
|
/>
|
||||||
<div className="h-full rounded-lg text-custom-text-200 overflow-x-auto whitespace-nowrap bg-custom-background-100">
|
<div
|
||||||
|
className={`h-full rounded-lg text-custom-text-200 overflow-x-auto whitespace-nowrap bg-custom-background-100 ${
|
||||||
|
isInlineCreateIssueFormOpen ? "mb-24" : "mb-12"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<div className="sticky z-[2] top-0 border-b border-custom-border-200 bg-custom-background-90 w-full min-w-max">
|
<div className="sticky z-[2] top-0 border-b border-custom-border-200 bg-custom-background-90 w-full min-w-max">
|
||||||
<SpreadsheetColumns columnData={columnData} gridTemplateColumns={gridTemplateColumns} />
|
<SpreadsheetColumns columnData={columnData} gridTemplateColumns={gridTemplateColumns} />
|
||||||
</div>
|
</div>
|
||||||
@ -89,62 +93,66 @@ export const SpreadsheetView: React.FC<Props> = ({
|
|||||||
userAuth={userAuth}
|
userAuth={userAuth}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<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)}
|
|
||||||
prePopulatedData={{
|
|
||||||
...(cycleId && { cycle: cycleId.toString() }),
|
|
||||||
...(moduleId && { module: moduleId.toString() }),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{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 border-custom-border-200 w-full"
|
|
||||||
onClick={() => setIsInlineCreateIssueFormOpen(true)}
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-4 w-4" />
|
|
||||||
Add Issue
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
: !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 border-custom-border-200 w-full"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<PlusIcon className="h-4 w-4" />
|
|
||||||
Add Issue
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
position="left"
|
|
||||||
verticalPosition="top"
|
|
||||||
optionsClassName="left-5 !w-36"
|
|
||||||
noBorder
|
|
||||||
>
|
|
||||||
<CustomMenu.MenuItem onClick={() => setIsInlineCreateIssueFormOpen(true)}>
|
|
||||||
Create new
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
{openIssuesListModal && (
|
|
||||||
<CustomMenu.MenuItem onClick={openIssuesListModal}>
|
|
||||||
Add an existing issue
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
)}
|
|
||||||
</CustomMenu>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`absolute bottom-0 left-0 z-10 group hover:rounded-sm bg-custom-background-100 hover:bg-custom-background-80 border-b border-custom-border-200 w-full min-w-max ${
|
||||||
|
isInlineCreateIssueFormOpen ? "pb-2" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<ListInlineCreateIssueForm
|
||||||
|
isOpen={isInlineCreateIssueFormOpen}
|
||||||
|
handleClose={() => setIsInlineCreateIssueFormOpen(false)}
|
||||||
|
prePopulatedData={{
|
||||||
|
...(cycleId && { cycle: cycleId.toString() }),
|
||||||
|
...(moduleId && { module: moduleId.toString() }),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{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 border-custom-border-200 w-full"
|
||||||
|
onClick={() => setIsInlineCreateIssueFormOpen(true)}
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
Add Issue
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
: !disableUserActions &&
|
||||||
|
!isInlineCreateIssueFormOpen && (
|
||||||
|
<CustomMenu
|
||||||
|
className="sticky left-0 z-[1] !w-full"
|
||||||
|
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 border-custom-border-200 w-full"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
Add Issue
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
position="left"
|
||||||
|
verticalPosition="top"
|
||||||
|
optionsClassName="left-5 !w-36"
|
||||||
|
noBorder
|
||||||
|
>
|
||||||
|
<CustomMenu.MenuItem onClick={() => setIsInlineCreateIssueFormOpen(true)}>
|
||||||
|
Create new
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
{openIssuesListModal && (
|
||||||
|
<CustomMenu.MenuItem onClick={openIssuesListModal}>
|
||||||
|
Add an existing issue
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
)}
|
||||||
|
</CustomMenu>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -92,6 +92,7 @@ export const GanttSidebar: React.FC<Props> = (props) => {
|
|||||||
<StrictModeDroppable droppableId="gantt-sidebar">
|
<StrictModeDroppable droppableId="gantt-sidebar">
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<div
|
<div
|
||||||
|
id={`gantt-sidebar-${cycleId}`}
|
||||||
className="h-full overflow-y-auto pl-2.5"
|
className="h-full overflow-y-auto pl-2.5"
|
||||||
ref={droppableProvided.innerRef}
|
ref={droppableProvided.innerRef}
|
||||||
{...droppableProvided.droppableProps}
|
{...droppableProvided.droppableProps}
|
||||||
@ -162,6 +163,19 @@ export const GanttSidebar: React.FC<Props> = (props) => {
|
|||||||
<GanttInlineCreateIssueForm
|
<GanttInlineCreateIssueForm
|
||||||
isOpen={isCreateIssueFormOpen}
|
isOpen={isCreateIssueFormOpen}
|
||||||
handleClose={() => setIsCreateIssueFormOpen(false)}
|
handleClose={() => setIsCreateIssueFormOpen(false)}
|
||||||
|
onSuccess={() => {
|
||||||
|
const ganttSidebar = document.getElementById(`gantt-sidebar-${cycleId}`);
|
||||||
|
|
||||||
|
const timeoutId = setTimeout(() => {
|
||||||
|
if (ganttSidebar)
|
||||||
|
ganttSidebar.scrollBy({
|
||||||
|
top: ganttSidebar.scrollHeight,
|
||||||
|
left: 0,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
}, 10);
|
||||||
|
}}
|
||||||
prePopulatedData={{
|
prePopulatedData={{
|
||||||
start_date: new Date(Date.now()).toISOString().split("T")[0],
|
start_date: new Date(Date.now()).toISOString().split("T")[0],
|
||||||
target_date: new Date(Date.now() + 86400000).toISOString().split("T")[0],
|
target_date: new Date(Date.now() + 86400000).toISOString().split("T")[0],
|
||||||
|
@ -44,7 +44,9 @@ export const WorkspaceSidebarQuickAction = () => {
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="relative flex items-center gap-2 flex-grow rounded flex-shrink-0 py-1.5"
|
className={`relative flex items-center gap-2 flex-grow rounded flex-shrink-0 py-1.5 ${
|
||||||
|
store?.theme?.sidebarCollapsed ? "justify-center" : ""
|
||||||
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const e = new KeyboardEvent("keydown", { key: "c" });
|
const e = new KeyboardEvent("keydown", { key: "c" });
|
||||||
document.dispatchEvent(e);
|
document.dispatchEvent(e);
|
||||||
@ -61,11 +63,17 @@ export const WorkspaceSidebarQuickAction = () => {
|
|||||||
|
|
||||||
{storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && (
|
{storedValue && Object.keys(JSON.parse(storedValue)).length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="h-8 w-0.5 bg-custom-sidebar-background-80" />
|
<div
|
||||||
|
className={`h-8 w-0.5 bg-custom-sidebar-background-80 ${
|
||||||
|
store?.theme?.sidebarCollapsed ? "hidden" : "block"
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex items-center justify-center rounded flex-shrink-0 py-1.5 ml-1.5"
|
className={`flex items-center justify-center rounded flex-shrink-0 py-1.5 ml-1.5 ${
|
||||||
|
store?.theme?.sidebarCollapsed ? "hidden" : "block"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
size={16}
|
size={16}
|
||||||
@ -73,7 +81,11 @@ export const WorkspaceSidebarQuickAction = () => {
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="absolute w-full h-10 pt-2 top-full left-0 opacity-0 group-hover:opacity-100 mt-0 pointer-events-none group-hover:pointer-events-auto">
|
<div
|
||||||
|
className={`fixed h-10 pt-2 w-[203px] left-4 opacity-0 group-hover:opacity-100 mt-0 pointer-events-none group-hover:pointer-events-auto ${
|
||||||
|
store?.theme?.sidebarCollapsed ? "top-[5.5rem]" : "top-24"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsDraftIssueModalOpen(true)}
|
onClick={() => setIsDraftIssueModalOpen(true)}
|
||||||
|
@ -189,7 +189,7 @@ const SingleCycle: React.FC = () => {
|
|||||||
{cycleStatus === "completed" && (
|
{cycleStatus === "completed" && (
|
||||||
<TransferIssues handleClick={() => setTransferIssuesModal(true)} />
|
<TransferIssues handleClick={() => setTransferIssuesModal(true)} />
|
||||||
)}
|
)}
|
||||||
<div className="relative overflow-y-auto">
|
<div className="relative overflow-y-auto w-full h-full">
|
||||||
<IssuesView
|
<IssuesView
|
||||||
openIssuesListModal={openIssuesListModal}
|
openIssuesListModal={openIssuesListModal}
|
||||||
disableUserActions={cycleStatus === "completed" ?? false}
|
disableUserActions={cycleStatus === "completed" ?? false}
|
||||||
|
Loading…
Reference in New Issue
Block a user