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:
Anmol Singh Bhatia 2023-03-23 22:42:08 +05:30 committed by GitHub
parent 5191fc5f7c
commit b6a3615f66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 133 additions and 74 deletions

View File

@ -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}
/>
);

View File

@ -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>
);

View File

@ -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>
)}

View File

@ -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>

View File

@ -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}
/>
)}

View File

@ -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}
/>
);

View File

@ -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>

View File

@ -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}
/>
))

View File

@ -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>

View File

@ -70,6 +70,7 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
cycle={cycle}
handleDeleteCycle={() => handleDeleteCycle(cycle)}
handleEditCycle={() => handleEditCycle(cycle)}
isCompleted
/>
))}
</div>

View File

@ -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>

View File

@ -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" />

View File

@ -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>
);