forked from github/plane
fix: peekoverview (#2603)
* fix: peekoverview mutation fix * fix: peekoverview mutation fix * fix: sub-issue peekoverview
This commit is contained in:
parent
4512651f8b
commit
0072160891
@ -85,8 +85,8 @@ export const CyclesListGanttChartView: FC<Props> = ({ cycles, mutateCycles }) =>
|
||||
loaderTitle="Cycles"
|
||||
blocks={cycles ? blockFormat(cycles) : null}
|
||||
blockUpdateHandler={(block, payload) => handleCycleUpdate(block, payload)}
|
||||
SidebarBlockRender={CycleGanttSidebarBlock}
|
||||
BlockRender={CycleGanttBlock}
|
||||
blockToRender={(data: ICycle) => <CycleGanttBlock data={data} />}
|
||||
sidebarBlockToRender={(data: ICycle) => <CycleGanttSidebarBlock data={data} />}
|
||||
enableBlockLeftResize={false}
|
||||
enableBlockRightResize={false}
|
||||
enableBlockMove={false}
|
||||
|
@ -11,7 +11,7 @@ import { IBlockUpdateData, IGanttBlock } from "../types";
|
||||
export const GanttChartBlocks: FC<{
|
||||
itemsContainerWidth: number;
|
||||
blocks: IGanttBlock[] | null;
|
||||
BlockRender: React.FC<any>;
|
||||
blockToRender: (data: any) => React.ReactNode;
|
||||
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void;
|
||||
enableBlockLeftResize: boolean;
|
||||
enableBlockRightResize: boolean;
|
||||
@ -19,7 +19,7 @@ export const GanttChartBlocks: FC<{
|
||||
}> = ({
|
||||
itemsContainerWidth,
|
||||
blocks,
|
||||
BlockRender,
|
||||
blockToRender,
|
||||
blockUpdateHandler,
|
||||
enableBlockLeftResize,
|
||||
enableBlockRightResize,
|
||||
@ -49,11 +49,9 @@ export const GanttChartBlocks: FC<{
|
||||
const updatedTargetDate = new Date(originalTargetDate);
|
||||
|
||||
// update the start date on left resize
|
||||
if (dragDirection === "left")
|
||||
updatedStartDate.setDate(originalStartDate.getDate() - totalBlockShifts);
|
||||
if (dragDirection === "left") updatedStartDate.setDate(originalStartDate.getDate() - totalBlockShifts);
|
||||
// update the target date on right resize
|
||||
else if (dragDirection === "right")
|
||||
updatedTargetDate.setDate(originalTargetDate.getDate() + totalBlockShifts);
|
||||
else if (dragDirection === "right") updatedTargetDate.setDate(originalTargetDate.getDate() + totalBlockShifts);
|
||||
// update both the dates on x-axis move
|
||||
else if (dragDirection === "move") {
|
||||
updatedStartDate.setDate(originalStartDate.getDate() + totalBlockShifts);
|
||||
@ -86,7 +84,7 @@ export const GanttChartBlocks: FC<{
|
||||
>
|
||||
<ChartDraggable
|
||||
block={block}
|
||||
BlockRender={BlockRender}
|
||||
blockToRender={blockToRender}
|
||||
handleBlock={(...args) => handleChartBlockPosition(block, ...args)}
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
|
@ -39,8 +39,8 @@ type ChartViewRootProps = {
|
||||
loaderTitle: string;
|
||||
blocks: IGanttBlock[] | null;
|
||||
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void;
|
||||
SidebarBlockRender: React.FC<any>;
|
||||
BlockRender: React.FC<any>;
|
||||
blockToRender: (data: any) => React.ReactNode;
|
||||
sidebarBlockToRender: (block: any) => React.ReactNode;
|
||||
enableBlockLeftResize: boolean;
|
||||
enableBlockRightResize: boolean;
|
||||
enableBlockMove: boolean;
|
||||
@ -54,8 +54,8 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
|
||||
blocks = null,
|
||||
loaderTitle,
|
||||
blockUpdateHandler,
|
||||
SidebarBlockRender,
|
||||
BlockRender,
|
||||
sidebarBlockToRender,
|
||||
blockToRender,
|
||||
enableBlockLeftResize,
|
||||
enableBlockRightResize,
|
||||
enableBlockMove,
|
||||
@ -289,7 +289,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
|
||||
title={title}
|
||||
blockUpdateHandler={blockUpdateHandler}
|
||||
blocks={chartBlocks}
|
||||
SidebarBlockRender={SidebarBlockRender}
|
||||
sidebarBlockToRender={sidebarBlockToRender}
|
||||
enableReorder={enableReorder}
|
||||
/>
|
||||
</div>
|
||||
@ -311,7 +311,7 @@ export const ChartViewRoot: FC<ChartViewRootProps> = ({
|
||||
<GanttChartBlocks
|
||||
itemsContainerWidth={itemsContainerWidth}
|
||||
blocks={chartBlocks}
|
||||
BlockRender={BlockRender}
|
||||
blockToRender={blockToRender}
|
||||
blockUpdateHandler={blockUpdateHandler}
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
|
@ -9,7 +9,7 @@ import { IGanttBlock } from "../types";
|
||||
|
||||
type Props = {
|
||||
block: IGanttBlock;
|
||||
BlockRender: React.FC<any>;
|
||||
blockToRender: (data: any) => React.ReactNode;
|
||||
handleBlock: (totalBlockShifts: number, dragDirection: "left" | "right" | "move") => void;
|
||||
enableBlockLeftResize: boolean;
|
||||
enableBlockRightResize: boolean;
|
||||
@ -18,7 +18,7 @@ type Props = {
|
||||
|
||||
export const ChartDraggable: React.FC<Props> = ({
|
||||
block,
|
||||
BlockRender,
|
||||
blockToRender,
|
||||
handleBlock,
|
||||
enableBlockLeftResize,
|
||||
enableBlockRightResize,
|
||||
@ -286,7 +286,7 @@ export const ChartDraggable: React.FC<Props> = ({
|
||||
className={`relative z-[2] rounded h-8 w-full flex items-center ${isMoving ? "pointer-events-none" : ""}`}
|
||||
onMouseDown={handleBlockMove}
|
||||
>
|
||||
<BlockRender data={block.data} />
|
||||
{blockToRender(block.data)}
|
||||
</div>
|
||||
{/* right resize drag handle */}
|
||||
{enableBlockRightResize && (
|
||||
|
@ -12,8 +12,8 @@ type GanttChartRootProps = {
|
||||
loaderTitle: string;
|
||||
blocks: IGanttBlock[] | null;
|
||||
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void;
|
||||
SidebarBlockRender: FC<any>;
|
||||
BlockRender: FC<any>;
|
||||
blockToRender: (data: any) => React.ReactNode;
|
||||
sidebarBlockToRender: (block: any) => React.ReactNode;
|
||||
enableBlockLeftResize?: boolean;
|
||||
enableBlockRightResize?: boolean;
|
||||
enableBlockMove?: boolean;
|
||||
@ -27,8 +27,8 @@ export const GanttChartRoot: FC<GanttChartRootProps> = ({
|
||||
blocks,
|
||||
loaderTitle = "blocks",
|
||||
blockUpdateHandler,
|
||||
SidebarBlockRender,
|
||||
BlockRender,
|
||||
sidebarBlockToRender,
|
||||
blockToRender,
|
||||
enableBlockLeftResize = true,
|
||||
enableBlockRightResize = true,
|
||||
enableBlockMove = true,
|
||||
@ -42,8 +42,8 @@ export const GanttChartRoot: FC<GanttChartRootProps> = ({
|
||||
blocks={blocks}
|
||||
loaderTitle={loaderTitle}
|
||||
blockUpdateHandler={blockUpdateHandler}
|
||||
SidebarBlockRender={SidebarBlockRender}
|
||||
BlockRender={BlockRender}
|
||||
sidebarBlockToRender={sidebarBlockToRender}
|
||||
blockToRender={blockToRender}
|
||||
enableBlockLeftResize={enableBlockLeftResize}
|
||||
enableBlockRightResize={enableBlockRightResize}
|
||||
enableBlockMove={enableBlockMove}
|
||||
|
@ -17,14 +17,14 @@ type Props = {
|
||||
title: string;
|
||||
blockUpdateHandler: (block: any, payload: IBlockUpdateData) => void;
|
||||
blocks: IGanttBlock[] | null;
|
||||
SidebarBlockRender: React.FC<any>;
|
||||
sidebarBlockToRender: (block: any) => React.ReactNode;
|
||||
enableReorder: boolean;
|
||||
enableQuickIssueCreate?: boolean;
|
||||
};
|
||||
|
||||
export const GanttSidebar: React.FC<Props> = (props) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { title, blockUpdateHandler, blocks, SidebarBlockRender, enableReorder, enableQuickIssueCreate } = props;
|
||||
const { title, blockUpdateHandler, blocks, sidebarBlockToRender, enableReorder, enableQuickIssueCreate } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { cycleId } = router.query;
|
||||
@ -130,9 +130,7 @@ export const GanttSidebar: React.FC<Props> = (props) => {
|
||||
</button>
|
||||
)}
|
||||
<div className="flex-grow truncate h-full flex items-center justify-between gap-2">
|
||||
<div className="flex-grow truncate">
|
||||
<SidebarBlockRender data={block.data} />
|
||||
</div>
|
||||
<div className="flex-grow truncate">{sidebarBlockToRender(block.data)}</div>
|
||||
<div className="flex-shrink-0 text-sm text-custom-text-200">
|
||||
{duration} day{duration > 1 ? "s" : ""}
|
||||
</div>
|
||||
|
@ -15,11 +15,12 @@ type Props = {
|
||||
issues: IIssueGroupedStructure | null;
|
||||
layout: "month" | "week" | undefined;
|
||||
showWeekends: boolean;
|
||||
handleIssues: (date: string, issue: IIssue, action: "update" | "delete") => void;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const CalendarChart: React.FC<Props> = observer((props) => {
|
||||
const { issues, layout, showWeekends, quickActions } = props;
|
||||
const { issues, layout, showWeekends, handleIssues, quickActions } = props;
|
||||
|
||||
const { calendar: calendarStore } = useMobxStore();
|
||||
|
||||
@ -49,6 +50,7 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
|
||||
week={week}
|
||||
issues={issues}
|
||||
enableQuickIssueCreate
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
/>
|
||||
))}
|
||||
@ -59,6 +61,7 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
|
||||
week={calendarStore.allDaysOfActiveWeek}
|
||||
issues={issues}
|
||||
enableQuickIssueCreate
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
/>
|
||||
)}
|
||||
|
@ -16,12 +16,13 @@ import { IIssue } from "types";
|
||||
type Props = {
|
||||
date: ICalendarDate;
|
||||
issues: IIssueGroupedStructure | null;
|
||||
handleIssues: (date: string, issue: IIssue, action: "update" | "delete") => void;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
enableQuickIssueCreate?: boolean;
|
||||
};
|
||||
|
||||
export const CalendarDayTile: React.FC<Props> = observer((props) => {
|
||||
const { date, issues, quickActions, enableQuickIssueCreate } = props;
|
||||
const { date, issues, handleIssues, quickActions, enableQuickIssueCreate } = props;
|
||||
|
||||
const { issueFilter: issueFilterStore } = useMobxStore();
|
||||
|
||||
@ -63,7 +64,7 @@ export const CalendarDayTile: React.FC<Props> = observer((props) => {
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
>
|
||||
<CalendarIssueBlocks issues={issuesList} quickActions={quickActions} />
|
||||
<CalendarIssueBlocks issues={issuesList} handleIssues={handleIssues} quickActions={quickActions} />
|
||||
{enableQuickIssueCreate && (
|
||||
<div className="py-1 px-2">
|
||||
<CalendarInlineCreateIssueForm
|
||||
|
@ -2,16 +2,20 @@ import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Draggable } from "@hello-pangea/dnd";
|
||||
// components
|
||||
import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
||||
import { Tooltip } from "@plane/ui";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
|
||||
type Props = {
|
||||
issues: IIssue[] | null;
|
||||
handleIssues: (date: string, issue: IIssue, action: "update" | "delete") => void;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
};
|
||||
|
||||
export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
||||
const { issues, quickActions } = props;
|
||||
const { issues, handleIssues, quickActions } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
@ -47,7 +51,19 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
||||
<div className="text-xs text-custom-text-300 flex-shrink-0">
|
||||
{issue.project_detail.identifier}-{issue.sequence_id}
|
||||
</div>
|
||||
<h6 className="text-xs flex-grow truncate">{issue.name}</h6>
|
||||
<IssuePeekOverview
|
||||
workspaceSlug={issue?.workspace_detail?.slug}
|
||||
projectId={issue?.project_detail?.id}
|
||||
issueId={issue?.id}
|
||||
// TODO: add the logic here
|
||||
handleIssue={(issueToUpdate) => {
|
||||
handleIssues(issue.target_date ?? "", { ...issue, ...issueToUpdate }, "update");
|
||||
}}
|
||||
>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||
<span className="text-xs flex-grow truncate">{issue.name}</span>
|
||||
</Tooltip>
|
||||
</IssuePeekOverview>
|
||||
<div className="hidden group-hover/calendar-block:block">{quickActions(issue)}</div>
|
||||
</a>
|
||||
</Link>
|
||||
|
@ -64,6 +64,7 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={(issue) => (
|
||||
<CycleIssueQuickActions
|
||||
issue={issue}
|
||||
|
@ -66,6 +66,7 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={(issue) => (
|
||||
<ModuleIssueQuickActions
|
||||
issue={issue}
|
||||
|
@ -57,6 +57,7 @@ export const CalendarLayout: React.FC = observer(() => {
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={(issue) => (
|
||||
<ProjectIssueQuickActions
|
||||
issue={issue}
|
||||
|
@ -57,6 +57,7 @@ export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
||||
issues={issues as IIssueGroupedStructure | null}
|
||||
layout={issueFilterStore.userDisplayFilters.calendar?.layout}
|
||||
showWeekends={issueFilterStore.userDisplayFilters.calendar?.show_weekends ?? false}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={(issue) => (
|
||||
<ProjectIssueQuickActions
|
||||
issue={issue}
|
||||
|
@ -14,12 +14,13 @@ import { IIssue } from "types";
|
||||
type Props = {
|
||||
issues: IIssueGroupedStructure | null;
|
||||
week: ICalendarWeek | undefined;
|
||||
handleIssues: (date: string, issue: IIssue, action: "update" | "delete") => void;
|
||||
quickActions: (issue: IIssue) => React.ReactNode;
|
||||
enableQuickIssueCreate?: boolean;
|
||||
};
|
||||
|
||||
export const CalendarWeekDays: React.FC<Props> = observer((props) => {
|
||||
const { issues, week, quickActions, enableQuickIssueCreate } = props;
|
||||
const { issues, week, handleIssues, quickActions, enableQuickIssueCreate } = props;
|
||||
|
||||
const { issueFilter: issueFilterStore } = useMobxStore();
|
||||
|
||||
@ -42,6 +43,7 @@ export const CalendarWeekDays: React.FC<Props> = observer((props) => {
|
||||
key={renderDateFormat(date.date)}
|
||||
date={date}
|
||||
issues={issues}
|
||||
handleIssues={handleIssues}
|
||||
quickActions={quickActions}
|
||||
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||
/>
|
||||
|
@ -1,29 +1,28 @@
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// ui
|
||||
import { Tooltip, StateGroupIcon } from "@plane/ui";
|
||||
import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
||||
import { IBlockUpdateData } from "components/gantt-chart";
|
||||
// helpers
|
||||
import { renderShortDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const IssueGanttBlock = ({ data }: { data: IIssue }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const openPeekOverview = () => {
|
||||
const { query } = router;
|
||||
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { ...query, peekIssue: data.id },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
export const IssueGanttBlock = ({
|
||||
data,
|
||||
handleIssue,
|
||||
}: {
|
||||
data: IIssue;
|
||||
handleIssue: (block: IIssue, payload: IBlockUpdateData) => void;
|
||||
}) => (
|
||||
<IssuePeekOverview
|
||||
workspaceSlug={data?.workspace_detail?.slug}
|
||||
projectId={data?.project_detail?.id}
|
||||
issueId={data?.id}
|
||||
handleIssue={(issueToUpdate) => handleIssue({ ...data, ...issueToUpdate }, {})}
|
||||
>
|
||||
<div
|
||||
className="flex items-center relative h-full w-full rounded cursor-pointer"
|
||||
style={{ backgroundColor: data?.state_detail?.color }}
|
||||
onClick={openPeekOverview}
|
||||
>
|
||||
<div className="absolute top-0 left-0 h-full w-full bg-custom-background-100/50" />
|
||||
<Tooltip
|
||||
@ -37,32 +36,36 @@ export const IssueGanttBlock = ({ data }: { data: IIssue }) => {
|
||||
}
|
||||
position="top-left"
|
||||
>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={data.name}>
|
||||
<div className="relative text-custom-text-100 text-sm truncate py-1 px-2.5 w-full">{data?.name}</div>
|
||||
</Tooltip>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</IssuePeekOverview>
|
||||
);
|
||||
|
||||
// rendering issues on gantt sidebar
|
||||
export const IssueGanttSidebarBlock = ({ data }: { data: IIssue }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const openPeekOverview = () => {
|
||||
const { query } = router;
|
||||
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { ...query, peekIssue: data.id },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative w-full flex items-center gap-2 h-full cursor-pointer" onClick={openPeekOverview}>
|
||||
export const IssueGanttSidebarBlock = ({
|
||||
data,
|
||||
handleIssue,
|
||||
}: {
|
||||
data: IIssue;
|
||||
handleIssue: (block: IIssue, payload: IBlockUpdateData) => void;
|
||||
}) => (
|
||||
<IssuePeekOverview
|
||||
workspaceSlug={data?.workspace_detail?.slug}
|
||||
projectId={data?.project_detail?.id}
|
||||
issueId={data?.id}
|
||||
handleIssue={(issueToUpdate) => handleIssue({ ...data, ...issueToUpdate }, {})}
|
||||
>
|
||||
<div className="relative w-full flex items-center gap-2 h-full cursor-pointer">
|
||||
<StateGroupIcon stateGroup={data?.state_detail?.group} color={data?.state_detail?.color} />
|
||||
<div className="text-xs text-custom-text-300 flex-shrink-0">
|
||||
{data?.project_detail?.identifier} {data?.sequence_id}
|
||||
</div>
|
||||
<h6 className="text-sm font-medium flex-grow truncate">{data?.name}</h6>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={data.name}>
|
||||
<span className="text-sm font-medium flex-grow truncate">{data?.name}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</IssuePeekOverview>
|
||||
);
|
||||
|
@ -8,6 +8,7 @@ import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "co
|
||||
import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues";
|
||||
// types
|
||||
import { IIssueUnGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const CycleGanttLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
@ -38,8 +39,8 @@ export const CycleGanttLayout: React.FC = observer(() => {
|
||||
loaderTitle="Issues"
|
||||
blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null}
|
||||
blockUpdateHandler={updateIssue}
|
||||
BlockRender={IssueGanttBlock}
|
||||
SidebarBlockRender={IssueGanttSidebarBlock}
|
||||
blockToRender={(data: IIssue) => <IssueGanttBlock data={data} handleIssue={updateIssue} />}
|
||||
sidebarBlockToRender={(data: IIssue) => <IssueGanttSidebarBlock data={data} handleIssue={updateIssue} />}
|
||||
enableBlockLeftResize={isAllowed}
|
||||
enableBlockRightResize={isAllowed}
|
||||
enableBlockMove={isAllowed}
|
||||
|
@ -8,6 +8,7 @@ import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "co
|
||||
import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues";
|
||||
// types
|
||||
import { IIssueUnGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const ModuleGanttLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
@ -38,8 +39,8 @@ export const ModuleGanttLayout: React.FC = observer(() => {
|
||||
loaderTitle="Issues"
|
||||
blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null}
|
||||
blockUpdateHandler={updateIssue}
|
||||
BlockRender={IssueGanttBlock}
|
||||
SidebarBlockRender={IssueGanttSidebarBlock}
|
||||
blockToRender={(data: IIssue) => <IssueGanttBlock data={data} handleIssue={updateIssue} />}
|
||||
sidebarBlockToRender={(data: IIssue) => <IssueGanttSidebarBlock data={data} handleIssue={updateIssue} />}
|
||||
enableBlockLeftResize={isAllowed}
|
||||
enableBlockRightResize={isAllowed}
|
||||
enableBlockMove={isAllowed}
|
||||
|
@ -10,6 +10,7 @@ import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "co
|
||||
import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues";
|
||||
// types
|
||||
import { IIssueUnGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const ProjectViewGanttLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
@ -40,8 +41,8 @@ export const ProjectViewGanttLayout: React.FC = observer(() => {
|
||||
loaderTitle="Issues"
|
||||
blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null}
|
||||
blockUpdateHandler={updateIssue}
|
||||
BlockRender={IssueGanttBlock}
|
||||
SidebarBlockRender={IssueGanttSidebarBlock}
|
||||
blockToRender={(data: IIssue) => <IssueGanttBlock data={data} handleIssue={updateIssue} />}
|
||||
sidebarBlockToRender={(data: IIssue) => <IssueGanttSidebarBlock data={data} handleIssue={updateIssue} />}
|
||||
enableBlockLeftResize={isAllowed}
|
||||
enableBlockRightResize={isAllowed}
|
||||
enableBlockMove={isAllowed}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
@ -8,6 +9,7 @@ import { GanttChartRoot, IBlockUpdateData, renderIssueBlocksStructure } from "co
|
||||
import { IssueGanttBlock, IssueGanttSidebarBlock } from "components/issues";
|
||||
// types
|
||||
import { IIssueUnGroupedStructure } from "store/issue";
|
||||
import { IIssue } from "types";
|
||||
|
||||
export const GanttLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
@ -21,7 +23,7 @@ export const GanttLayout: React.FC = observer(() => {
|
||||
|
||||
const issues = issueStore.getIssues;
|
||||
|
||||
const updateIssue = (block: any, payload: IBlockUpdateData) => {
|
||||
const updateIssue = (block: IIssue, payload: IBlockUpdateData) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
issueStore.updateGanttIssueStructure(workspaceSlug.toString(), block, payload);
|
||||
@ -38,8 +40,8 @@ export const GanttLayout: React.FC = observer(() => {
|
||||
loaderTitle="Issues"
|
||||
blocks={issues ? renderIssueBlocksStructure(issues as IIssueUnGroupedStructure) : null}
|
||||
blockUpdateHandler={updateIssue}
|
||||
BlockRender={IssueGanttBlock}
|
||||
SidebarBlockRender={IssueGanttSidebarBlock}
|
||||
blockToRender={(data: IIssue) => <IssueGanttBlock data={data} handleIssue={updateIssue} />}
|
||||
sidebarBlockToRender={(data: IIssue) => <IssueGanttSidebarBlock data={data} handleIssue={updateIssue} />}
|
||||
enableBlockLeftResize={isAllowed}
|
||||
enableBlockRightResize={isAllowed}
|
||||
enableBlockMove={isAllowed}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Draggable } from "@hello-pangea/dnd";
|
||||
// components
|
||||
import { KanBanProperties } from "./properties";
|
||||
import { Tooltip } from "@plane/ui";
|
||||
import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
||||
// types
|
||||
import { IIssueDisplayProperties, IIssue } from "types";
|
||||
|
||||
@ -57,7 +59,23 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = (props) => {
|
||||
{issue.project_detail.identifier}-{issue.sequence_id}
|
||||
</div>
|
||||
)}
|
||||
<IssuePeekOverview
|
||||
workspaceSlug={issue?.workspace_detail?.slug}
|
||||
projectId={issue?.project_detail?.id}
|
||||
issueId={issue?.id}
|
||||
handleIssue={(issueToUpdate) => {
|
||||
handleIssues(
|
||||
!sub_group_id && sub_group_id === "null" ? null : sub_group_id,
|
||||
!columnId && columnId === "null" ? null : columnId,
|
||||
{ ...issue, ...issueToUpdate },
|
||||
"update"
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||
<div className="line-clamp-2 text-sm font-medium text-custom-text-100">{issue.name}</div>
|
||||
</Tooltip>
|
||||
</IssuePeekOverview>
|
||||
<div>
|
||||
<KanBanProperties
|
||||
sub_group_id={sub_group_id}
|
||||
|
@ -36,8 +36,9 @@ export const IssueBlock: React.FC<IssueBlockProps> = (props) => {
|
||||
workspaceSlug={issue?.workspace_detail?.slug}
|
||||
projectId={issue?.project_detail?.id}
|
||||
issueId={issue?.id}
|
||||
// TODO: add the logic here
|
||||
handleIssue={() => {}}
|
||||
handleIssue={(issueToUpdate) => {
|
||||
handleIssues(!columnId && columnId === "null" ? null : columnId, issueToUpdate as IIssue, "update");
|
||||
}}
|
||||
>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||
<div className="line-clamp-1 text-sm font-medium text-custom-text-100 w-full">{issue.name}</div>
|
||||
|
@ -4,6 +4,9 @@ import { Popover2 } from "@blueprintjs/popover2";
|
||||
import { MoreHorizontal, Pencil, Trash2, ChevronRight, Link } from "lucide-react";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
||||
import { Tooltip } from "@plane/ui";
|
||||
// helpers
|
||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||
// types
|
||||
@ -13,6 +16,7 @@ type Props = {
|
||||
issue: IIssue;
|
||||
expanded: boolean;
|
||||
handleToggleExpand: (issueId: string) => void;
|
||||
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
|
||||
properties: IIssueDisplayProperties;
|
||||
handleEditIssue: (issue: IIssue) => void;
|
||||
handleDeleteIssue: (issue: IIssue) => void;
|
||||
@ -24,6 +28,7 @@ export const IssueColumn: React.FC<Props> = ({
|
||||
issue,
|
||||
expanded,
|
||||
handleToggleExpand,
|
||||
handleUpdateIssue,
|
||||
properties,
|
||||
handleEditIssue,
|
||||
handleDeleteIssue,
|
||||
@ -38,15 +43,6 @@ export const IssueColumn: React.FC<Props> = ({
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const openPeekOverview = () => {
|
||||
const { query } = router;
|
||||
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { ...query, peekIssue: issue.id },
|
||||
});
|
||||
};
|
||||
|
||||
const handleCopyText = () => {
|
||||
copyUrlToClipboard(`${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`).then(() => {
|
||||
setToastAlert({
|
||||
@ -142,15 +138,20 @@ export const IssueColumn: React.FC<Props> = ({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<span className="flex items-center px-4 py-2.5 h-full truncate flex-grow">
|
||||
<button
|
||||
type="button"
|
||||
className="truncate text-custom-text-100 text-left cursor-pointer w-full text-[0.825rem]"
|
||||
onClick={openPeekOverview}
|
||||
<IssuePeekOverview
|
||||
workspaceSlug={issue?.workspace_detail?.slug}
|
||||
projectId={issue?.project_detail?.id}
|
||||
issueId={issue?.id}
|
||||
handleIssue={(issueToUpdate) => handleUpdateIssue(issueToUpdate as IIssue, issueToUpdate)}
|
||||
>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={issue.name}>
|
||||
<span className="flex items-center px-4 py-2.5 h-full truncate flex-grow">
|
||||
<div className="truncate text-custom-text-100 text-left cursor-pointer w-full text-[0.825rem]">
|
||||
{issue.name}
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</IssuePeekOverview>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -11,6 +11,7 @@ type Props = {
|
||||
issue: IIssue;
|
||||
expandedIssues: string[];
|
||||
setExpandedIssues: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
|
||||
properties: IIssueDisplayProperties;
|
||||
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
|
||||
disableUserActions: boolean;
|
||||
@ -21,6 +22,7 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
||||
issue,
|
||||
expandedIssues,
|
||||
setExpandedIssues,
|
||||
handleUpdateIssue,
|
||||
properties,
|
||||
handleIssueAction,
|
||||
disableUserActions,
|
||||
@ -49,6 +51,7 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
||||
expanded={isExpanded}
|
||||
handleToggleExpand={handleToggleExpand}
|
||||
properties={properties}
|
||||
handleUpdateIssue={handleUpdateIssue}
|
||||
handleEditIssue={() => handleIssueAction(issue, "edit")}
|
||||
handleDeleteIssue={() => handleIssueAction(issue, "delete")}
|
||||
disableUserActions={disableUserActions}
|
||||
@ -64,6 +67,7 @@ export const SpreadsheetIssuesColumn: React.FC<Props> = ({
|
||||
key={subIssue.id}
|
||||
issue={subIssue}
|
||||
expandedIssues={expandedIssues}
|
||||
handleUpdateIssue={handleUpdateIssue}
|
||||
setExpandedIssues={setExpandedIssues}
|
||||
properties={properties}
|
||||
handleIssueAction={handleIssueAction}
|
||||
|
@ -104,6 +104,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
||||
key={`${issue.id}_${index}`}
|
||||
issue={issue}
|
||||
expandedIssues={expandedIssues}
|
||||
handleUpdateIssue={handleUpdateIssue}
|
||||
setExpandedIssues={setExpandedIssues}
|
||||
properties={displayProperties}
|
||||
handleIssueAction={handleIssueAction}
|
||||
|
@ -22,10 +22,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
const { issueDetail: issueDetailStore }: RootStore = useMobxStore();
|
||||
|
||||
const issueUpdate = (_data: Partial<IIssue>) => {
|
||||
if (handleIssue) {
|
||||
handleIssue(_data);
|
||||
issueDetailStore.updateIssue(workspaceSlug, projectId, issueId, _data);
|
||||
}
|
||||
};
|
||||
|
||||
const issueReactionCreate = (reaction: string) =>
|
||||
|
@ -1,12 +1,9 @@
|
||||
import React from "react";
|
||||
// next imports
|
||||
import { useRouter } from "next/router";
|
||||
// swr
|
||||
import { mutate } from "swr";
|
||||
|
||||
// lucide icons
|
||||
import { ChevronDown, ChevronRight, X, Pencil, Trash, Link as LinkIcon, Loader } from "lucide-react";
|
||||
// components
|
||||
import { IssuePeekOverview } from "components/issues/peek-overview";
|
||||
import { IssuePeekOverview } from "../issue-peek-overview";
|
||||
import { SubIssuesRootList } from "./issues-list";
|
||||
import { IssueProperty } from "./properties";
|
||||
// ui
|
||||
@ -15,7 +12,6 @@ import { CustomMenu, Tooltip } from "@plane/ui";
|
||||
import { IUser, IIssue } from "types";
|
||||
import { ISubIssuesRootLoaders, ISubIssuesRootLoadersHandler } from "./root";
|
||||
// fetch keys
|
||||
import { SUB_ISSUES } from "constants/fetch-keys";
|
||||
|
||||
export interface ISubIssues {
|
||||
workspaceSlug: string;
|
||||
@ -34,6 +30,7 @@ export interface ISubIssues {
|
||||
issueId: string,
|
||||
issue?: IIssue | null
|
||||
) => void;
|
||||
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
|
||||
}
|
||||
|
||||
export const SubIssues: React.FC<ISubIssues> = ({
|
||||
@ -49,19 +46,8 @@ export const SubIssues: React.FC<ISubIssues> = ({
|
||||
handleIssuesLoader,
|
||||
copyText,
|
||||
handleIssueCrudOperation,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { query } = router;
|
||||
const { peekIssue } = query as { peekIssue: string };
|
||||
|
||||
const openPeekOverview = (issue_id: string) => {
|
||||
router.push({
|
||||
pathname: router.pathname,
|
||||
query: { ...query, peekIssue: issue_id },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
handleUpdateIssue,
|
||||
}) => (
|
||||
<div>
|
||||
{issue && (
|
||||
<div
|
||||
@ -91,7 +77,16 @@ export const SubIssues: React.FC<ISubIssues> = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full flex items-center gap-2 cursor-pointer" onClick={() => openPeekOverview(issue?.id)}>
|
||||
<IssuePeekOverview
|
||||
workspaceSlug={issue?.workspace_detail?.slug}
|
||||
projectId={issue?.project_detail?.id}
|
||||
issueId={issue?.id}
|
||||
handleIssue={(issueToUpdate) => {
|
||||
console.log("issueToUpdate", issueToUpdate);
|
||||
handleUpdateIssue(issue, { ...issue, ...issueToUpdate });
|
||||
}}
|
||||
>
|
||||
<div className="w-full flex items-center gap-2 cursor-pointer">
|
||||
<div
|
||||
className="flex-shrink-0 w-[6px] h-[6px] rounded-full"
|
||||
style={{
|
||||
@ -105,6 +100,7 @@ export const SubIssues: React.FC<ISubIssues> = ({
|
||||
<div className="line-clamp-1 text-xs text-custom-text-100 pr-2">{issue?.name}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</IssuePeekOverview>
|
||||
|
||||
<div className="flex-shrink-0 text-sm">
|
||||
<IssueProperty
|
||||
@ -182,17 +178,8 @@ export const SubIssues: React.FC<ISubIssues> = ({
|
||||
handleIssuesLoader={handleIssuesLoader}
|
||||
copyText={copyText}
|
||||
handleIssueCrudOperation={handleIssueCrudOperation}
|
||||
/>
|
||||
)}
|
||||
|
||||
{peekIssue && peekIssue === issue?.id && (
|
||||
<IssuePeekOverview
|
||||
handleMutation={() => parentIssue && parentIssue?.id && mutate(SUB_ISSUES(parentIssue?.id))}
|
||||
projectId={issue?.project ?? ""}
|
||||
workspaceSlug={workspaceSlug ?? ""}
|
||||
readOnly={!editable}
|
||||
handleUpdateIssue={handleUpdateIssue}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
@ -27,6 +27,7 @@ export interface ISubIssuesRootList {
|
||||
issueId: string,
|
||||
issue?: IIssue | null
|
||||
) => void;
|
||||
handleUpdateIssue: (issue: IIssue, data: Partial<IIssue>) => void;
|
||||
}
|
||||
|
||||
const issueService = new IssueService();
|
||||
@ -43,6 +44,7 @@ export const SubIssuesRootList: React.FC<ISubIssuesRootList> = ({
|
||||
handleIssuesLoader,
|
||||
copyText,
|
||||
handleIssueCrudOperation,
|
||||
handleUpdateIssue,
|
||||
}) => {
|
||||
const { data: issues, isLoading } = useSWR(
|
||||
workspaceSlug && projectId && parentIssue && parentIssue?.id ? SUB_ISSUES(parentIssue?.id) : null,
|
||||
@ -82,6 +84,7 @@ export const SubIssuesRootList: React.FC<ISubIssuesRootList> = ({
|
||||
handleIssuesLoader={handleIssuesLoader}
|
||||
copyText={copyText}
|
||||
handleIssueCrudOperation={handleIssueCrudOperation}
|
||||
handleUpdateIssue={handleUpdateIssue}
|
||||
/>
|
||||
))}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useCallback } from "react";
|
||||
// next imports
|
||||
import { useRouter } from "next/router";
|
||||
// swr
|
||||
@ -13,6 +13,7 @@ import { ProgressBar } from "./progressbar";
|
||||
// ui
|
||||
import { CustomMenu } from "@plane/ui";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import { useProjectMyMembership } from "contexts/project-member.context";
|
||||
import useToast from "hooks/use-toast";
|
||||
// helpers
|
||||
@ -49,6 +50,8 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
||||
peekIssue: string;
|
||||
};
|
||||
|
||||
const { issue: issueStore, issueDetail: issueDetailStore } = useMobxStore();
|
||||
|
||||
const { memberRole } = useProjectMyMembership();
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
@ -158,6 +161,21 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateIssue = useCallback(
|
||||
(issue: IIssue, data: Partial<IIssue>) => {
|
||||
if (!workspaceSlug || !projectId || !user) return;
|
||||
|
||||
const payload = {
|
||||
...issue,
|
||||
...data,
|
||||
};
|
||||
|
||||
issueStore.updateIssueStructure(null, null, payload);
|
||||
issueDetailStore.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, data);
|
||||
},
|
||||
[issueStore, issueDetailStore, projectId, user, workspaceSlug]
|
||||
);
|
||||
|
||||
const isEditable = memberRole?.isGuest || memberRole?.isViewer ? false : true;
|
||||
|
||||
const mutateSubIssues = (parentIssueId: string | null) => {
|
||||
@ -228,6 +246,7 @@ export const SubIssuesRoot: React.FC<ISubIssuesRoot> = ({ parentIssue, user }) =
|
||||
handleIssuesLoader={handleIssuesLoader}
|
||||
copyText={copyText}
|
||||
handleIssueCrudOperation={handleIssueCrudOperation}
|
||||
handleUpdateIssue={handleUpdateIssue}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -45,8 +45,8 @@ export const ModulesListGanttChartView: React.FC = observer(() => {
|
||||
loaderTitle="Modules"
|
||||
blocks={modules ? blockFormat(modules) : null}
|
||||
blockUpdateHandler={(block, payload) => handleModuleUpdate(block, payload)}
|
||||
SidebarBlockRender={ModuleGanttSidebarBlock}
|
||||
BlockRender={ModuleGanttBlock}
|
||||
sidebarBlockToRender={ModuleGanttSidebarBlock}
|
||||
blockToRender={(data: IModule) => <ModuleGanttBlock data={data} />}
|
||||
enableBlockLeftResize={isAllowed}
|
||||
enableBlockRightResize={isAllowed}
|
||||
enableBlockMove={isAllowed}
|
||||
|
@ -265,9 +265,10 @@ export class IssueStore implements IIssueStore {
|
||||
...i,
|
||||
...(i.id === issue.id
|
||||
? {
|
||||
...issue,
|
||||
sort_order: payload.sort_order?.newSortOrder ?? i.sort_order,
|
||||
start_date: payload.start_date,
|
||||
target_date: payload.target_date,
|
||||
start_date: payload.start_date ?? i.start_date,
|
||||
target_date: payload.target_date ?? i.target_date,
|
||||
}
|
||||
: {}),
|
||||
}));
|
||||
@ -288,7 +289,7 @@ export class IssueStore implements IIssueStore {
|
||||
};
|
||||
});
|
||||
|
||||
const newPayload: any = { ...payload };
|
||||
const newPayload: any = { ...issue, ...payload };
|
||||
|
||||
if (newPayload.sort_order && payload.sort_order) newPayload.sort_order = payload.sort_order.newSortOrder;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user