mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
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:
parent
12a3392722
commit
1257a88089
@ -2,6 +2,7 @@ import { useRouter } from "next/router";
|
||||
import { Command } from "cmdk";
|
||||
// icons
|
||||
import { SettingIcon } from "components/icons";
|
||||
import Link from "next/link";
|
||||
|
||||
type Props = {
|
||||
closePalette: () => void;
|
||||
@ -13,48 +14,55 @@ export const CommandPaletteWorkspaceSettingsActions: React.FC<Props> = (props) =
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const redirect = (path: string) => {
|
||||
closePalette();
|
||||
router.push(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings`)} className="focus:outline-none">
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
General
|
||||
</div>
|
||||
<Command.Item onSelect={closePalette} className="focus:outline-none">
|
||||
<Link href={`/${workspaceSlug}/settings`}>
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
General
|
||||
</div>
|
||||
</Link>
|
||||
</Command.Item>
|
||||
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/members`)} className="focus:outline-none">
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Members
|
||||
</div>
|
||||
<Command.Item onSelect={closePalette} className="focus:outline-none">
|
||||
<Link href={`/${workspaceSlug}/settings/members`}>
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Members
|
||||
</div>
|
||||
</Link>
|
||||
</Command.Item>
|
||||
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/billing`)} className="focus:outline-none">
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Billing and Plans
|
||||
</div>
|
||||
<Command.Item onSelect={closePalette} className="focus:outline-none">
|
||||
<Link href={`/${workspaceSlug}/settings/billing`}>
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Billing and Plans
|
||||
</div>
|
||||
</Link>
|
||||
</Command.Item>
|
||||
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/integrations`)} className="focus:outline-none">
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Integrations
|
||||
</div>
|
||||
<Command.Item onSelect={closePalette} className="focus:outline-none">
|
||||
<Link href={`/${workspaceSlug}/settings/integrations`}>
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Integrations
|
||||
</div>
|
||||
</Link>
|
||||
</Command.Item>
|
||||
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/imports`)} className="focus:outline-none">
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Import
|
||||
</div>
|
||||
<Command.Item onSelect={closePalette} className="focus:outline-none">
|
||||
<Link href={`/${workspaceSlug}/settings/imports`}>
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Import
|
||||
</div>
|
||||
</Link>
|
||||
</Command.Item>
|
||||
<Command.Item onSelect={() => redirect(`/${workspaceSlug}/settings/exports`)} className="focus:outline-none">
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Export
|
||||
</div>
|
||||
<Command.Item onSelect={closePalette} className="focus:outline-none">
|
||||
<Link href={`/${workspaceSlug}/settings/exports`}>
|
||||
<div className="flex items-center gap-2 text-custom-text-200">
|
||||
<SettingIcon className="h-4 w-4 text-custom-text-200" />
|
||||
Export
|
||||
</div>
|
||||
</Link>
|
||||
</Command.Item>
|
||||
</>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { eachDayOfInterval } from "date-fns";
|
||||
import { eachDayOfInterval, isValid } from "date-fns";
|
||||
// ui
|
||||
import { LineGraph } from "components/ui";
|
||||
// helpers
|
||||
@ -47,7 +47,13 @@ const ProgressChart: React.FC<Props> = ({ distribution, startDate, endDate, tota
|
||||
}));
|
||||
|
||||
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 totalDates = dates.length;
|
||||
|
@ -33,6 +33,7 @@ import { truncateText } from "helpers/string.helper";
|
||||
import { ICycle } from "@plane/types";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { ACTIVE_CYCLE_ISSUES } from "store/issue/cycle";
|
||||
import { CYCLE_ISSUES_WITH_PARAMS } from "constants/fetch-keys";
|
||||
|
||||
const stateGroups = [
|
||||
{
|
||||
@ -73,7 +74,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
const { workspaceSlug, projectId } = props;
|
||||
|
||||
const {
|
||||
issues: { issues },
|
||||
issues: { issues, fetchActiveCycleIssues },
|
||||
issueMap,
|
||||
} = useIssues(EIssuesStoreType.CYCLE);
|
||||
// store hooks
|
||||
@ -99,13 +100,14 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
const activeCycle = currentProjectActiveCycleId ? getActiveCycleById(currentProjectActiveCycleId) : null;
|
||||
const issueIds = issues?.[ACTIVE_CYCLE_ISSUES];
|
||||
|
||||
// useSWR(
|
||||
// workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId, { priority: "urgent,high" }) : null,
|
||||
// workspaceSlug && projectId && cycleId
|
||||
// ? () =>
|
||||
// fetchActiveCycleIssues(workspaceSlug, projectId, )
|
||||
// : null
|
||||
// );
|
||||
useSWR(
|
||||
workspaceSlug && projectId && currentProjectActiveCycleId
|
||||
? CYCLE_ISSUES_WITH_PARAMS(currentProjectActiveCycleId, { priority: "urgent,high" })
|
||||
: null,
|
||||
workspaceSlug && projectId && currentProjectActiveCycleId
|
||||
? () => fetchActiveCycleIssues(workspaceSlug, projectId, currentProjectActiveCycleId)
|
||||
: null
|
||||
);
|
||||
|
||||
if (!activeCycle && isLoading)
|
||||
return (
|
||||
@ -382,9 +384,9 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
{issueIds ? (
|
||||
issueIds.length > 0 ? (
|
||||
issueIds.map((issue: any) => (
|
||||
<div
|
||||
<Link
|
||||
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"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
@ -427,7 +429,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))
|
||||
) : (
|
||||
<div className="grid place-items-center text-center text-sm text-custom-text-200">
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Link from "next/link";
|
||||
// hooks
|
||||
import {
|
||||
useApplication,
|
||||
@ -41,14 +42,11 @@ const CycleDropdownOption: React.FC<{ cycleId: string }> = ({ cycleId }) => {
|
||||
if (!cycle) return null;
|
||||
|
||||
return (
|
||||
<CustomMenu.MenuItem
|
||||
key={cycle.id}
|
||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`)}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CustomMenu.MenuItem key={cycle.id}>
|
||||
<Link href={`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`} className="flex items-center gap-1.5">
|
||||
<ContrastIcon className="h-3 w-3" />
|
||||
{truncateText(cycle.name, 40)}
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Link from "next/link";
|
||||
// hooks
|
||||
import {
|
||||
useApplication,
|
||||
@ -41,14 +42,14 @@ const ModuleDropdownOption: React.FC<{ moduleId: string }> = ({ moduleId }) => {
|
||||
if (!moduleDetail) return null;
|
||||
|
||||
return (
|
||||
<CustomMenu.MenuItem
|
||||
key={moduleDetail.id}
|
||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/modules/${moduleDetail.id}`)}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CustomMenu.MenuItem key={moduleDetail.id}>
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${projectId}/modules/${moduleDetail.id}`}
|
||||
className="flex items-center gap-1.5"
|
||||
>
|
||||
<DiceIcon className="h-3 w-3" />
|
||||
{truncateText(moduleDetail.name, 40)}
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
);
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import { useCallback } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Plus } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
// hooks
|
||||
import {
|
||||
useApplication,
|
||||
@ -154,14 +155,14 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
if (!view) return;
|
||||
|
||||
return (
|
||||
<CustomMenu.MenuItem
|
||||
key={viewId}
|
||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/views/${viewId}`)}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<CustomMenu.MenuItem key={viewId}>
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${projectId}/views/${viewId}`}
|
||||
className="flex items-center gap-1.5"
|
||||
>
|
||||
<PhotoFilterIcon height={12} width={12} />
|
||||
{truncateText(view.name, 40)}
|
||||
</div>
|
||||
</Link>
|
||||
</CustomMenu.MenuItem>
|
||||
);
|
||||
})}
|
||||
|
@ -2,6 +2,7 @@ import React from "react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { ArchiveRestore, Clock, MessageSquare, User2 } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// icons
|
||||
@ -10,17 +11,14 @@ import { ArchiveIcon, CustomMenu, Tooltip } from "@plane/ui";
|
||||
import { snoozeOptions } from "constants/notification";
|
||||
// helper
|
||||
import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper";
|
||||
import {
|
||||
calculateTimeAgo,
|
||||
renderFormattedTime,
|
||||
renderFormattedDate,
|
||||
} from "helpers/date-time.helper";
|
||||
import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper";
|
||||
// type
|
||||
import type { IUserNotification } from "@plane/types";
|
||||
|
||||
type NotificationCardProps = {
|
||||
notification: IUserNotification;
|
||||
isSnoozedTabOpen: boolean;
|
||||
closePopover: () => void;
|
||||
markNotificationReadStatus: (notificationId: string) => Promise<void>;
|
||||
markNotificationReadStatusToggle: (notificationId: string) => Promise<void>;
|
||||
markNotificationArchivedStatus: (notificationId: string) => Promise<void>;
|
||||
@ -32,6 +30,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
const {
|
||||
notification,
|
||||
isSnoozedTabOpen,
|
||||
closePopover,
|
||||
markNotificationReadStatus,
|
||||
markNotificationReadStatusToggle,
|
||||
markNotificationArchivedStatus,
|
||||
@ -47,15 +46,14 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
if (isSnoozedTabOpen && new Date(notification.snoozed_till!) < new Date()) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
<Link
|
||||
onClick={() => {
|
||||
markNotificationReadStatus(notification.id);
|
||||
router.push(
|
||||
`/${workspaceSlug}/projects/${notification.project}/${
|
||||
notification.data.issue_activity.field === "archived_at" ? "archived-issues" : "issues"
|
||||
}/${notification.data.issue.id}`
|
||||
);
|
||||
closePopover();
|
||||
}}
|
||||
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 ${
|
||||
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">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>
|
||||
Till {renderFormattedDate(notification.snoozed_till)}, {renderFormattedTime(notification.snoozed_till, '12-hour')}
|
||||
Till {renderFormattedDate(notification.snoozed_till)},{" "}
|
||||
{renderFormattedTime(notification.snoozed_till, "12-hour")}
|
||||
</span>
|
||||
</p>
|
||||
) : (
|
||||
@ -195,6 +194,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
item.onClick();
|
||||
}}
|
||||
key={item.id}
|
||||
@ -204,7 +205,6 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
</button>
|
||||
</Tooltip>
|
||||
))}
|
||||
|
||||
<Tooltip tooltipContent="Snooze">
|
||||
<CustomMenu
|
||||
className="flex items-center"
|
||||
@ -223,6 +223,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
key={item.label}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (!item.value) {
|
||||
setSelectedNotificationForSnooze(notification.id);
|
||||
@ -243,6 +244,6 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
</CustomMenu>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
@ -119,6 +119,7 @@ export const NotificationPopover = observer(() => {
|
||||
<NotificationCard
|
||||
key={notification.id}
|
||||
isSnoozedTabOpen={snoozed}
|
||||
closePopover={closePopover}
|
||||
notification={notification}
|
||||
markNotificationArchivedStatus={markNotificationArchivedStatus}
|
||||
markNotificationReadStatus={markNotificationAsRead}
|
||||
|
@ -2,6 +2,7 @@ import React, { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { LinkIcon, Lock, Pencil, Star } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
// hooks
|
||||
import { useProject } from "hooks/store";
|
||||
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 (
|
||||
<>
|
||||
@ -178,7 +179,7 @@ export const ProjectCard: React.FC<ProjectCardProps> = observer((props) => {
|
||||
}
|
||||
position="top"
|
||||
>
|
||||
{projectMembersIds.length > 0 ? (
|
||||
{projectMembersIds && projectMembersIds.length > 0 ? (
|
||||
<div className="flex cursor-pointer items-center gap-2 text-custom-text-200">
|
||||
<AvatarGroup showTooltip={false}>
|
||||
{projectMembersIds.map((memberId) => {
|
||||
@ -195,17 +196,15 @@ export const ProjectCard: React.FC<ProjectCardProps> = observer((props) => {
|
||||
)}
|
||||
</Tooltip>
|
||||
{(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"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
router.push(`/${workspaceSlug}/projects/${project.id}/settings`);
|
||||
}}
|
||||
href={`/${workspaceSlug}/projects/${project.id}/settings`}
|
||||
>
|
||||
<Pencil className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{!project.is_member ? (
|
||||
|
@ -45,28 +45,39 @@ type EmptySpaceItemProps = {
|
||||
title: string;
|
||||
description?: React.ReactNode | string;
|
||||
Icon: any;
|
||||
action: () => void;
|
||||
action?: () => void;
|
||||
href?: string;
|
||||
};
|
||||
|
||||
const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Icon, action }) => (
|
||||
<>
|
||||
<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="flex-shrink-0">
|
||||
<span className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-custom-primary">
|
||||
<Icon className="h-6 w-6 text-white" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1 text-custom-text-200">
|
||||
<div className="text-sm font-medium group-hover:text-custom-text-100">{title}</div>
|
||||
{description ? <div className="text-sm">{description}</div> : null}
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<ChevronRight className="h-5 w-5 text-custom-text-200 group-hover:text-custom-text-100" aria-hidden="true" />
|
||||
</div>
|
||||
const EmptySpaceItem: React.FC<EmptySpaceItemProps> = ({ title, description, Icon, action, href }) => {
|
||||
let spaceItem = (
|
||||
<div className={`group relative flex ${description ? "items-start" : "items-center"} space-x-3 py-4`}>
|
||||
<div className="flex-shrink-0">
|
||||
<span className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-custom-primary">
|
||||
<Icon className="h-6 w-6 text-white" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
<div className="min-w-0 flex-1 text-custom-text-200">
|
||||
<div className="text-sm font-medium group-hover:text-custom-text-100">{title}</div>
|
||||
{description ? <div className="text-sm">{description}</div> : null}
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<ChevronRight className="h-5 w-5 text-custom-text-200 group-hover:text-custom-text-100" aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (href) {
|
||||
spaceItem = <Link href={href}>{spaceItem}</Link>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<li className="cursor-pointer" onClick={action} role="button">
|
||||
{spaceItem}
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { EmptySpace, EmptySpaceItem };
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { FC, ReactNode } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Link from "next/link";
|
||||
// hooks
|
||||
import { useUser } from "hooks/store";
|
||||
// components
|
||||
@ -31,14 +32,12 @@ export const ProjectSettingLayout: FC<IProjectSettingLayout> = observer((props)
|
||||
<NotAuthorizedView
|
||||
type="project"
|
||||
actionButton={
|
||||
<Button
|
||||
variant="primary"
|
||||
size="md"
|
||||
prependIcon={<LayersIcon />}
|
||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/issues`)}
|
||||
>
|
||||
Go to issues
|
||||
</Button>
|
||||
//TODO: Create a new component called Button Link to handle such scenarios
|
||||
<Link href={`/${workspaceSlug}/projects/${projectId}/issues`}>
|
||||
<Button variant="primary" size="md" prependIcon={<LayersIcon />}>
|
||||
Go to issues
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
@ -3,6 +3,7 @@ import { useRouter } from "next/router";
|
||||
import Image from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Link from "next/link";
|
||||
// hooks
|
||||
import { useUser } from "hooks/store";
|
||||
// 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="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" />
|
||||
<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"
|
||||
onClick={() => router.push("/")}
|
||||
href="/"
|
||||
>
|
||||
<div className="h-[30px] w-[133px]">
|
||||
{theme === "light" ? (
|
||||
@ -50,7 +51,7 @@ const CreateWorkspacePage: NextPageWithLayout = observer(() => {
|
||||
<Image src={WhiteHorizontalLogo} alt="Plane white logo" />
|
||||
)}
|
||||
</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">
|
||||
{currentUser?.email}
|
||||
</div>
|
||||
|
@ -81,7 +81,7 @@ const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
|
||||
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."
|
||||
>
|
||||
<EmptySpaceItem Icon={Boxes} title="Continue to Dashboard" action={() => router.push("/")} />
|
||||
<EmptySpaceItem Icon={Boxes} title="Continue to Dashboard" href="/" />
|
||||
</EmptySpace>
|
||||
</>
|
||||
) : (
|
||||
@ -103,35 +103,15 @@ const WorkspaceInvitationPage: NextPageWithLayout = observer(() => {
|
||||
link={{ text: "Or start from an empty project", href: "/" }}
|
||||
>
|
||||
{!currentUser ? (
|
||||
<EmptySpaceItem
|
||||
Icon={User2}
|
||||
title="Sign in to continue"
|
||||
action={() => {
|
||||
router.push("/");
|
||||
}}
|
||||
/>
|
||||
<EmptySpaceItem Icon={User2} title="Sign in to continue" href="/" />
|
||||
) : (
|
||||
<EmptySpaceItem
|
||||
Icon={Boxes}
|
||||
title="Continue to Dashboard"
|
||||
action={() => {
|
||||
router.push("/");
|
||||
}}
|
||||
/>
|
||||
<EmptySpaceItem Icon={Boxes} title="Continue to Dashboard" href="/" />
|
||||
)}
|
||||
<EmptySpaceItem
|
||||
Icon={Star}
|
||||
title="Star us on GitHub"
|
||||
action={() => {
|
||||
router.push("https://github.com/makeplane");
|
||||
}}
|
||||
/>
|
||||
<EmptySpaceItem Icon={Star} title="Star us on GitHub" href="https://github.com/makeplane" />
|
||||
<EmptySpaceItem
|
||||
Icon={Share2}
|
||||
title="Join our community of active creators"
|
||||
action={() => {
|
||||
router.push("https://discord.com/invite/8SR2N9PAcJ");
|
||||
}}
|
||||
href="https://discord.com/invite/8SR2N9PAcJ"
|
||||
/>
|
||||
</EmptySpace>
|
||||
) : (
|
||||
|
@ -14,7 +14,7 @@ import { CycleService } from "services/cycle.service";
|
||||
export interface ICycleStore {
|
||||
// observables
|
||||
cycleMap: Record<string, ICycle>;
|
||||
activeCycleMap: Record<string, ICycle>; // TODO: Merge these two into single map
|
||||
activeCycleIdMap: Record<string, boolean>;
|
||||
// computed
|
||||
currentProjectCycleIds: string[] | null;
|
||||
currentProjectCompletedCycleIds: string[] | null;
|
||||
@ -49,7 +49,7 @@ export interface ICycleStore {
|
||||
export class CycleStore implements ICycleStore {
|
||||
// observables
|
||||
cycleMap: Record<string, ICycle> = {};
|
||||
activeCycleMap: Record<string, ICycle> = {};
|
||||
activeCycleIdMap: Record<string, boolean> = {};
|
||||
// root store
|
||||
rootStore;
|
||||
// services
|
||||
@ -61,7 +61,7 @@ export class CycleStore implements ICycleStore {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
cycleMap: observable,
|
||||
activeCycleMap: observable,
|
||||
activeCycleIdMap: observable,
|
||||
// computed
|
||||
currentProjectCycleIds: computed,
|
||||
currentProjectCompletedCycleIds: computed,
|
||||
@ -168,8 +168,8 @@ export class CycleStore implements ICycleStore {
|
||||
get currentProjectActiveCycleId() {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId) return null;
|
||||
const activeCycle = Object.keys(this.activeCycleMap ?? {}).find(
|
||||
(cycleId) => this.activeCycleMap?.[cycleId]?.project === projectId
|
||||
const activeCycle = Object.keys(this.activeCycleIdMap ?? {}).find(
|
||||
(cycleId) => this.cycleMap?.[cycleId]?.project === projectId
|
||||
);
|
||||
return activeCycle || null;
|
||||
}
|
||||
@ -186,7 +186,8 @@ export class CycleStore implements ICycleStore {
|
||||
* @param cycleId
|
||||
* @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
|
||||
@ -235,7 +236,8 @@ export class CycleStore implements ICycleStore {
|
||||
await this.cycleService.getCyclesWithParams(workspaceSlug, projectId, "current").then((response) => {
|
||||
runInAction(() => {
|
||||
response.forEach((cycle) => {
|
||||
set(this.activeCycleMap, [cycle.id], cycle);
|
||||
set(this.activeCycleIdMap, [cycle.id], true);
|
||||
set(this.cycleMap, [cycle.id], cycle);
|
||||
});
|
||||
});
|
||||
return response;
|
||||
@ -252,7 +254,6 @@ export class CycleStore implements ICycleStore {
|
||||
await this.cycleService.getCycleDetails(workspaceSlug, projectId, cycleId).then((response) => {
|
||||
runInAction(() => {
|
||||
set(this.cycleMap, [response.id], { ...this.cycleMap?.[response.id], ...response });
|
||||
set(this.activeCycleMap, [response.id], { ...this.activeCycleMap?.[response.id], ...response });
|
||||
});
|
||||
return response;
|
||||
});
|
||||
@ -268,7 +269,6 @@ export class CycleStore implements ICycleStore {
|
||||
await this.cycleService.createCycle(workspaceSlug, projectId, data).then((response) => {
|
||||
runInAction(() => {
|
||||
set(this.cycleMap, [response.id], response);
|
||||
set(this.activeCycleMap, [response.id], response);
|
||||
});
|
||||
return response;
|
||||
});
|
||||
@ -285,7 +285,6 @@ export class CycleStore implements ICycleStore {
|
||||
try {
|
||||
runInAction(() => {
|
||||
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);
|
||||
return response;
|
||||
@ -307,7 +306,7 @@ export class CycleStore implements ICycleStore {
|
||||
await this.cycleService.deleteCycle(workspaceSlug, projectId, cycleId).then(() => {
|
||||
runInAction(() => {
|
||||
delete this.cycleMap[cycleId];
|
||||
delete this.activeCycleMap[cycleId];
|
||||
delete this.activeCycleIdMap[cycleId];
|
||||
});
|
||||
});
|
||||
|
||||
@ -324,7 +323,6 @@ export class CycleStore implements ICycleStore {
|
||||
try {
|
||||
runInAction(() => {
|
||||
if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], true);
|
||||
if (currentActiveCycle) set(this.activeCycleMap, [cycleId, "is_favorite"], true);
|
||||
});
|
||||
// updating through api.
|
||||
const response = await this.cycleService.addCycleToFavorites(workspaceSlug, projectId, { cycle: cycleId });
|
||||
@ -332,7 +330,6 @@ export class CycleStore implements ICycleStore {
|
||||
} catch (error) {
|
||||
runInAction(() => {
|
||||
if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], false);
|
||||
if (currentActiveCycle) set(this.activeCycleMap, [cycleId, "is_favorite"], false);
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
@ -351,14 +348,12 @@ export class CycleStore implements ICycleStore {
|
||||
try {
|
||||
runInAction(() => {
|
||||
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);
|
||||
return response;
|
||||
} catch (error) {
|
||||
runInAction(() => {
|
||||
if (currentCycle) set(this.cycleMap, [cycleId, "is_favorite"], true);
|
||||
if (currentActiveCycle) set(this.activeCycleMap, [cycleId, "is_favorite"], true);
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user