mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
feat: completed cycle validation , fix: quick action and kanban fix (#505)
* feat: completed cycle card validation * fix: unique key to hidden group * feat: completed cycle sidebar validation * fix: remove console log from progress chart hover * feat: kanban and list view completed cycle validation * feat: quick action validation * refactor: code refactor * fix: sidebar draft cycle status
This commit is contained in:
parent
5191fc5f7c
commit
b6a3615f66
@ -18,6 +18,7 @@ type Props = {
|
||||
handleDeleteIssue: (issue: IIssue) => void;
|
||||
handleTrashBox: (isDragging: boolean) => void;
|
||||
removeIssue: ((bridgeId: string) => void) | null;
|
||||
isCompleted?: boolean;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
@ -31,6 +32,7 @@ export const AllBoards: React.FC<Props> = ({
|
||||
handleDeleteIssue,
|
||||
handleTrashBox,
|
||||
removeIssue,
|
||||
isCompleted = false,
|
||||
userAuth,
|
||||
}) => {
|
||||
const {
|
||||
@ -62,6 +64,7 @@ export const AllBoards: React.FC<Props> = ({
|
||||
openIssuesListModal={openIssuesListModal ?? null}
|
||||
handleTrashBox={handleTrashBox}
|
||||
removeIssue={removeIssue}
|
||||
isCompleted={isCompleted}
|
||||
userAuth={userAuth}
|
||||
/>
|
||||
);
|
||||
|
@ -15,6 +15,7 @@ type Props = {
|
||||
addIssueToState: () => void;
|
||||
isCollapsed: boolean;
|
||||
setIsCollapsed: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
isCompleted?: boolean;
|
||||
};
|
||||
|
||||
export const BoardHeader: React.FC<Props> = ({
|
||||
@ -23,6 +24,7 @@ export const BoardHeader: React.FC<Props> = ({
|
||||
addIssueToState,
|
||||
isCollapsed,
|
||||
setIsCollapsed,
|
||||
isCompleted = false,
|
||||
}) => {
|
||||
const { groupedByIssues, groupByProperty: selectedGroup } = useIssuesView();
|
||||
|
||||
@ -81,13 +83,15 @@ export const BoardHeader: React.FC<Props> = ({
|
||||
<ArrowsPointingOutIcon className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="grid h-7 w-7 place-items-center rounded p-1 text-gray-700 outline-none duration-300 hover:bg-gray-100"
|
||||
onClick={addIssueToState}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
</button>
|
||||
{!isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
className="grid h-7 w-7 place-items-center rounded p-1 text-gray-700 outline-none duration-300 hover:bg-gray-100"
|
||||
onClick={addIssueToState}
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -30,6 +30,7 @@ type Props = {
|
||||
openIssuesListModal?: (() => void) | null;
|
||||
handleTrashBox: (isDragging: boolean) => void;
|
||||
removeIssue: ((bridgeId: string) => void) | null;
|
||||
isCompleted?: boolean;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
@ -44,6 +45,7 @@ export const SingleBoard: React.FC<Props> = ({
|
||||
openIssuesListModal,
|
||||
handleTrashBox,
|
||||
removeIssue,
|
||||
isCompleted = false,
|
||||
userAuth,
|
||||
}) => {
|
||||
// collapse/expand
|
||||
@ -56,7 +58,7 @@ export const SingleBoard: React.FC<Props> = ({
|
||||
|
||||
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
|
||||
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted;
|
||||
|
||||
useEffect(() => {
|
||||
if (currentState?.group === "completed" || currentState?.group === "cancelled")
|
||||
@ -72,6 +74,7 @@ export const SingleBoard: React.FC<Props> = ({
|
||||
groupTitle={groupTitle}
|
||||
isCollapsed={isCollapsed}
|
||||
setIsCollapsed={setIsCollapsed}
|
||||
isCompleted={isCompleted}
|
||||
/>
|
||||
{isCollapsed && (
|
||||
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
|
||||
@ -125,6 +128,7 @@ export const SingleBoard: React.FC<Props> = ({
|
||||
removeIssue={() => {
|
||||
if (removeIssue && issue.bridge_id) removeIssue(issue.bridge_id);
|
||||
}}
|
||||
isCompleted={isCompleted}
|
||||
userAuth={userAuth}
|
||||
/>
|
||||
)}
|
||||
@ -147,26 +151,30 @@ export const SingleBoard: React.FC<Props> = ({
|
||||
Add Issue
|
||||
</button>
|
||||
) : (
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 font-medium text-theme outline-none"
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add Issue
|
||||
</button>
|
||||
}
|
||||
optionsPosition="left"
|
||||
noBorder
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={addIssueToState}>Create new</CustomMenu.MenuItem>
|
||||
{openIssuesListModal && (
|
||||
<CustomMenu.MenuItem onClick={openIssuesListModal}>
|
||||
Add an existing issue
|
||||
!isCompleted && (
|
||||
<CustomMenu
|
||||
customButton={
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-2 font-medium text-theme outline-none"
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
Add Issue
|
||||
</button>
|
||||
}
|
||||
optionsPosition="left"
|
||||
noBorder
|
||||
>
|
||||
<CustomMenu.MenuItem onClick={addIssueToState}>
|
||||
Create new
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
{openIssuesListModal && (
|
||||
<CustomMenu.MenuItem onClick={openIssuesListModal}>
|
||||
Add an existing issue
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
@ -59,6 +59,7 @@ type Props = {
|
||||
removeIssue?: (() => void) | null;
|
||||
handleDeleteIssue: (issue: IIssue) => void;
|
||||
handleTrashBox: (isDragging: boolean) => void;
|
||||
isCompleted?: boolean;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
@ -76,6 +77,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
groupTitle,
|
||||
handleDeleteIssue,
|
||||
handleTrashBox,
|
||||
isCompleted = false,
|
||||
userAuth,
|
||||
}) => {
|
||||
// context menu
|
||||
@ -177,7 +179,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
if (snapshot.isDragging) handleTrashBox(snapshot.isDragging);
|
||||
}, [snapshot, handleTrashBox]);
|
||||
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -187,15 +189,19 @@ export const SingleBoardIssue: React.FC<Props> = ({
|
||||
isOpen={contextMenu}
|
||||
setIsOpen={setContextMenu}
|
||||
>
|
||||
<ContextMenu.Item Icon={PencilIcon} onClick={editIssue}>
|
||||
Edit issue
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={ClipboardDocumentCheckIcon} onClick={makeIssueCopy}>
|
||||
Make a copy...
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={TrashIcon} onClick={() => handleDeleteIssue(issue)}>
|
||||
Delete issue
|
||||
</ContextMenu.Item>
|
||||
{!isNotAllowed &&(
|
||||
<>
|
||||
<ContextMenu.Item Icon={PencilIcon} onClick={editIssue}>
|
||||
Edit issue
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={ClipboardDocumentCheckIcon} onClick={makeIssueCopy}>
|
||||
Make a copy...
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={TrashIcon} onClick={() => handleDeleteIssue(issue)}>
|
||||
Delete issue
|
||||
</ContextMenu.Item>
|
||||
</>
|
||||
)}
|
||||
<ContextMenu.Item Icon={LinkIcon} onClick={handleCopyText}>
|
||||
Copy issue link
|
||||
</ContextMenu.Item>
|
||||
|
@ -55,10 +55,16 @@ import { getStateGroupIcon } from "components/icons";
|
||||
type Props = {
|
||||
type?: "issue" | "cycle" | "module";
|
||||
openIssuesListModal?: () => void;
|
||||
isCompleted?: boolean;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
export const IssuesView: React.FC<Props> = ({ type = "issue", openIssuesListModal, userAuth }) => {
|
||||
export const IssuesView: React.FC<Props> = ({
|
||||
type = "issue",
|
||||
openIssuesListModal,
|
||||
isCompleted = false,
|
||||
userAuth,
|
||||
}) => {
|
||||
// create issue modal
|
||||
const [createIssueModal, setCreateIssueModal] = useState(false);
|
||||
const [createViewModal, setCreateViewModal] = useState<any>(null);
|
||||
@ -592,6 +598,7 @@ export const IssuesView: React.FC<Props> = ({ type = "issue", openIssuesListModa
|
||||
? removeIssueFromModule
|
||||
: null
|
||||
}
|
||||
isCompleted={isCompleted}
|
||||
userAuth={userAuth}
|
||||
/>
|
||||
) : (
|
||||
@ -611,6 +618,7 @@ export const IssuesView: React.FC<Props> = ({ type = "issue", openIssuesListModa
|
||||
? removeIssueFromModule
|
||||
: null
|
||||
}
|
||||
isCompleted={isCompleted}
|
||||
userAuth={userAuth}
|
||||
/>
|
||||
)}
|
||||
|
@ -15,6 +15,7 @@ type Props = {
|
||||
handleDeleteIssue: (issue: IIssue) => void;
|
||||
openIssuesListModal?: (() => void) | null;
|
||||
removeIssue: ((bridgeId: string) => void) | null;
|
||||
isCompleted?: boolean;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
@ -27,6 +28,7 @@ export const AllLists: React.FC<Props> = ({
|
||||
handleEditIssue,
|
||||
handleDeleteIssue,
|
||||
removeIssue,
|
||||
isCompleted = false,
|
||||
userAuth,
|
||||
}) => {
|
||||
const { groupedByIssues, groupByProperty: selectedGroup, showEmptyGroups } = useIssuesView();
|
||||
@ -55,6 +57,7 @@ export const AllLists: React.FC<Props> = ({
|
||||
handleDeleteIssue={handleDeleteIssue}
|
||||
openIssuesListModal={type !== "issue" ? openIssuesListModal : null}
|
||||
removeIssue={removeIssue}
|
||||
isCompleted={isCompleted}
|
||||
userAuth={userAuth}
|
||||
/>
|
||||
);
|
||||
|
@ -49,6 +49,7 @@ type Props = {
|
||||
makeIssueCopy: () => void;
|
||||
removeIssue?: (() => void) | null;
|
||||
handleDeleteIssue: (issue: IIssue) => void;
|
||||
isCompleted?: boolean;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
@ -62,6 +63,7 @@ export const SingleListIssue: React.FC<Props> = ({
|
||||
removeIssue,
|
||||
groupTitle,
|
||||
handleDeleteIssue,
|
||||
isCompleted = false,
|
||||
userAuth,
|
||||
}) => {
|
||||
// context menu
|
||||
@ -145,7 +147,7 @@ export const SingleListIssue: React.FC<Props> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer;
|
||||
const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -155,15 +157,19 @@ export const SingleListIssue: React.FC<Props> = ({
|
||||
isOpen={contextMenu}
|
||||
setIsOpen={setContextMenu}
|
||||
>
|
||||
<ContextMenu.Item Icon={PencilIcon} onClick={editIssue}>
|
||||
Edit issue
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={ClipboardDocumentCheckIcon} onClick={makeIssueCopy}>
|
||||
Make a copy...
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={TrashIcon} onClick={() => handleDeleteIssue(issue)}>
|
||||
Delete issue
|
||||
</ContextMenu.Item>
|
||||
{!isNotAllowed && (
|
||||
<>
|
||||
<ContextMenu.Item Icon={PencilIcon} onClick={editIssue}>
|
||||
Edit issue
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={ClipboardDocumentCheckIcon} onClick={makeIssueCopy}>
|
||||
Make a copy...
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Item Icon={TrashIcon} onClick={() => handleDeleteIssue(issue)}>
|
||||
Delete issue
|
||||
</ContextMenu.Item>
|
||||
</>
|
||||
)}
|
||||
<ContextMenu.Item Icon={LinkIcon} onClick={handleCopyText}>
|
||||
Copy issue link
|
||||
</ContextMenu.Item>
|
||||
|
@ -30,6 +30,7 @@ type Props = {
|
||||
handleDeleteIssue: (issue: IIssue) => void;
|
||||
openIssuesListModal?: (() => void) | null;
|
||||
removeIssue: ((bridgeId: string) => void) | null;
|
||||
isCompleted?: boolean;
|
||||
userAuth: UserAuth;
|
||||
};
|
||||
|
||||
@ -46,6 +47,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
handleDeleteIssue,
|
||||
openIssuesListModal,
|
||||
removeIssue,
|
||||
isCompleted = false,
|
||||
userAuth,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
@ -93,9 +95,11 @@ export const SingleList: React.FC<Props> = ({
|
||||
>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
</button>
|
||||
) : isCompleted ? (
|
||||
""
|
||||
) : (
|
||||
<CustomMenu
|
||||
label={
|
||||
customButton={
|
||||
<span className="flex items-center">
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
</span>
|
||||
@ -138,6 +142,7 @@ export const SingleList: React.FC<Props> = ({
|
||||
removeIssue={() => {
|
||||
if (removeIssue !== null && issue.bridge_id) removeIssue(issue.bridge_id);
|
||||
}}
|
||||
isCompleted={isCompleted}
|
||||
userAuth={userAuth}
|
||||
/>
|
||||
))
|
||||
|
@ -39,7 +39,6 @@ const ProgressChart: React.FC<Props> = ({ issues, start, end }) => {
|
||||
|
||||
const CustomTooltip = ({ active, payload }: TooltipProps<ValueType, NameType>) => {
|
||||
if (active && payload && payload.length) {
|
||||
console.log(payload[0].payload.currentDate);
|
||||
return (
|
||||
<div className="rounded-sm bg-gray-300 p-1 text-xs text-gray-800">
|
||||
<p>{payload[0].payload.currentDate}</p>
|
||||
|
@ -70,6 +70,7 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
|
||||
cycle={cycle}
|
||||
handleDeleteCycle={() => handleDeleteCycle(cycle)}
|
||||
handleEditCycle={() => handleEditCycle(cycle)}
|
||||
isCompleted
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
@ -45,9 +45,15 @@ type Props = {
|
||||
cycle: ICycle | undefined;
|
||||
isOpen: boolean;
|
||||
cycleStatus: string;
|
||||
isCompleted: boolean;
|
||||
};
|
||||
|
||||
export const CycleDetailsSidebar: React.FC<Props> = ({ cycle, isOpen, cycleStatus }) => {
|
||||
export const CycleDetailsSidebar: React.FC<Props> = ({
|
||||
cycle,
|
||||
isOpen,
|
||||
cycleStatus,
|
||||
isCompleted,
|
||||
}) => {
|
||||
const [cycleDeleteModal, setCycleDeleteModal] = useState(false);
|
||||
const [startDateRange, setStartDateRange] = useState<Date | null>(new Date());
|
||||
const [endDateRange, setEndDateRange] = useState<Date | null>(null);
|
||||
@ -164,6 +170,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({ cycle, isOpen, cycleStatu
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
disabled={isCompleted ?? false}
|
||||
className={`group flex h-full items-center gap-1 rounded border-[0.5px] border-gray-200 bg-gray-100 px-2.5 py-1.5 text-gray-800 ${
|
||||
open ? "bg-gray-100" : ""
|
||||
}`}
|
||||
@ -209,6 +216,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({ cycle, isOpen, cycleStatu
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
disabled={isCompleted ?? false}
|
||||
className={`group flex items-center gap-1 rounded border-[0.5px] border-gray-200 bg-gray-100 px-2.5 py-1.5 text-gray-800 ${
|
||||
open ? "bg-gray-100" : ""
|
||||
}`}
|
||||
@ -262,12 +270,14 @@ export const CycleDetailsSidebar: React.FC<Props> = ({ cycle, isOpen, cycleStatu
|
||||
<span>Copy Link</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
<CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}>
|
||||
<span className="flex items-center justify-start gap-2 text-gray-800">
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
<span>Delete</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
{!isCompleted && (
|
||||
<CustomMenu.MenuItem onClick={() => setCycleDeleteModal(true)}>
|
||||
<span className="flex items-center justify-start gap-2 text-gray-800">
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
<span>Delete</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
</CustomMenu>
|
||||
</div>
|
||||
|
||||
|
@ -43,6 +43,7 @@ type TSingleStatProps = {
|
||||
cycle: ICycle;
|
||||
handleEditCycle: () => void;
|
||||
handleDeleteCycle: () => void;
|
||||
isCompleted?: boolean;
|
||||
};
|
||||
|
||||
const stateGroups = [
|
||||
@ -77,6 +78,7 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
|
||||
cycle,
|
||||
handleEditCycle,
|
||||
handleDeleteCycle,
|
||||
isCompleted = false,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
@ -293,22 +295,26 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = ({
|
||||
<span className="text-gray-900">{cycle.owned_by.first_name}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
onClick={handleEditCycle}
|
||||
className="flex cursor-pointer items-center rounded p-1 duration-300 hover:bg-gray-100"
|
||||
>
|
||||
<span>
|
||||
<PencilIcon className="h-4 w-4" />
|
||||
</span>
|
||||
</button>
|
||||
{!isCompleted && (
|
||||
<button
|
||||
onClick={handleEditCycle}
|
||||
className="flex cursor-pointer items-center rounded p-1 duration-300 hover:bg-gray-100"
|
||||
>
|
||||
<span>
|
||||
<PencilIcon className="h-4 w-4" />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<CustomMenu width="auto" verticalEllipsis>
|
||||
<CustomMenu.MenuItem onClick={handleDeleteCycle}>
|
||||
<span className="flex items-center justify-start gap-2 text-gray-800">
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
<span>Delete Cycle</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
{!isCompleted && (
|
||||
<CustomMenu.MenuItem onClick={handleDeleteCycle}>
|
||||
<span className="flex items-center justify-start gap-2 text-gray-800">
|
||||
<TrashIcon className="h-4 w-4" />
|
||||
<span>Delete Cycle</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
)}
|
||||
<CustomMenu.MenuItem onClick={handleCopyText}>
|
||||
<span className="flex items-center justify-start gap-2 text-gray-800">
|
||||
<DocumentDuplicateIcon className="h-4 w-4" />
|
||||
|
@ -73,7 +73,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
||||
const cycleStatus =
|
||||
cycleDetails?.start_date && cycleDetails?.end_date
|
||||
? getDateRangeStatus(cycleDetails?.start_date, cycleDetails?.end_date)
|
||||
: "";
|
||||
: "draft";
|
||||
|
||||
const { data: issues } = useSWR(
|
||||
workspaceSlug && projectId
|
||||
@ -159,9 +159,9 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
|
||||
}
|
||||
>
|
||||
<div className={`h-full ${cycleSidebar ? "mr-[24rem]" : ""} duration-300`}>
|
||||
<IssuesView type="cycle" userAuth={props} openIssuesListModal={openIssuesListModal} />
|
||||
<IssuesView type="cycle" userAuth={props} openIssuesListModal={openIssuesListModal} isCompleted={cycleStatus === "completed" ?? false} />
|
||||
</div>
|
||||
<CycleDetailsSidebar cycleStatus={cycleStatus} cycle={cycleDetails} isOpen={cycleSidebar} />
|
||||
<CycleDetailsSidebar cycleStatus={cycleStatus} cycle={cycleDetails} isOpen={cycleSidebar} isCompleted={cycleStatus === "completed" ?? false} />
|
||||
</AppLayout>
|
||||
</IssueViewContextProvider>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user