mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-12-14 17:03:29 +03:00
feat: upgrade button in user settings (#1484)
# Description Epic: #1429 User Story: #1431 - Add an upgrade button in user settings. - Remove hover links on sidebar buttons (otherwise the link could partially hide the button) ## Screenshots (if appropriate): <img width="749" alt="image" src="https://github.com/StanGirard/quivr/assets/67386567/6265ba2b-8d91-4ee8-abb3-98417ad91076"> <img width="803" alt="image" src="https://github.com/StanGirard/quivr/assets/67386567/c13ce60b-a54d-44d7-a622-bcb1200ddb81">
This commit is contained in:
parent
7038cddd2f
commit
ee7af51c4d
@ -17,7 +17,7 @@ export const BrainsList = (): JSX.Element => {
|
||||
const { t } = useTranslation(["brain", "chat"]);
|
||||
|
||||
return (
|
||||
<Sidebar showButtons={["user", "upgradeToPlus"]}>
|
||||
<Sidebar showButtons={["upgradeToPlus", "user"]}>
|
||||
<div className="flex flex-col p-2 gap-2">
|
||||
<Link href="/chat">
|
||||
<Button type="button" className="bg-primary text-white py-2 w-full">
|
||||
|
@ -15,7 +15,7 @@ export const ChatsList = (): JSX.Element => {
|
||||
const { shouldDisplayWelcomeChat } = useOnboarding();
|
||||
|
||||
return (
|
||||
<Sidebar showButtons={["myBrains", "user", "upgradeToPlus"]}>
|
||||
<Sidebar showButtons={["myBrains", "upgradeToPlus", "user"]}>
|
||||
<div className="flex flex-col flex-1 h-full" data-testid="chats-list">
|
||||
<div className="pt-2">
|
||||
<NewChatButton />
|
||||
|
@ -3,9 +3,11 @@
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { StripePricingModal } from "@/lib/components/Stripe";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Card, { CardBody, CardHeader } from "@/lib/components/ui/Card";
|
||||
import { useSupabase } from "@/lib/context/SupabaseProvider";
|
||||
import { useUserData } from "@/lib/hooks/useUserData";
|
||||
import { redirectToLogin } from "@/lib/router/redirectToLogin";
|
||||
|
||||
import { UserStatistics } from "./components";
|
||||
@ -15,13 +17,20 @@ import ThemeSelect from "./components/ThemeSelect/ThemeSelect";
|
||||
|
||||
const UserPage = (): JSX.Element => {
|
||||
const { session } = useSupabase();
|
||||
const { userData } = useUserData();
|
||||
const is_premium = userData?.is_premium;
|
||||
|
||||
if (!session) {
|
||||
redirectToLogin();
|
||||
}
|
||||
|
||||
const { user } = session;
|
||||
const { t } = useTranslation(["translation", "user", "config"]);
|
||||
const { t } = useTranslation([
|
||||
"translation",
|
||||
"user",
|
||||
"config",
|
||||
"monetization",
|
||||
]);
|
||||
|
||||
return (
|
||||
<main className="container lg:w-2/3 mx-auto py-10 px-5">
|
||||
@ -32,18 +41,22 @@ const UserPage = (): JSX.Element => {
|
||||
</h2>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
<p className="mb-3">
|
||||
<CardBody className="flex flex-col items-stretch max-w-max gap-2">
|
||||
<div className="flex gap-5 items-center">
|
||||
<p>
|
||||
<strong>{t("email")}:</strong> <span>{user.email}</span>
|
||||
</p>
|
||||
|
||||
<div className="inline-block">
|
||||
<Link href={"/logout"}>
|
||||
<Button className="px-3 py-2" variant="secondary">
|
||||
{t("logoutButton")}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
{is_premium === true ? null : (
|
||||
<StripePricingModal
|
||||
Trigger={<Button>{t("monetization:upgrade")}</Button>}
|
||||
/>
|
||||
)}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
|
@ -12,12 +12,16 @@ type SidebarFooterProps = {
|
||||
export const SidebarFooter = ({
|
||||
showButtons,
|
||||
}: SidebarFooterProps): JSX.Element => {
|
||||
const buttons = {
|
||||
myBrains: <BrainManagementButton />,
|
||||
upgradeToPlus: <UpgradeToPlus />,
|
||||
user: <UserButton />,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-gray-50 dark:bg-gray-900 border-t dark:border-white/10 mt-auto p-2">
|
||||
<div className="max-w-screen-xl flex justify-center items-center flex-col">
|
||||
{showButtons.includes("myBrains") && <BrainManagementButton />}
|
||||
{showButtons.includes("upgradeToPlus") && <UpgradeToPlus />}
|
||||
{showButtons.includes("user") && <UserButton />}
|
||||
{showButtons.map((button) => buttons[button])}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,22 +1,20 @@
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaBrain } from "react-icons/fa";
|
||||
|
||||
import { sidebarLinkStyle } from "@/lib/components/Sidebar/components/SidebarFooter/styles/SidebarLinkStyle";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { SidebarFooterButton } from "./SidebarFooterButton";
|
||||
|
||||
export const BrainManagementButton = (): JSX.Element => {
|
||||
const { currentBrainId } = useBrainContext();
|
||||
const { t } = useTranslation("brain");
|
||||
|
||||
return (
|
||||
<Link
|
||||
<SidebarFooterButton
|
||||
href={`/brains-management/${currentBrainId ?? ""}`}
|
||||
className={sidebarLinkStyle}
|
||||
icon={<FaBrain className="w-8 h-8" />}
|
||||
label={t("myBrains")}
|
||||
data-testid="brain-management-button"
|
||||
>
|
||||
<FaBrain className="w-8 h-8" />
|
||||
<span>{t("myBrains")}</span>
|
||||
</Link>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,34 @@
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
type SidebarFooterButtonProps = {
|
||||
icon: JSX.Element;
|
||||
label: string | JSX.Element;
|
||||
href?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export const SidebarFooterButton = ({
|
||||
icon,
|
||||
label,
|
||||
href,
|
||||
onClick,
|
||||
}: SidebarFooterButtonProps): JSX.Element => {
|
||||
const router = useRouter();
|
||||
|
||||
if (href !== undefined) {
|
||||
onClick = () => {
|
||||
void router.push(href);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="w-full rounded-lg px-5 py-2 text-base flex justify-start items-center gap-4 hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-primary focus:outline-none"
|
||||
onClick={onClick}
|
||||
>
|
||||
{icon}
|
||||
<span className="text-ellipsis overflow-hidden">{label}</span>
|
||||
</button>
|
||||
);
|
||||
};
|
@ -5,7 +5,7 @@ import { FiUser } from "react-icons/fi";
|
||||
import { StripePricingModal } from "@/lib/components/Stripe";
|
||||
import { useUserData } from "@/lib/hooks/useUserData";
|
||||
|
||||
import { sidebarLinkStyle } from "../styles/SidebarLinkStyle";
|
||||
import { SidebarFooterButton } from "./SidebarFooterButton";
|
||||
|
||||
export const UpgradeToPlus = (): JSX.Element => {
|
||||
const { userData } = useUserData();
|
||||
@ -20,15 +20,17 @@ export const UpgradeToPlus = (): JSX.Element => {
|
||||
return (
|
||||
<StripePricingModal
|
||||
Trigger={
|
||||
<button type="button" className={sidebarLinkStyle}>
|
||||
<FiUser className="w-8 h-8" />
|
||||
<span>
|
||||
<SidebarFooterButton
|
||||
icon={<FiUser className="w-8 h-8" />}
|
||||
label={
|
||||
<>
|
||||
{t("upgrade")}{" "}
|
||||
<span className="rounded bg-primary/50 py-1 px-3 text-xs">
|
||||
{t("new")}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
@ -1,21 +1,18 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { Avatar } from "@/lib/components/ui/Avatar";
|
||||
import { useSupabase } from "@/lib/context/SupabaseProvider";
|
||||
|
||||
import { SidebarFooterButton } from "./SidebarFooterButton";
|
||||
import { useGravatar } from "../../../../../hooks/useGravatar";
|
||||
import { sidebarLinkStyle } from "../styles/SidebarLinkStyle";
|
||||
|
||||
export const UserButton = (): JSX.Element => {
|
||||
const { session } = useSupabase();
|
||||
const { gravatarUrl } = useGravatar();
|
||||
|
||||
return (
|
||||
<Link aria-label="account" className={sidebarLinkStyle} href={"/user"}>
|
||||
<Avatar url={gravatarUrl} alt="user-gravatar" />
|
||||
<span className="text-ellipsis overflow-hidden">
|
||||
{session?.user.email ?? ""}
|
||||
</span>
|
||||
</Link>
|
||||
<SidebarFooterButton
|
||||
href={"/user"}
|
||||
icon={<Avatar url={gravatarUrl} />}
|
||||
label={session?.user.email ?? ""}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,2 +0,0 @@
|
||||
export const sidebarLinkStyle =
|
||||
"w-full rounded-lg px-5 py-2 text-base flex justify-start items-center gap-4 hover:bg-gray-200 dark:hover:bg-gray-800 focus:outline-none";
|
@ -4,20 +4,18 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
type AvatarProps = {
|
||||
url: string;
|
||||
alt: string;
|
||||
imgClassName?: string;
|
||||
className?: string;
|
||||
};
|
||||
export const Avatar = ({
|
||||
url,
|
||||
alt,
|
||||
imgClassName,
|
||||
className,
|
||||
}: AvatarProps): JSX.Element => {
|
||||
return (
|
||||
<div className={cn("relative w-8 h-8", className)}>
|
||||
<div className={cn("relative w-8 h-8 shrink-0", className)}>
|
||||
<Image
|
||||
alt={alt}
|
||||
alt="avatar"
|
||||
fill={true}
|
||||
sizes="32px"
|
||||
src={url}
|
||||
|
Loading…
Reference in New Issue
Block a user