fix: breaking cycle issues and replacing router.push with Links (#3330)

* fix cycle creation and active cycle map

* minor fix in cycle store

* create cycle breaking fix

* replace last possible bits of router.push with Link

---------

Co-authored-by: Rahul R <rahulr@Rahuls-MacBook-Pro.local>
This commit is contained in:
rahulramesha 2024-01-08 19:20:42 +05:30 committed by GitHub
parent a71491ecb9
commit eccb1f5d10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 161 additions and 158 deletions

View File

@ -2,6 +2,7 @@ import { useRouter } from "next/router";
import { Command } from "cmdk"; import { Command } from "cmdk";
// icons // icons
import { SettingIcon } from "components/icons"; import { SettingIcon } from "components/icons";
import Link from "next/link";
type Props = { type Props = {
closePalette: () => void; closePalette: () => void;
@ -13,48 +14,55 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
const redirect = (path: string) => {
closePalette();
router.push(path);
};
return ( return (
<> <>
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings`)} className="focus:outline-none"> <Command.Item onSelect={closePalette} className="focus:outline-none">
<Link href={`/${workspaceSlug}/settings`}>
<div className="flex items-center gap-2 text-custom-text-200"> <div className="flex items-center gap-2 text-custom-text-200">
<SettingIcon className="h-4 w-4 text-custom-text-200" /> <SettingIcon className="h-4 w-4 text-custom-text-200" />
General General
</div> </div>
</Link>
</Command.Item> </Command.Item>
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/members`)} className="focus:outline-none"> <Command.Item onSelect={closePalette} className="focus:outline-none">
<Link href={`/${workspaceSlug}/settings/members`}>
<div className="flex items-center gap-2 text-custom-text-200"> <div className="flex items-center gap-2 text-custom-text-200">
<SettingIcon className="h-4 w-4 text-custom-text-200" /> <SettingIcon className="h-4 w-4 text-custom-text-200" />
Members Members
</div> </div>
</Link>
</Command.Item> </Command.Item>
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/billing`)} className="focus:outline-none"> <Command.Item onSelect={closePalette} className="focus:outline-none">
<Link href={`/${workspaceSlug}/settings/billing`}>
<div className="flex items-center gap-2 text-custom-text-200"> <div className="flex items-center gap-2 text-custom-text-200">
<SettingIcon className="h-4 w-4 text-custom-text-200" /> <SettingIcon className="h-4 w-4 text-custom-text-200" />
Billing and Plans Billing and Plans
</div> </div>
</Link>
</Command.Item> </Command.Item>
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/integrations`)} className="focus:outline-none"> <Command.Item onSelect={closePalette} className="focus:outline-none">
<Link href={`/${workspaceSlug}/settings/integrations`}>
<div className="flex items-center gap-2 text-custom-text-200"> <div className="flex items-center gap-2 text-custom-text-200">
<SettingIcon className="h-4 w-4 text-custom-text-200" /> <SettingIcon className="h-4 w-4 text-custom-text-200" />
Integrations Integrations
</div> </div>
</Link>
</Command.Item> </Command.Item>
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/imports`)} className="focus:outline-none"> <Command.Item onSelect={closePalette} className="focus:outline-none">
<Link href={`/${workspaceSlug}/settings/imports`}>
<div className="flex items-center gap-2 text-custom-text-200"> <div className="flex items-center gap-2 text-custom-text-200">
<SettingIcon className="h-4 w-4 text-custom-text-200" /> <SettingIcon className="h-4 w-4 text-custom-text-200" />
Import Import
</div> </div>
</Link>
</Command.Item> </Command.Item>
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/exports`)} className="focus:outline-none"> <Command.Item onSelect={closePalette} className="focus:outline-none">
<Link href={`/${workspaceSlug}/settings/exports`}>
<div className="flex items-center gap-2 text-custom-text-200"> <div className="flex items-center gap-2 text-custom-text-200">
<SettingIcon className="h-4 w-4 text-custom-text-200" /> <SettingIcon className="h-4 w-4 text-custom-text-200" />
Export Export
</div> </div>
</Link>
</Command.Item> </Command.Item>
</> </>
); );

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { eachDayOfInterval } from "date-fns"; import { eachDayOfInterval, isValid } from "date-fns";
// ui // ui
import { LineGraph } from "components/ui"; import { LineGraph } from "components/ui";
// helpers // helpers
@ -47,7 +47,13 @@ const ProgressChart: React.FC<Props> = ({ distribution, startDate, endDate, tota
})); }));
const generateXAxisTickValues = () => { const generateXAxisTickValues = () => {
const dates = eachDayOfInterval({ start: new Date(startDate), end: new Date(endDate) }); const start = new Date(startDate);
const end = new Date(endDate);
let dates: Date[] = [];
if (isValid(start) && isValid(end)) {
dates = eachDayOfInterval({ start, end });
}
const maxDates = 4; const maxDates = 4;
const totalDates = dates.length; const totalDates = dates.length;

View File

@ -33,6 +33,7 @@ import { truncateText } from "helpers/string.helper";
import { ICycle } from "@plane/types"; import { ICycle } from "@plane/types";
import { EIssuesStoreType } from "constants/issue"; import { EIssuesStoreType } from "constants/issue";
import { ACTIVE_CYCLE_ISSUES } from "store/issue/cycle"; import { ACTIVE_CYCLE_ISSUES } from "store/issue/cycle";
import { CYCLE_ISSUES_WITH_PARAMS } from "constants/fetch-keys";
const stateGroups = [ const stateGroups = [
{ {
@ -73,7 +74,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId } = props;
const { const {
issues: { issues }, issues: { issues, fetchActiveCycleIssues },
issueMap, issueMap,
} = useIssues(EIssuesStoreType.CYCLE); } = useIssues(EIssuesStoreType.CYCLE);
// store hooks // store hooks
@ -99,13 +100,14 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null; const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null;
const issueIds = issues?.[ACTIVE_CYCLE_ISSUES]; const issueIds = issues?.[ACTIVE_CYCLE_ISSUES];
// useSWR( useSWR(
// workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId, { priority: "urgent,high" }) : null, workspaceSlug && projectId && currentProjectActiveCycleId
// workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(currentProjectActiveCycleId, { priority: "urgent,high" })
// ? () => : null,
// fetchActiveCycleIssues(workspaceSlug, projectId, ) workspaceSlug && projectId && currentProjectActiveCycleId
// : null ? () => fetchActiveCycleIssues(workspaceSlug, projectId, currentProjectActiveCycleId)
// ); : null
);
if (!activeCycle && isLoading) if (!activeCycle && isLoading)
return ( return (
@ -382,9 +384,9 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
{issueIds ? ( {issueIds ? (
issueIds.length > 0 ? ( issueIds.length > 0 ? (
issueIds.map((issue: any) => ( issueIds.map((issue: any) => (
<div <Link
key={issue.id} key={issue.id}
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`)} href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}
className="flex cursor-pointer flex-wrap items-center justify-between gap-2 rounded-md border border-custom-border-200 bg-custom-background-90 px-3 py-1.5" className="flex cursor-pointer flex-wrap items-center justify-between gap-2 rounded-md border border-custom-border-200 bg-custom-background-90 px-3 py-1.5"
> >
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
@ -427,7 +429,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
)} )}
</div> </div>
</div> </div>
</div> </Link>
)) ))
) : ( ) : (
<div className="grid place-items-center text-center text-sm text-custom-text-200"> <div className="grid place-items-center text-center text-sm text-custom-text-200">

View File

@ -1,6 +1,7 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import Link from "next/link";
// hooks // hooks
import { import {
useApplication, useApplication,
@ -41,14 +42,11 @@ const CycleDropdownOption: React.FC<{ cycleId: string }> = ({ cycleId }) => {
if (!cycle) return null; if (!cycle) return null;
return ( return (
<CustomMenu.MenuItem <CustomMenu.MenuItem key={cycle.id}>
key={cycle.id} <Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`} className="flex items-center gap-1.5">
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`)}
>
<div className="flex items-center gap-1.5">
<ContrastIcon className="h-3 w-3" /> <ContrastIcon className="h-3 w-3" />
{truncateText(cycle.name, 40)} {truncateText(cycle.name, 40)}
</div> </Link>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
); );
}; };

View File

@ -1,6 +1,7 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import Link from "next/link";
// hooks // hooks
import { import {
useApplication, useApplication,
@ -41,14 +42,14 @@ const ModuleDropdownOption: React.FC<{ moduleId: string }> = ({ moduleId }) => {
if (!moduleDetail) return null; if (!moduleDetail) return null;
return ( return (
<CustomMenu.MenuItem <CustomMenu.MenuItem key={moduleDetail.id}>
key={moduleDetail.id} <Link
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/modules/${moduleDetail.id}`)} href={`/${workspaceSlug}/projects/${projectId}/modules/${moduleDetail.id}`}
className="flex items-center gap-1.5"
> >
<div className="flex items-center gap-1.5">
<DiceIcon className="h-3 w-3" /> <DiceIcon className="h-3 w-3" />
{truncateText(moduleDetail.name, 40)} {truncateText(moduleDetail.name, 40)}
</div> </Link>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
); );
}; };

View File

@ -2,6 +2,7 @@ import { useCallback } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Plus } from "lucide-react"; import { Plus } from "lucide-react";
import Link from "next/link";
// hooks // hooks
import { import {
useApplication, useApplication,
@ -154,14 +155,14 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
if (!view) return; if (!view) return;
return ( return (
<CustomMenu.MenuItem <CustomMenu.MenuItem key={viewId}>
key={viewId} <Link
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/views/${viewId}`)} href={`/${workspaceSlug}/projects/${projectId}/views/${viewId}`}
className="flex items-center gap-1.5"
> >
<div className="flex items-center gap-1.5">
<PhotoFilterIcon height={12} width={12} /> <PhotoFilterIcon height={12} width={12} />
{truncateText(view.name, 40)} {truncateText(view.name, 40)}
</div> </Link>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
); );
})} })}

View File

@ -2,6 +2,7 @@ import React from "react";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { ArchiveRestore, Clock, MessageSquare, User2 } from "lucide-react"; import { ArchiveRestore, Clock, MessageSquare, User2 } from "lucide-react";
import Link from "next/link";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// icons // icons
@ -10,17 +11,14 @@ import { ArchiveIcon, CustomMenu, Tooltip } from "@plane/ui";
import { snoozeOptions } from "constants/notification"; import { snoozeOptions } from "constants/notification";
// helper // helper
import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper"; import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper";
import { import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper";
calculateTimeAgo,
renderFormattedTime,
renderFormattedDate,
} from "helpers/date-time.helper";
// type // type
import type { IUserNotification } from "@plane/types"; import type { IUserNotification } from "@plane/types";
type NotificationCardProps = { type NotificationCardProps = {
notification: IUserNotification; notification: IUserNotification;
isSnoozedTabOpen: boolean; isSnoozedTabOpen: boolean;
closePopover: () => void;
markNotificationReadStatus: (notificationId: string) => Promise<void>; markNotificationReadStatus: (notificationId: string) => Promise<void>;
markNotificationReadStatusToggle: (notificationId: string) => Promise<void>; markNotificationReadStatusToggle: (notificationId: string) => Promise<void>;
markNotificationArchivedStatus: (notificationId: string) => Promise<void>; markNotificationArchivedStatus: (notificationId: string) => Promise<void>;
@ -32,6 +30,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
const { const {
notification, notification,
isSnoozedTabOpen, isSnoozedTabOpen,
closePopover,
markNotificationReadStatus, markNotificationReadStatus,
markNotificationReadStatusToggle, markNotificationReadStatusToggle,
markNotificationArchivedStatus, markNotificationArchivedStatus,
@ -47,15 +46,14 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
if (isSnoozedTabOpen && new Date(notification.snoozed_till!) < new Date()) return null; if (isSnoozedTabOpen && new Date(notification.snoozed_till!) < new Date()) return null;
return ( return (
<div <Link
onClick={() => { onClick={() => {
markNotificationReadStatus(notification.id); markNotificationReadStatus(notification.id);
router.push( closePopover();
`/${workspaceSlug}/projects/${notification.project}/${
notification.data.issue_activity.field === "archived_at" ? "archived-issues" : "issues"
}/${notification.data.issue.id}`
);
}} }}
href={`/${workspaceSlug}/projects/${notification.project}/${
notification.data.issue_activity.field === "archived_at" ? "archived-issues" : "issues"
}/${notification.data.issue.id}`}
className={`group relative flex w-full cursor-pointer items-center gap-4 p-3 pl-6 ${ className={`group relative flex w-full cursor-pointer items-center gap-4 p-3 pl-6 ${
notification.read_at === null ? "bg-custom-primary-70/5" : "hover:bg-custom-background-200" notification.read_at === null ? "bg-custom-primary-70/5" : "hover:bg-custom-background-200"
}`} }`}
@ -149,7 +147,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
<p className="flex flex-shrink-0 items-center justify-end gap-x-1 text-custom-text-300"> <p className="flex flex-shrink-0 items-center justify-end gap-x-1 text-custom-text-300">
<Clock className="h-4 w-4" /> <Clock className="h-4 w-4" />
<span> <span>
Till {renderFormattedDate(notification.snoozed_till)}, {renderFormattedTime(notification.snoozed_till, '12-hour')} Till {renderFormattedDate(notification.snoozed_till)},{" "}
{renderFormattedTime(notification.snoozed_till, "12-hour")}
</span> </span>
</p> </p>
) : ( ) : (
@ -195,6 +194,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
type="button" type="button"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault();
item.onClick(); item.onClick();
}} }}
key={item.id} key={item.id}
@ -204,7 +205,6 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
</button> </button>
</Tooltip> </Tooltip>
))} ))}
<Tooltip tooltipContent="Snooze"> <Tooltip tooltipContent="Snooze">
<CustomMenu <CustomMenu
className="flex items-center" className="flex items-center"
@ -223,6 +223,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
key={item.label} key={item.label}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault();
if (!item.value) { if (!item.value) {
setSelectedNotificationForSnooze(notification.id); setSelectedNotificationForSnooze(notification.id);
@ -243,6 +244,6 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
</CustomMenu> </CustomMenu>
</Tooltip> </Tooltip>
</div> </div>
</div> </Link>
); );
}; };

View File

@ -119,6 +119,7 @@ export const NotificationPopover = observer(() => {
<NotificationCard <NotificationCard
key={notification.id} key={notification.id}
isSnoozedTabOpen={snoozed} isSnoozedTabOpen={snoozed}
closePopover={closePopover}
notification={notification} notification={notification}
markNotificationArchivedStatus={markNotificationArchivedStatus} markNotificationArchivedStatus={markNotificationArchivedStatus}
markNotificationReadStatus={markNotificationAsRead} markNotificationReadStatus={markNotificationAsRead}

View File

@ -2,6 +2,7 @@ import React, { useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { LinkIcon, Lock, Pencil, Star } from "lucide-react"; import { LinkIcon, Lock, Pencil, Star } from "lucide-react";
import Link from "next/link";
// hooks // hooks
import { useProject } from "hooks/store"; import { useProject } from "hooks/store";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
@ -74,7 +75,7 @@ export const ProjectCard: React.FC<ProjectCardProps> = observer((props) => {
}); });
}; };
const projectMembersIds = project.members.map((member) => member.member_id); const projectMembersIds = project.members?.map((member) => member.member_id);
return ( return (
<> <>
@ -178,7 +179,7 @@ export const ProjectCard: React.FC<ProjectCardProps> = observer((props) => {
} }
position="top" position="top"
> >
{projectMembersIds.length > 0 ? ( {projectMembersIds && projectMembersIds.length > 0 ? (
<div className="flex cursor-pointer items-center gap-2 text-custom-text-200"> <div className="flex cursor-pointer items-center gap-2 text-custom-text-200">
<AvatarGroup showTooltip={false}> <AvatarGroup showTooltip={false}>
{projectMembersIds.map((memberId) => { {projectMembersIds.map((memberId) => {
@ -195,17 +196,15 @@ export const ProjectCard: React.FC<ProjectCardProps> = observer((props) => {
)} )}
</Tooltip> </Tooltip>
{(isOwner || isMember) && ( {(isOwner || isMember) && (
<button <Link
className="flex items-center justify-center rounded p-1 text-custom-text-400 hover:bg-custom-background-80 hover:text-custom-text-200" className="flex items-center justify-center rounded p-1 text-custom-text-400 hover:bg-custom-background-80 hover:text-custom-text-200"
onClick={(e) => { onClick={(e) => {
e.preventDefault();
e.stopPropagation(); e.stopPropagation();
router.push(`/${workspaceSlug}/projects/${project.id}/settings`);
}} }}
href={`/${workspaceSlug}/projects/${project.id}/settings`}
> >
<Pencil className="h-3.5 w-3.5" /> <Pencil className="h-3.5 w-3.5" />
</button> </Link>
)} )}
{!project.is_member ? ( {!project.is_member ? (

View File

@ -45,12 +45,12 @@ type EmptySpaceItemProps = {
title: string; title: string;
description?: React.ReactNode | string; description?: React.ReactNode | string;
Icon: any; Icon: any;
action: () => void; action?: () => void;
href?: string;
}; };
const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Icon, action }) => ( const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Icon, action, href }) => {
<> let spaceItem = (
<li className="cursor-pointer" onClick={action} role="button">
<div className={`group relative flex ${description ? "items-start" : "items-center"} space-x-3 py-4`}> <div className={`group relative flex ${description ? "items-start" : "items-center"} space-x-3 py-4`}>
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-custom-primary"> <span className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-custom-primary">
@ -65,8 +65,19 @@ const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Ico
<ChevronRight className="h-5 w-5 text-custom-text-200 group-hover:text-custom-text-100" aria-hidden="true" /> <ChevronRight className="h-5 w-5 text-custom-text-200 group-hover:text-custom-text-100" aria-hidden="true" />
</div> </div>
</div> </div>
);
if (href) {
spaceItem = <Link href={href}>{spaceItem}</Link>;
}
return (
<>
<li className="cursor-pointer" onClick={action} role="button">
{spaceItem}
</li> </li>
</> </>
); );
};
export { EmptySpace, EmptySpaceItem }; export { EmptySpace, EmptySpaceItem };

View File

@ -1,6 +1,7 @@
import { FC, ReactNode } from "react"; import { FC, ReactNode } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import Link from "next/link";
// hooks // hooks
import { useUser } from "hooks/store"; import { useUser } from "hooks/store";
// components // components
@ -31,14 +32,12 @@ export const ProjectSettingLayout: FC<IProjectSettingLayout> = observer((props)
<NotAuthorizedView <NotAuthorizedView
type="project" type="project"
actionButton={ actionButton={
<Button //TODO: Create a new component called Button Link to handle such scenarios
variant="primary" <Link href={`/${workspaceSlug}/projects/${projectId}/issues`}>
size="md" <Button variant="primary" size="md" prependIcon={<LayersIcon />}>
prependIcon={<LayersIcon />}
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/issues`)}
>
Go to issues Go to issues
</Button> </Button>
</Link>
} }
/> />
) : ( ) : (

View File

@ -3,6 +3,7 @@ import { useRouter } from "next/router";
import Image from "next/image"; import Image from "next/image";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import Link from "next/link";
// hooks // hooks
import { useUser } from "hooks/store"; import { useUser } from "hooks/store";
// layouts // layouts
@ -39,9 +40,9 @@ const CreateWorkspacePage: NextPageWithLayout = observer(() => {
<div className="flex h-full flex-col gap-y-2 overflow-hidden sm:flex-row sm:gap-y-0"> <div className="flex h-full flex-col gap-y-2 overflow-hidden sm:flex-row sm:gap-y-0">
<div className="relative h-1/6 flex-shrink-0 sm:w-2/12 md:w-3/12 lg:w-1/5"> <div className="relative h-1/6 flex-shrink-0 sm:w-2/12 md:w-3/12 lg:w-1/5">
<div className="absolute left-0 top-1/2 h-[0.5px] w-full -translate-y-1/2 border-b-[0.5px] border-custom-border-200 sm:left-1/2 sm:top-0 sm:h-screen sm:w-[0.5px] sm:-translate-x-1/2 sm:translate-y-0 sm:border-r-[0.5px] md:left-1/3" /> <div className="absolute left-0 top-1/2 h-[0.5px] w-full -translate-y-1/2 border-b-[0.5px] border-custom-border-200 sm:left-1/2 sm:top-0 sm:h-screen sm:w-[0.5px] sm:-translate-x-1/2 sm:translate-y-0 sm:border-r-[0.5px] md:left-1/3" />
<button <Link
className="absolute left-5 top-1/2 grid -translate-y-1/2 place-items-center bg-custom-background-100 px-3 sm:left-1/2 sm:top-12 sm:-translate-x-[15px] sm:translate-y-0 sm:px-0 sm:py-5 md:left-1/3" className="absolute left-5 top-1/2 grid -translate-y-1/2 place-items-center bg-custom-background-100 px-3 sm:left-1/2 sm:top-12 sm:-translate-x-[15px] sm:translate-y-0 sm:px-0 sm:py-5 md:left-1/3"
onClick={() => router.push("/")} href="/"
> >
<div className="h-[30px] w-[133px]"> <div className="h-[30px] w-[133px]">
{theme === "light" ? ( {theme === "light" ? (
@ -50,7 +51,7 @@ const CreateWorkspacePage: NextPageWithLayout = observer(() => {
<Image src={WhiteHorizontalLogo} alt="Plane white logo" /> <Image src={WhiteHorizontalLogo} alt="Plane white logo" />
)} )}
</div> </div>
</button> </Link>
<div className="absolute right-4 top-1/4 -translate-y-1/2 text-sm text-custom-text-100 sm:fixed sm:right-16 sm:top-12 sm:translate-y-0 sm:py-5"> <div className="absolute right-4 top-1/4 -translate-y-1/2 text-sm text-custom-text-100 sm:fixed sm:right-16 sm:top-12 sm:translate-y-0 sm:py-5">
{currentUser?.email} {currentUser?.email}
</div> </div>

View File

@ -81,7 +81,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
title={`You are already a member of ${invitationDetail.workspace.name}`} title={`You are already a member of ${invitationDetail.workspace.name}`}
description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account." description="Your workspace is where you'll create projects, collaborate on your issues, and organize different streams of work in your Plane account."
> >
<EmptySpaceItem Icon={Boxes} title="Continue to Dashboard" action={() => router.push("/")} /> <EmptySpaceItem Icon={Boxes} title="Continue to Dashboard" href="/" />
</EmptySpace> </EmptySpace>
</> </>
) : ( ) : (
@ -103,35 +103,15 @@ const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
link={{ text: "Or start from an empty project", href: "/" }} link={{ text: "Or start from an empty project", href: "/" }}
> >
{!currentUser ? ( {!currentUser ? (
<EmptySpaceItem <EmptySpaceItem Icon={User2} title="Sign in to continue" href="/" />
Icon={User2}
title="Sign in to continue"
action={() => {
router.push("/");
}}
/>
) : ( ) : (
<EmptySpaceItem <EmptySpaceItem Icon={Boxes} title="Continue to Dashboard" href="/" />
Icon={Boxes}
title="Continue to Dashboard"
action={() => {
router.push("/");
}}
/>
)} )}
<EmptySpaceItem <EmptySpaceItem Icon={Star} title="Star us on GitHub" href="https://github.com/makeplane" />
Icon={Star}
title="Star us on GitHub"
action={() => {
router.push("https://github.com/makeplane");
}}
/>
<EmptySpaceItem <EmptySpaceItem
Icon={Share2} Icon={Share2}
title="Join our community of active creators" title="Join our community of active creators"
action={() => { href="https://discord.com/invite/8SR2N9PAcJ"
router.push("https://discord.com/invite/8SR2N9PAcJ");
}}
/> />
</EmptySpace> </EmptySpace>
) : ( ) : (

View File

@ -14,7 +14,7 @@ import { CycleService } from "services/cycle.service";
export interface ICycleStore { export interface ICycleStore {
// observables // observables
cycleMap: Record<string, ICycle>; cycleMap: Record<string, ICycle>;
activeCycleMap: Record<string, ICycle>; // TODO: Merge these two into single map activeCycleIdMap: Record<string, boolean>;
// computed // computed
currentProjectCycleIds: string[] | null; currentProjectCycleIds: string[] | null;
currentProjectCompletedCycleIds: string[] | null; currentProjectCompletedCycleIds: string[] | null;
@ -49,7 +49,7 @@ export interface ICycleStore {
export class CycleStore implements ICycleStore { export class CycleStore implements ICycleStore {
// observables // observables
cycleMap: Record<string, ICycle> = {}; cycleMap: Record<string, ICycle> = {};
activeCycleMap: Record<string, ICycle> = {}; activeCycleIdMap: Record<string, boolean> = {};
// root store // root store
rootStore; rootStore;
// services // services
@ -61,7 +61,7 @@ export class CycleStore implements ICycleStore {
makeObservable(this, { makeObservable(this, {
// observables // observables
cycleMap: observable, cycleMap: observable,
activeCycleMap: observable, activeCycleIdMap: observable,
// computed // computed
currentProjectCycleIds: computed, currentProjectCycleIds: computed,
currentProjectCompletedCycleIds: computed, currentProjectCompletedCycleIds: computed,
@ -168,8 +168,8 @@ export class CycleStore implements ICycleStore {
get currentProjectActiveCycleId() { get currentProjectActiveCycleId() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId) return null;
const activeCycle = Object.keys(this.activeCycleMap ?? {}).find( const activeCycle = Object.keys(this.activeCycleIdMap ?? {}).find(
(cycleId) => this.activeCycleMap?.[cycleId]?.project === projectId (cycleId) => this.cycleMap?.[cycleId]?.project === projectId
); );
return activeCycle || null; return activeCycle || null;
} }
@ -186,7 +186,8 @@ export class CycleStore implements ICycleStore {
* @param cycleId * @param cycleId
* @returns * @returns
*/ */
getActiveCycleById = (cycleId: string): ICycle | null => this.activeCycleMap?.[cycleId] ?? null; getActiveCycleById = (cycleId: string): ICycle | null =>
this.activeCycleIdMap?.[cycleId] && this.cycleMap?.[cycleId] ? this.cycleMap?.[cycleId] : null;
/** /**
* @description returns list of cycle ids of the project id passed as argument * @description returns list of cycle ids of the project id passed as argument
@ -235,7 +236,8 @@ export class CycleStore implements ICycleStore {
await this.cycleService.getCyclesWithParams(workspaceSlug, projectId, "current").then((response) => { await this.cycleService.getCyclesWithParams(workspaceSlug, projectId, "current").then((response) => {
runInAction(() => { runInAction(() => {
response.forEach((cycle) => { response.forEach((cycle) => {
set(this.activeCycleMap, [cycle.id], cycle); set(this.activeCycleIdMap, [cycle.id], true);
set(this.cycleMap, [cycle.id], cycle);
}); });
}); });
return response; return response;
@ -252,7 +254,6 @@ export class CycleStore implements ICycleStore {
await this.cycleService.getCycleDetails(workspaceSlug, projectId, cycleId).then((response) => { await this.cycleService.getCycleDetails(workspaceSlug, projectId, cycleId).then((response) => {
runInAction(() => { runInAction(() => {
set(this.cycleMap, [response.id], { ...this.cycleMap?.[response.id], ...response }); set(this.cycleMap, [response.id], { ...this.cycleMap?.[response.id], ...response });
set(this.activeCycleMap, [response.id], { ...this.activeCycleMap?.[response.id], ...response });
}); });
return response; return response;
}); });
@ -268,7 +269,6 @@ export class CycleStore implements ICycleStore {
await this.cycleService.createCycle(workspaceSlug, projectId, data).then((response) => { await this.cycleService.createCycle(workspaceSlug, projectId, data).then((response) => {
runInAction(() => { runInAction(() => {
set(this.cycleMap, [response.id], response); set(this.cycleMap, [response.id], response);
set(this.activeCycleMap, [response.id], response);
}); });
return response; return response;
}); });
@ -285,7 +285,6 @@ export class CycleStore implements ICycleStore {
try { try {
runInAction(() => { runInAction(() => {
set(this.cycleMap, [cycleId], { ...this.cycleMap?.[cycleId], ...data }); set(this.cycleMap, [cycleId], { ...this.cycleMap?.[cycleId], ...data });
set(this.activeCycleMap, [cycleId], { ...this.activeCycleMap?.[cycleId], ...data });
}); });
const response = await this.cycleService.patchCycle(workspaceSlug, projectId, cycleId, data); const response = await this.cycleService.patchCycle(workspaceSlug, projectId, cycleId, data);
return response; return response;
@ -307,7 +306,7 @@ export class CycleStore implements ICycleStore {
await this.cycleService.deleteCycle(workspaceSlug, projectId, cycleId).then(() => { await this.cycleService.deleteCycle(workspaceSlug, projectId, cycleId).then(() => {
runInAction(() => { runInAction(() => {
delete this.cycleMap[cycleId]; delete this.cycleMap[cycleId];
delete this.activeCycleMap[cycleId]; delete this.activeCycleIdMap[cycleId];
}); });
}); });
@ -324,7 +323,6 @@ export class CycleStore implements ICycleStore {
try { try {
runInAction(() => { runInAction(() => {
if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], true); if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], true);
if (currentActiveCycle) set(this.activeCycleMap, [cycleId, "is_favorite"], true);
}); });
// updating through api. // updating through api.
const response = await this.cycleService.addCycleToFavorites(workspaceSlug, projectId, { cycle: cycleId }); const response = await this.cycleService.addCycleToFavorites(workspaceSlug, projectId, { cycle: cycleId });
@ -332,7 +330,6 @@ export class CycleStore implements ICycleStore {
} catch (error) { } catch (error) {
runInAction(() => { runInAction(() => {
if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], false); if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], false);
if (currentActiveCycle) set(this.activeCycleMap, [cycleId, "is_favorite"], false);
}); });
throw error; throw error;
} }
@ -351,14 +348,12 @@ export class CycleStore implements ICycleStore {
try { try {
runInAction(() => { runInAction(() => {
if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], false); if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], false);
if (currentActiveCycle) set(this.activeCycleMap, [cycleId, "is_favorite"], false);
}); });
const response = await this.cycleService.removeCycleFromFavorites(workspaceSlug, projectId, cycleId); const response = await this.cycleService.removeCycleFromFavorites(workspaceSlug, projectId, cycleId);
return response; return response;
} catch (error) { } catch (error) {
runInAction(() => { runInAction(() => {
if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], true); if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], true);
if (currentActiveCycle) set(this.activeCycleMap, [cycleId, "is_favorite"], true);
}); });
throw error; throw error;
} }