feat: manage plan (#1488)

# Description

- add crown on premium user
- link to manage plan in page `/user`

## ⚠️ Before merging

Setup env variable:
```env
NEXT_PUBLIC_STRIPE_MANAGE_PLAN_URL=<ignore-me-or-change-me>
```

## Screenshots (if appropriate):

<img width="290" alt="image"
src="https://github.com/StanGirard/quivr/assets/67386567/a87b0f7e-b07c-4f4e-b9d2-515ac25ebf05">

<img width="318" alt="image"
src="https://github.com/StanGirard/quivr/assets/67386567/6a4f4f72-8c75-45da-9468-cae1a8d28935">
This commit is contained in:
Matthieu Jacq 2023-10-25 16:11:17 +02:00 committed by GitHub
parent 41563767ad
commit d311a53b6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 67 additions and 30 deletions

View File

@ -18,3 +18,4 @@ NEXT_PUBLIC_CMS_URL=https://cms.quivr.app
NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID=<ignore-me-or-change-me>
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=<ignore-me-or-change-me>
NEXT_PUBLIC_STRIPE_MANAGE_PLAN_URL=<ignore-me-or-change-me>

View File

@ -0,0 +1,29 @@
import { useFeatureIsOn } from "@growthbook/growthbook-react";
import { useTranslation } from "react-i18next";
import { StripePricingModal } from "@/lib/components/Stripe";
import Button from "@/lib/components/ui/Button";
import { useUserData } from "@/lib/hooks/useUserData";
const MANAGE_PLAN_URL = process.env.NEXT_PUBLIC_STRIPE_MANAGE_PLAN_URL;
export const StripePricingOrManageButton = (): JSX.Element => {
const { t } = useTranslation("monetization");
const { userData } = useUserData();
const monetizationIsOn = useFeatureIsOn("monetization");
if (!monetizationIsOn) {
return <></>;
}
const is_premium = userData?.is_premium ?? false;
if (is_premium) {
return (
<a href={MANAGE_PLAN_URL} target="_blank" rel="noopener">
<Button className="w-full">{t("manage_plan")}</Button>
</a>
);
}
return <StripePricingModal Trigger={<Button>{t("upgrade")}</Button>} />;
};

View File

@ -1 +1,2 @@
export { UserStatistics } from "./UserStatistics";
export { StripePricingOrManageButton } from "./StripePricingOrManageButton";

View File

@ -1,36 +1,26 @@
/* eslint-disable max-lines */
"use client";
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";
import { StripePricingOrManageButton, UserStatistics } from "./components";
import { ApiKeyConfig } from "./components/ApiKeyConfig";
import LanguageSelect from "./components/LanguageDropDown/LanguageSelect";
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",
"monetization",
]);
const { t } = useTranslation(["translation", "user", "config"]);
return (
<main className="container lg:w-2/3 mx-auto py-10 px-5">
@ -52,11 +42,7 @@ const UserPage = (): JSX.Element => {
</Button>
</Link>
</div>
{is_premium === true ? null : (
<StripePricingModal
Trigger={<Button>{t("monetization:upgrade")}</Button>}
/>
)}
<StripePricingOrManageButton />
</CardBody>
</Card>

View File

@ -27,8 +27,10 @@ export const SidebarFooterButton = ({
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>
<span className="w-8 shrink-0">{icon}</span>
<span className="w-full text-ellipsis overflow-hidden text-start">
{label}
</span>
</button>
);
};

View File

@ -23,12 +23,12 @@ export const UpgradeToPlus = (): JSX.Element => {
<SidebarFooterButton
icon={<FiUser className="w-8 h-8" />}
label={
<>
{t("upgrade")}{" "}
<span className="rounded bg-primary/50 py-1 px-3 text-xs">
<div className="flex justify-between items-center w-full">
{t("upgrade")}
<span className="rounded bg-primary/30 py-1 px-3 text-xs">
{t("new")}
</span>
</>
</div>
}
/>
}

View File

@ -1,5 +1,8 @@
import { FaCrown } from "react-icons/fa";
import { Avatar } from "@/lib/components/ui/Avatar";
import { useSupabase } from "@/lib/context/SupabaseProvider";
import { useUserData } from "@/lib/hooks/useUserData";
import { SidebarFooterButton } from "./SidebarFooterButton";
import { useGravatar } from "../../../../../hooks/useGravatar";
@ -7,12 +10,21 @@ import { useGravatar } from "../../../../../hooks/useGravatar";
export const UserButton = (): JSX.Element => {
const { session } = useSupabase();
const { gravatarUrl } = useGravatar();
const { userData } = useUserData();
const is_premium = userData?.is_premium ?? false;
const email = session?.user.email ?? "";
const label = (
<span className="flex justify-between items-center flex-nowrap gap-1 w-full">
<span className="text-ellipsis overflow-hidden">{email}</span>
{is_premium && <FaCrown className="w-5 h-5 shrink-0" />}
</span>
);
return (
<SidebarFooterButton
href={"/user"}
icon={<Avatar url={gravatarUrl} />}
label={session?.user.email ?? ""}
label={label}
/>
);
};

View File

@ -1,4 +1,5 @@
{
"upgrade": "Upgrade to plus",
"new": "New"
"new": "New",
"manage_plan": "Manage my plan"
}

View File

@ -1,4 +1,5 @@
{
"upgrade": "Actualizar a plus",
"new": "Nuevo"
"new": "Nuevo",
"manage_plan": "Gestionar mi plan"
}

View File

@ -1,4 +1,5 @@
{
"upgrade": "Passer à la version plus",
"new": "Nouveau"
"new": "Nouveau",
"manage_plan": "Gérer mon plan"
}

View File

@ -1,4 +1,5 @@
{
"upgrade": "Atualizar para o plus",
"new": "Novo"
"new": "Novo",
"manage_plan": "Gerenciar meu plano"
}

View File

@ -1,4 +1,5 @@
{
"upgrade": "Обновить до плюса",
"new": "Новый"
"new": "Новый",
"manage_plan": "Управлять моим планом"
}

View File

@ -1,4 +1,5 @@
{
"upgrade": "升级至高级版",
"new": "新"
"new": "新",
"manage_plan": "管理我的计划"
}