Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: web waitlist modal integration #1487

Merged
merged 16 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions apps/app/components/ui/circular-progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useEffect, useState } from "react";

export const CircularProgress = ({ progress }: { progress: number }) => {
const [circumference, setCircumference] = useState(0);

useEffect(() => {
const radius = 40;
const calcCircumference = 2 * Math.PI * radius;
setCircumference(calcCircumference);
}, []);

const progressAngle = (progress / 100) * 360 >= 360 ? 359.9 : (progress / 100) * 360;
const progressX = 50 + Math.cos((progressAngle - 90) * (Math.PI / 180)) * 40;
const progressY = 50 + Math.sin((progressAngle - 90) * (Math.PI / 180)) * 40;

return (
<div className="relative h-5 w-5">
<svg className="absolute top-0 left-0" viewBox="0 0 100 100">
<circle
className="stroke-current"
cx="50"
cy="50"
r="40"
strokeWidth="12"
fill="none"
strokeDasharray={`${circumference} ${circumference}`}
/>
<path
className="fill-current"
d={`M50 10
A40 40 0 ${progress > 50 ? 1 : 0} 1 ${progressX} ${progressY}
L50 50 Z`}
strokeWidth="12"
strokeLinecap="round"
/>
</svg>
</div>
);
};
1 change: 1 addition & 0 deletions apps/app/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export * from "./product-updates-modal";
export * from "./integration-and-import-export-banner";
export * from "./range-datepicker";
export * from "./icon";
export * from "./circular-progress";
285 changes: 197 additions & 88 deletions apps/app/components/workspace/help-section.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useState, useRef, FC } from "react";

import { useRouter } from "next/router";

import Link from "next/link";

import useSWR from "swr";

