promote: develop to stage-release (#1589)
* fix: onboarding invitations overflow (#1575) * fix: onboarding invitations overflow * fix: user avatar in the notification card * style: update graph grid color * fix: no 'Create by me' label coming up (#1573) * feat: added new issue subscriber table * dev: notification model * feat: added CRUD operation for issue subscriber * Revert "feat: added CRUD operation for issue subscriber" This reverts commit b22e0625768f0b096b5898936ace76d6882b0736. * feat: added CRUD operation for issue subscriber * dev: notification models and operations * dev: remove delete endpoint response data * dev: notification endpoints and fix bg worker for saving notifications * feat: added list and unsubscribe function in issue subscriber * dev: filter by snoozed and response update for list and permissions * dev: update issue notifications * dev: notification segregation * dev: update notifications * dev: notification filtering * dev: add issue name in notifications * dev: notification new endpoints * fix: pushing local settings * feat: notification workflow setup and made basic UI * style: improved UX with toast alerts and other interactions refactor: changed classnames according to new theme structure, changed all icons to material icons * feat: showing un-read notification count * feat: not showing 'subscribe' button on issue created by user & assigned to user not showing 'Create by you' for view & guest of the workspace * fix: 'read' -> 'unread' heading, my issue wrong filter * feat: made snooze dropdown & modal feat: switched to calendar * fix: minor ui fixes * feat: snooze modal date/time select * fix: params for read/un-read notification * style: snooze notification modal * fix: no label for 'Create by me' * fix: no label for 'Create by me' * fix: removed console log * fix: tooltip going behind popover --------- Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> * style: tooltip on notification header actions (#1577) * style: tooltip on notification header * chore: update tooltip content --------- Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> * fix: user migrations for back population (#1578) * fix: total notifications count (#1579) * fix: notification card (#1583) * feat: add new icons package (#1586) * feat: add material icons package * chore: replace issue view icons * chore: notification ordering (#1584) * fix: uuid error when cycle and module updates (#1585) * refactor: height of popover & api fetch call (#1587) * fix: snooze dropdown overflow (#1588) --------- Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com>
This commit is contained in:
parent
b38898753f
commit
9275e6f373
28 changed files with 736 additions and 1018 deletions
|
|
@ -1,33 +1,16 @@
|
|||
import React, { useRef, useState } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import Link from "next/link";
|
||||
|
||||
import useSWR from "swr";
|
||||
|
||||
// headless ui
|
||||
import { Transition } from "@headlessui/react";
|
||||
// services
|
||||
import workspaceService from "services/workspace.service";
|
||||
// hooks
|
||||
import useTheme from "hooks/use-theme";
|
||||
import useUser from "hooks/use-user";
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
// components
|
||||
import UpgradeToProModal from "./upgrade-to-pro-modal";
|
||||
// ui
|
||||
import { CircularProgress, Icon } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
ArrowLongLeftIcon,
|
||||
ChatBubbleOvalLeftEllipsisIcon,
|
||||
ArrowUpCircleIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_DETAILS } from "constants/fetch-keys";
|
||||
import { Bolt, HelpOutlineOutlined, WestOutlined } from "@mui/icons-material";
|
||||
import { ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline";
|
||||
import { DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
|
||||
|
||||
const helpOptions = [
|
||||
{
|
||||
|
|
@ -58,150 +41,74 @@ export interface WorkspaceHelpSectionProps {
|
|||
}
|
||||
|
||||
export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = ({ setSidebarActive }) => {
|
||||
const [alert, setAlert] = useState(false);
|
||||
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
|
||||
|
||||
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
|
||||
|
||||
useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false));
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const [upgradeModal, setUpgradeModal] = useState(false);
|
||||
|
||||
const { data: workspaceDetails } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
|
||||
workspaceSlug ? () => workspaceService.getWorkspace(workspaceSlug as string) : null
|
||||
);
|
||||
|
||||
const issueNumber = workspaceDetails?.total_issues || 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<UpgradeToProModal
|
||||
isOpen={upgradeModal}
|
||||
onClose={() => setUpgradeModal(false)}
|
||||
user={user}
|
||||
issueNumber={issueNumber}
|
||||
/>
|
||||
{!sidebarCollapse && (alert || issueNumber >= 750) && (
|
||||
<div
|
||||
className={`border-t border-custom-sidebar-border-200 p-4 ${
|
||||
issueNumber >= 750
|
||||
? "bg-red-500/10 text-red-600"
|
||||
: issueNumber >= 500
|
||||
? "bg-yellow-500/10 text-yellow-600"
|
||||
: "text-green-600"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<CircularProgress progress={(issueNumber / 1024) * 100} />
|
||||
<div>Free Plan</div>
|
||||
{issueNumber < 750 && (
|
||||
<div
|
||||
className="ml-auto text-custom-text-200 cursor-pointer"
|
||||
onClick={() => setAlert(false)}
|
||||
>
|
||||
<XMarkIcon className="h-4 w-4" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-custom-text-200 text-xs mt-2">
|
||||
This workspace has used {issueNumber} of its 1024 issues creation limit (
|
||||
{((issueNumber / 1024) * 100).toFixed(0)}
|
||||
%).
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-border-200 bg-custom-sidebar-background-100 px-4 py-2 ${
|
||||
className={`flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-border-200 bg-custom-sidebar-background-100 py-2 px-4 ${
|
||||
sidebarCollapse ? "flex-col" : ""
|
||||
}`}
|
||||
>
|
||||
{alert || issueNumber >= 750 ? (
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
|
||||
issueNumber >= 750
|
||||
? "bg-red-500/10 text-red-500"
|
||||
: "bg-blue-500/10 text-custom-primary-100"
|
||||
} ${sidebarCollapse ? "w-full justify-center" : ""}`}
|
||||
title="Shortcuts"
|
||||
onClick={() => setUpgradeModal(true)}
|
||||
>
|
||||
<ArrowUpCircleIcon className="h-4 w-4" />
|
||||
{!sidebarCollapse && <span>Learn more</span>}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
|
||||
issueNumber >= 750
|
||||
? "bg-red-500/10 text-red-600"
|
||||
: issueNumber >= 500
|
||||
? "bg-yellow-500/10 text-yellow-600"
|
||||
: "bg-green-500/10 text-green-600"
|
||||
}
|
||||
${sidebarCollapse ? "w-full justify-center" : ""}`}
|
||||
title="Shortcuts"
|
||||
onClick={() => setAlert(true)}
|
||||
>
|
||||
<CircularProgress
|
||||
progress={(issueNumber / 1024) * 100 > 100 ? 100 : (issueNumber / 1024) * 100}
|
||||
/>
|
||||
{!sidebarCollapse && <span>Free Plan</span>}
|
||||
</button>
|
||||
{!sidebarCollapse && (
|
||||
<div className="w-1/2 text-center rounded-md px-2.5 py-1.5 font-medium outline-none text-sm bg-green-500/10 text-green-500">
|
||||
Free Plan
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
|
||||
sidebarCollapse ? "w-full justify-center" : ""
|
||||
<div
|
||||
className={`flex items-center gap-1 ${
|
||||
sidebarCollapse ? "flex-col justify-center" : "justify-evenly w-1/2"
|
||||
}`}
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "h",
|
||||
});
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
title="Shortcuts"
|
||||
>
|
||||
<Icon iconName="bolt" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
|
||||
sidebarCollapse ? "w-full justify-center" : ""
|
||||
}`}
|
||||
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
|
||||
title="Help"
|
||||
>
|
||||
<QuestionMarkCircleIcon className="h-4 w-4 text-custom-text-200" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 md:hidden"
|
||||
onClick={() => setSidebarActive(false)}
|
||||
>
|
||||
<ArrowLongLeftIcon className="h-4 w-4 flex-shrink-0 text-custom-text-200 group-hover:text-custom-text-100" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`hidden items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 md:flex ${
|
||||
sidebarCollapse ? "w-full justify-center" : ""
|
||||
}`}
|
||||
onClick={() => toggleCollapsed()}
|
||||
>
|
||||
<ArrowLongLeftIcon
|
||||
className={`h-4 w-4 flex-shrink-0 text-custom-text-200 duration-300 group-hover:text-custom-text-100 ${
|
||||
sidebarCollapse ? "rotate-180" : ""
|
||||
<button
|
||||
type="button"
|
||||
className={`rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
|
||||
sidebarCollapse ? "w-full" : ""
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "h",
|
||||
});
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
>
|
||||
<Bolt fontSize="small" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
|
||||
sidebarCollapse ? "w-full" : ""
|
||||
}`}
|
||||
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
|
||||
>
|
||||
<HelpOutlineOutlined fontSize="small" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none md:hidden"
|
||||
onClick={() => setSidebarActive(false)}
|
||||
>
|
||||
<WestOutlined fontSize="small" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`hidden md:flex rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
|
||||
sidebarCollapse ? "w-full" : ""
|
||||
}`}
|
||||
onClick={() => toggleCollapsed()}
|
||||
>
|
||||
<WestOutlined
|
||||
fontSize="small"
|
||||
className={`duration-300 ${sidebarCollapse ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<Transition
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const userLinks = (workspaceSlug: string) => [
|
|||
export const WorkspaceSidebarDropdown = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
// fetching user details
|
||||
|
||||
const { user, mutateUser } = useUser();
|
||||
|
||||
const { collapsed: sidebarCollapse } = useThemeHook();
|
||||
|
|
@ -139,8 +139,8 @@ export const WorkspaceSidebarDropdown = () => {
|
|||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
className="fixed left-2 z-20 mt-1 flex w-full max-w-[17rem] origin-top-left flex-col rounded-md
|
||||
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 shadow-lg focus:outline-none"
|
||||
className="fixed left-4 z-20 mt-1 flex flex-col w-full max-w-[17rem] origin-top-left rounded-md
|
||||
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 shadow-lg outline-none"
|
||||
>
|
||||
<div className="flex flex-col items-start justify-start gap-3 p-3">
|
||||
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div>
|
||||
|
|
|
|||
|
|
@ -5,40 +5,45 @@ import { useRouter } from "next/router";
|
|||
|
||||
// hooks
|
||||
import useTheme from "hooks/use-theme";
|
||||
|
||||
// components
|
||||
import { NotificationPopover } from "components/notifications";
|
||||
// ui
|
||||
import { Tooltip } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
BarChartRounded,
|
||||
GridViewOutlined,
|
||||
TaskAltOutlined,
|
||||
WorkOutlineOutlined,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const workspaceLinks = (workspaceSlug: string) => [
|
||||
{
|
||||
icon: "grid_view",
|
||||
Icon: GridViewOutlined,
|
||||
name: "Dashboard",
|
||||
href: `/${workspaceSlug}`,
|
||||
},
|
||||
{
|
||||
icon: "bar_chart",
|
||||
Icon: BarChartRounded,
|
||||
name: "Analytics",
|
||||
href: `/${workspaceSlug}/analytics`,
|
||||
},
|
||||
{
|
||||
icon: "work",
|
||||
Icon: WorkOutlineOutlined,
|
||||
name: "Projects",
|
||||
href: `/${workspaceSlug}/projects`,
|
||||
},
|
||||
{
|
||||
icon: "task_alt",
|
||||
Icon: TaskAltOutlined,
|
||||
name: "My Issues",
|
||||
href: `/${workspaceSlug}/me/my-issues`,
|
||||
},
|
||||
];
|
||||
|
||||
// components
|
||||
import { Icon, Tooltip } from "components/ui";
|
||||
|
||||
export const WorkspaceSidebarMenu = () => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
// theme context
|
||||
const { collapsed: sidebarCollapse } = useTheme();
|
||||
|
||||
return (
|
||||
|
|
@ -65,7 +70,7 @@ export const WorkspaceSidebarMenu = () => {
|
|||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
||||
} ${sidebarCollapse ? "justify-center" : ""}`}
|
||||
>
|
||||
<Icon iconName={`${link.icon}`} />
|
||||
{<link.Icon fontSize="small" />}
|
||||
{!sidebarCollapse && link.name}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -1,248 +0,0 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { XCircleIcon, RocketLaunchIcon } from "@heroicons/react/24/outline";
|
||||
import { CheckCircleIcon } from "@heroicons/react/24/solid";
|
||||
// ui
|
||||
import { CircularProgress } from "components/ui";
|
||||
// types
|
||||
import type { ICurrentUserResponse, IWorkspace } from "types";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
supabase: any;
|
||||
}
|
||||
}
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
user: ICurrentUserResponse | undefined;
|
||||
issueNumber: number;
|
||||
};
|
||||
|
||||
const UpgradeToProModal: React.FC<Props> = ({ isOpen, onClose, user, issueNumber }) => {
|
||||
const [supabaseClient, setSupabaseClient] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Create a Supabase client
|
||||
if (process.env.NEXT_PUBLIC_SUPABASE_URL && process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) {
|
||||
const { createClient } = window.supabase;
|
||||
const supabase = createClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
||||
{
|
||||
auth: {
|
||||
autoRefreshToken: false,
|
||||
persistSession: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (supabase) {
|
||||
setSupabaseClient(supabase);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const proFeatures = [
|
||||
"Everything in free",
|
||||
"Unlimited users",
|
||||
"Unlimited file uploads",
|
||||
"Priority Support",
|
||||
"Custom Theming",
|
||||
"Access to Roadmap",
|
||||
"Plane AI (GPT unlimited)",
|
||||
];
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState<null | { status: String; message: string }>(
|
||||
null
|
||||
);
|
||||
const [loader, setLoader] = useState(false);
|
||||
const submitEmail = async () => {
|
||||
setLoader(true);
|
||||
const payload = { email: user?.email || "" };
|
||||
|
||||
if (supabaseClient) {
|
||||
if (payload?.email) {
|
||||
const emailExists = await supabaseClient
|
||||
.from("web-waitlist")
|
||||
.select("id,email,count")
|
||||
.eq("email", payload?.email);
|
||||
if (emailExists.data.length === 0) {
|
||||
const emailCreation = await supabaseClient
|
||||
.from("web-waitlist")
|
||||
.insert([{ email: payload?.email, count: 1, last_visited: new Date() }])
|
||||
.select("id,email,count");
|
||||
if (emailCreation.status === 201)
|
||||
setErrorMessage({ status: "success", message: "Successfully registered." });
|
||||
else setErrorMessage({ status: "insert_error", message: "Insertion Error." });
|
||||
} else {
|
||||
const emailCountUpdate = await supabaseClient
|
||||
.from("web-waitlist")
|
||||
.upsert({
|
||||
id: emailExists.data[0]?.id,
|
||||
count: emailExists.data[0]?.count + 1,
|
||||
last_visited: new Date(),
|
||||
})
|
||||
.select("id,email,count");
|
||||
if (emailCountUpdate.status === 201)
|
||||
setErrorMessage({
|
||||
status: "email_already_exists",
|
||||
message: "Email already exists.",
|
||||
});
|
||||
else setErrorMessage({ status: "update_error", message: "Update Error." });
|
||||
}
|
||||
} else setErrorMessage({ status: "email_required", message: "Please provide email." });
|
||||
} else
|
||||
setErrorMessage({
|
||||
status: "supabase_error",
|
||||
message: "Network error. Please try again later.",
|
||||
});
|
||||
|
||||
setLoader(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-custom-backdrop bg-opacity-50 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-20 overflow-y-auto">
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-custom-border-200 bg-custom-background-100 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-4xl">
|
||||
<div className="flex flex-wrap">
|
||||
<div className="w-full md:w-3/5 p-6 flex flex-col gap-y-6">
|
||||
<div className="flex gap-2">
|
||||
<div
|
||||
className={`font-semibold outline-none text-sm mt-1.5 ${
|
||||
issueNumber >= 750
|
||||
? "text-red-600"
|
||||
: issueNumber >= 500
|
||||
? "text-yellow-600"
|
||||
: "text-green-600"
|
||||
}`}
|
||||
title="Shortcuts"
|
||||
>
|
||||
<CircularProgress
|
||||
progress={
|
||||
(issueNumber / 1024) * 100 > 100 ? 100 : (issueNumber / 1024) * 100
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="font-semibold text-lg">Upgrade to pro</div>
|
||||
<div className="text-custom-text-200 text-sm">
|
||||
This workspace has used {issueNumber} of its 1024 issues creation limit (
|
||||
{((issueNumber / 1024) * 100).toFixed(2)}%).
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
onClick={handleClose}
|
||||
className="w-5 h-5 text-custom-text-200 cursor-pointer mt-1.5 md:hidden block ml-auto"
|
||||
>
|
||||
<XCircleIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2 pt-6">
|
||||
<div
|
||||
className={`font-semibold outline-none text-sm mt-1.5 w-5 h-5 text-[#892FFF] flex-shrink-0`}
|
||||
title="Shortcuts"
|
||||
>
|
||||
<RocketLaunchIcon />
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="font-semibold text-lg">Order summary</div>
|
||||
<div className="text-custom-text-200 text-sm">
|
||||
Priority support, file uploads, and access to premium features.
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap my-4">
|
||||
{proFeatures.map((feature, index) => (
|
||||
<div key={index} className="w-1/2 py-2 flex gap-2 my-1.5">
|
||||
<div className="w-5 h-5 mt-0.5 text-green-600">
|
||||
<CheckCircleIcon />
|
||||
</div>
|
||||
<div>{feature}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full md:w-2/5 bg-custom-background-90 p-6 flex flex-col">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="font-semibold text-lg">Summary</div>
|
||||
<div
|
||||
onClick={handleClose}
|
||||
className="w-5 h-5 text-custom-text-200 cursor-pointer mt-1.5 hidden md:block"
|
||||
>
|
||||
<XCircleIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-custom-text-200 text-sm mt-4">
|
||||
Plane application is currently in dev-mode. We will soon introduce Pro plans
|
||||
once general availability has been established. Stay tuned for more updates.
|
||||
In the meantime, Plane remains free and unrestricted.
|
||||
<br /> <br />
|
||||
We{"'"}ll ensure a smooth transition from the community version to the Pro
|
||||
plan for you.
|
||||
</div>
|
||||
<button
|
||||
disabled={loader}
|
||||
onClick={() => submitEmail()}
|
||||
type="button"
|
||||
className="mt-5 md:mt-auto whitespace-nowrap max-w-min items-center gap-x-1 rounded-md px-3 py-2 font-medium outline-none text-sm bg-custom-primary-100 text-white"
|
||||
>
|
||||
{loader ? "Loading.." : " Join waitlist"}
|
||||
</button>
|
||||
{errorMessage && (
|
||||
<div
|
||||
className={`mt-1 text-sm ${
|
||||
errorMessage && errorMessage?.status === "success"
|
||||
? "text-green-500"
|
||||
: " text-red-500"
|
||||
}`}
|
||||
>
|
||||
{errorMessage?.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpgradeToProModal;
|
||||
Loading…
Add table
Add a link
Reference in a new issue