// headless ui
import { Transition } from "@headlessui/react";
// hooks
Expand All @@ -12,8 +16,19 @@ import {
ArrowLongLeftIcon,
ChatBubbleOvalLeftEllipsisIcon,
RocketLaunchIcon,
ArrowUpCircleIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
// services
import workspaceService from "services/workspace.service";
// fetch-keys
import { WORKSPACE_DETAILS } from "constants/fetch-keys";
// ui
import { CircularProgress } from "components/ui";
// components
import UpgradeToProModal from "./upgrade-to-pro-modal";
import useUser from "hooks/use-user";

const helpOptions = [
{
Expand Down Expand Up @@ -43,7 +58,14 @@ export interface WorkspaceHelpSectionProps {
setSidebarActive: React.Dispatch<React.SetStateAction<boolean>>;
}

type progress = {
progress: number;
};
export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
// router
const router = useRouter();
const { workspaceSlug } = router.query;

const { setSidebarActive } = props;
// theme
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
Expand All @@ -54,105 +76,192 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
// hooks
useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false));

const { user } = useUser();

const helpOptionMode = sidebarCollapse ? "left-full" : "left-[-75px]";

const [alert, setAlert] = useState(false);

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 (
<div
className={`flex w-full items-center justify-between self-baseline border-t border-custom-sidebar-border-100 bg-custom-sidebar-background-100 px-6 py-2 ${
sidebarCollapse ? "flex-col" : ""
}`}
>
<button
type="button"
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-sidebar-text-200 outline-none hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 ${
sidebarCollapse ? "w-full justify-center" : ""
}`}
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "h",
});
document.dispatchEvent(e);
}}
title="Shortcuts"
>
<RocketLaunchIcon className="h-4 w-4 text-custom-sidebar-text-200" />
{!sidebarCollapse && <span>Shortcuts</span>}
</button>
<button
type="button"
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-sidebar-text-200 outline-none hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 ${
sidebarCollapse ? "w-full justify-center" : ""
}`}
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
title="Help"
>
<QuestionMarkCircleIcon className="h-4 w-4 text-custom-sidebar-text-200" />
{!sidebarCollapse && <span>Help</span>}
</button>
<button
type="button"
className="flex items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-sidebar-text-200 outline-none hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 md:hidden"
onClick={() => setSidebarActive(false)}
>
<ArrowLongLeftIcon className="h-4 w-4 flex-shrink-0 text-custom-sidebar-text-200 group-hover:text-custom-sidebar-text-100" />
</button>
<button
type="button"
className={`hidden items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-sidebar-text-200 outline-none hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 md:flex ${
sidebarCollapse ? "w-full justify-center" : ""
<>
<UpgradeToProModal
isOpen={upgradeModal}
onClose={() => setUpgradeModal(false)}
user={user}
issueNumber={issueNumber}
/>
{!sidebarCollapse && (alert || (issueNumber && issueNumber >= 750)) ? (
<>
<div
className={`border-t p-4 ${
issueNumber >= 750
? "bg-red-50 text-red-600 border-red-200"
: issueNumber >= 500
? "bg-yellow-50 text-yellow-600 border-yellow-200"
: "text-green-600"
}`}
>
<div className="flex items-center gap-2 w-full">
<CircularProgress progress={(issueNumber / 1024) * 100} />
<div className="">Free Plan</div>
{issueNumber < 750 && (
<div className="ml-auto text-custom-text-200" 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 self-baseline border-t border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-2 ${
sidebarCollapse ? "flex-col" : ""
}`}
onClick={() => toggleCollapsed()}
>
<ArrowLongLeftIcon
className={`h-4 w-4 flex-shrink-0 text-custom-sidebar-text-200 duration-300 group-hover:text-custom-sidebar-text-100 ${
sidebarCollapse ? "rotate-180" : ""
{alert || (issueNumber && issueNumber >= 750) ? (
<button
type="button"
className={`flex items-center gap-x-1 rounded-md px-2 py-2 font-medium outline-none text-sm
${
issueNumber >= 750
? "bg-custom-primary-100 text-white"
: "bg-blue-50 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-x-1 rounded-md px-2 py-2 font-medium outline-none text-sm ${
issueNumber >= 750
? "bg-red-50 text-red-600"
: issueNumber >= 500
? "bg-yellow-50 text-yellow-600"
: "bg-green-50 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>
)}

<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" : ""
}`}
/>
</button>

<div className="relative">
<Transition
show={isNeedHelpOpen}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: "h",
});
document.dispatchEvent(e);
}}
title="Shortcuts"
>
<div
className={`absolute bottom-2 ${helpOptionMode} space-y-2 rounded-sm bg-custom-sidebar-background-80 p-1 shadow-md`}
ref={helpOptionsRef}
<RocketLaunchIcon className="h-4 w-4 text-custom-text-200" />
{/* {!sidebarCollapse && <span>Shortcuts</span>} */}
</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" />
{/* {!sidebarCollapse && <span>Help</span>} */}
</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>

<div className="relative">
<Transition
show={isNeedHelpOpen}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
{helpOptions.map(({ name, Icon, href, onClick }) => {
if (href)
return (
<Link href={href} key={name}>
<a
target="_blank"
className="flex items-center gap-x-2 whitespace-nowrap rounded-md px-2 py-1 text-xs hover:bg-custom-sidebar-background-90"
<div
className={`absolute bottom-2 ${helpOptionMode} space-y-2 rounded-sm bg-custom-background-80 p-1 shadow-md`}
ref={helpOptionsRef}
>
{helpOptions.map(({ name, Icon, href, onClick }) => {
if (href)
return (
<Link href={href} key={name}>
<a
target="_blank"
className="flex items-center gap-x-2 whitespace-nowrap rounded-md px-2 py-1 text-xs hover:bg-custom-background-90"
>
<Icon className="h-4 w-4 text-custom-text-200" />
<span className="text-sm">{name}</span>
</a>
</Link>
);
else
return (
<button
key={name}
type="button"
onClick={onClick ? onClick : undefined}
className="flex w-full items-center gap-x-2 whitespace-nowrap rounded-md px-2 py-1 text-xs hover:bg-custom-background-90"
>
<Icon className="h-4 w-4 text-custom-sidebar-text-200" />
<span className="text-sm">{name}</span>
</a>
</Link>
);
else
return (
<button
key={name}
type="button"
onClick={onClick ? onClick : undefined}
className="flex w-full items-center gap-x-2 whitespace-nowrap rounded-md px-2 py-1 text-xs hover:bg-custom-sidebar-background-90"
>
<Icon className="h-4 w-4 text-custom-sidebar-text-200" />
<span className="text-sm">{name}</span>
</button>
);
})}
</div>
</Transition>
</button>
);
})}
</div>
</Transition>
</div>
</div>
</div>
</>
);
};
Loading