mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-12-14 17:03:29 +03:00
feat: upgrade to plus button (#1482)
# Description Epic: #1429 User Story: #1430 ## Pour la mise en preview / prod: - Mettre à jour l'environnement ```env NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID=<change-me> NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=<change-me> ``` - Activer le feature flag `monetization` (booléen) ## Screenshots (if appropriate): Button: <img width="289" alt="image" src="https://github.com/StanGirard/quivr/assets/67386567/c0f7321e-2f48-4462-aab9-fd1c6f4282cd"> Modal: <img width="843" alt="image" src="https://github.com/StanGirard/quivr/assets/67386567/28082680-1126-44db-bf77-76ae7474747f">
This commit is contained in:
parent
0bf7d36629
commit
56d1f94b62
@ -15,3 +15,6 @@ NEXT_PUBLIC_E2E_EMAIL=<ignore-me-or-change-me>
|
||||
NEXT_PUBLIC_E2E_PASSWORD=<ignore-me-or-change-me>
|
||||
|
||||
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>
|
||||
|
@ -17,7 +17,7 @@ export const BrainsList = (): JSX.Element => {
|
||||
const { t } = useTranslation(["brain", "chat"]);
|
||||
|
||||
return (
|
||||
<Sidebar showButtons={["user"]}>
|
||||
<Sidebar showButtons={["user", "upgradeToPlus"]}>
|
||||
<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"]}>
|
||||
<Sidebar showButtons={["myBrains", "user", "upgradeToPlus"]}>
|
||||
<div className="flex flex-col flex-1 h-full" data-testid="chats-list">
|
||||
<div className="pt-2">
|
||||
<NewChatButton />
|
||||
|
@ -1,19 +1,23 @@
|
||||
import { BrainManagementButton } from "@/lib/components/Sidebar/components/SidebarFooter/components/BrainManagementButton";
|
||||
|
||||
import { UpgradeToPlus } from "./components/UpgradeToPlus";
|
||||
import { UserButton } from "./components/UserButton";
|
||||
|
||||
export type SidebarFooterButtons = "myBrains" | "user";
|
||||
export type SidebarFooterButtons = "myBrains" | "user" | "upgradeToPlus";
|
||||
|
||||
type SidebarFooterProps = {
|
||||
showButtons: SidebarFooterButtons[];
|
||||
};
|
||||
|
||||
export const SidebarFooter = ({showButtons}: SidebarFooterProps): JSX.Element => {
|
||||
export const SidebarFooter = ({
|
||||
showButtons,
|
||||
}: SidebarFooterProps): JSX.Element => {
|
||||
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('user') && <UserButton />}
|
||||
{showButtons.includes("myBrains") && <BrainManagementButton />}
|
||||
{showButtons.includes("upgradeToPlus") && <UpgradeToPlus />}
|
||||
{showButtons.includes("user") && <UserButton />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -0,0 +1,35 @@
|
||||
import { useFeatureIsOn } from "@growthbook/growthbook-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FiUser } from "react-icons/fi";
|
||||
|
||||
import { StripePricingModal } from "@/lib/components/Stripe";
|
||||
import { useUserData } from "@/lib/hooks/useUserData";
|
||||
|
||||
import { sidebarLinkStyle } from "../styles/SidebarLinkStyle";
|
||||
|
||||
export const UpgradeToPlus = (): JSX.Element => {
|
||||
const { userData } = useUserData();
|
||||
const is_premium = userData?.is_premium;
|
||||
const featureIsOn = useFeatureIsOn("monetization");
|
||||
const { t } = useTranslation("monetization");
|
||||
|
||||
if (!featureIsOn || is_premium === true) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<StripePricingModal
|
||||
Trigger={
|
||||
<button type="button" className={sidebarLinkStyle}>
|
||||
<FiUser className="w-8 h-8" />
|
||||
<span>
|
||||
{t("upgrade")}{" "}
|
||||
<span className="rounded bg-primary/50 py-1 px-3 text-xs">
|
||||
{t("new")}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
import { StripePricingTable } from "./components/PricingTable/PricingTable";
|
||||
import { Modal } from "../../ui/Modal";
|
||||
|
||||
type StripePricingModalProps = {
|
||||
Trigger: JSX.Element;
|
||||
};
|
||||
|
||||
export const StripePricingModal = ({
|
||||
Trigger,
|
||||
}: StripePricingModalProps): JSX.Element => {
|
||||
return (
|
||||
<Modal Trigger={Trigger} CloseTrigger={<div />}>
|
||||
<StripePricingTable />
|
||||
</Modal>
|
||||
);
|
||||
};
|
@ -0,0 +1,14 @@
|
||||
const PRICING_TABLE_ID = process.env.NEXT_PUBLIC_STRIPE_PRICING_TABLE_ID;
|
||||
const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
|
||||
|
||||
export const StripePricingTable = (): JSX.Element => {
|
||||
return (
|
||||
<>
|
||||
<script async src="https://js.stripe.com/v3/pricing-table.js"></script>
|
||||
<stripe-pricing-table
|
||||
pricing-table-id={PRICING_TABLE_ID}
|
||||
publishable-key={PUBLISHABLE_KEY}
|
||||
></stripe-pricing-table>
|
||||
</>
|
||||
);
|
||||
};
|
12
frontend/lib/components/Stripe/PricingModal/components/PricingTable/Types/types.d.ts
vendored
Normal file
12
frontend/lib/components/Stripe/PricingModal/components/PricingTable/Types/types.d.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
import * as React from "react";
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"stripe-pricing-table": React.DetailedHTMLProps<
|
||||
React.HTMLAttributes<HTMLElement>,
|
||||
HTMLElement
|
||||
>;
|
||||
}
|
||||
}
|
||||
}
|
1
frontend/lib/components/Stripe/index.ts
Normal file
1
frontend/lib/components/Stripe/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./PricingModal/StripePricingModal";
|
@ -11,6 +11,7 @@ import invitation_en from "../../../public/locales/en/invitation.json";
|
||||
import knowlegde_en from "../../../public/locales/en/knowledge.json";
|
||||
import login_en from "../../../public/locales/en/login.json";
|
||||
import logout_en from "../../../public/locales/en/logout.json";
|
||||
import monetization_en from "../../../public/locales/en/monetization.json";
|
||||
import signUp_en from "../../../public/locales/en/signUp.json";
|
||||
import translation_en from "../../../public/locales/en/translation.json";
|
||||
import updatePassword_en from "../../../public/locales/en/updatePassword.json";
|
||||
@ -28,6 +29,7 @@ import invitation_es from "../../../public/locales/es/invitation.json";
|
||||
import knowlegde_es from "../../../public/locales/es/knowledge.json";
|
||||
import login_es from "../../../public/locales/es/login.json";
|
||||
import logout_es from "../../../public/locales/es/logout.json";
|
||||
import monetization_es from "../../../public/locales/es/monetization.json";
|
||||
import signUp_es from "../../../public/locales/es/signUp.json";
|
||||
import translation_es from "../../../public/locales/es/translation.json";
|
||||
import updatePassword_es from "../../../public/locales/es/updatePassword.json";
|
||||
@ -45,6 +47,7 @@ import invitation_fr from "../../../public/locales/fr/invitation.json";
|
||||
import knowlegde_fr from "../../../public/locales/fr/knowledge.json";
|
||||
import login_fr from "../../../public/locales/fr/login.json";
|
||||
import logout_fr from "../../../public/locales/fr/logout.json";
|
||||
import monetization_fr from "../../../public/locales/fr/monetization.json";
|
||||
import signUp_fr from "../../../public/locales/fr/signUp.json";
|
||||
import translation_fr from "../../../public/locales/fr/translation.json";
|
||||
import updatePassword_fr from "../../../public/locales/fr/updatePassword.json";
|
||||
@ -62,6 +65,7 @@ import invitation_ptbr from "../../../public/locales/pt-br/invitation.json";
|
||||
import knowlegde_ptbr from "../../../public/locales/pt-br/knowledge.json";
|
||||
import login_ptbr from "../../../public/locales/pt-br/login.json";
|
||||
import logout_ptbr from "../../../public/locales/pt-br/logout.json";
|
||||
import monetization_ptbr from "../../../public/locales/pt-br/monetization.json";
|
||||
import signUp_ptbr from "../../../public/locales/pt-br/signUp.json";
|
||||
import translation_ptbr from "../../../public/locales/pt-br/translation.json";
|
||||
import updatePassword_ptbr from "../../../public/locales/pt-br/updatePassword.json";
|
||||
@ -79,6 +83,7 @@ import invitation_ru from "../../../public/locales/ru/invitation.json";
|
||||
import knowlegde_ru from "../../../public/locales/ru/knowledge.json";
|
||||
import login_ru from "../../../public/locales/ru/login.json";
|
||||
import logout_ru from "../../../public/locales/ru/logout.json";
|
||||
import monetization_ru from "../../../public/locales/ru/monetization.json";
|
||||
import signUp_ru from "../../../public/locales/ru/signUp.json";
|
||||
import translation_ru from "../../../public/locales/ru/translation.json";
|
||||
import updatePassword_ru from "../../../public/locales/ru/updatePassword.json";
|
||||
@ -96,6 +101,7 @@ import invitation_zh_cn from "../../../public/locales/zh-cn/invitation.json";
|
||||
import knowlegde_zh_cn from "../../../public/locales/zh-cn/knowledge.json";
|
||||
import login_zh_cn from "../../../public/locales/zh-cn/login.json";
|
||||
import logout_zh_cn from "../../../public/locales/zh-cn/logout.json";
|
||||
import monetization_zh_cn from "../../../public/locales/zh-cn/monetization.json";
|
||||
import signUp_zh_cn from "../../../public/locales/zh-cn/signUp.json";
|
||||
import translation_zh_cn from "../../../public/locales/zh-cn/translation.json";
|
||||
import updatePassword_zh_cn from "../../../public/locales/zh-cn/updatePassword.json";
|
||||
@ -114,6 +120,7 @@ export type Translations = {
|
||||
invitation: typeof import("../../../public/locales/en/invitation.json");
|
||||
login: typeof import("../../../public/locales/en/login.json");
|
||||
logout: typeof import("../../../public/locales/en/logout.json");
|
||||
monetization: typeof import("../../../public/locales/en/monetization.json");
|
||||
signUp: typeof import("../../../public/locales/en/signUp.json");
|
||||
translation: typeof import("../../../public/locales/en/translation.json");
|
||||
updatePassword: typeof import("../../../public/locales/en/updatePassword.json");
|
||||
@ -143,6 +150,7 @@ export const resources: Record<SupportedLanguages, Translations> = {
|
||||
invitation: invitation_en,
|
||||
login: login_en,
|
||||
logout: logout_en,
|
||||
monetization: monetization_en,
|
||||
signUp: signUp_en,
|
||||
translation: translation_en,
|
||||
updatePassword: updatePassword_en,
|
||||
@ -161,6 +169,7 @@ export const resources: Record<SupportedLanguages, Translations> = {
|
||||
invitation: invitation_es,
|
||||
login: login_es,
|
||||
logout: logout_es,
|
||||
monetization: monetization_es,
|
||||
signUp: signUp_es,
|
||||
translation: translation_es,
|
||||
updatePassword: updatePassword_es,
|
||||
@ -179,6 +188,7 @@ export const resources: Record<SupportedLanguages, Translations> = {
|
||||
invitation: invitation_fr,
|
||||
login: login_fr,
|
||||
logout: logout_fr,
|
||||
monetization: monetization_fr,
|
||||
signUp: signUp_fr,
|
||||
translation: translation_fr,
|
||||
updatePassword: updatePassword_fr,
|
||||
@ -197,6 +207,7 @@ export const resources: Record<SupportedLanguages, Translations> = {
|
||||
invitation: invitation_ptbr,
|
||||
login: login_ptbr,
|
||||
logout: logout_ptbr,
|
||||
monetization: monetization_ptbr,
|
||||
signUp: signUp_ptbr,
|
||||
translation: translation_ptbr,
|
||||
updatePassword: updatePassword_ptbr,
|
||||
@ -215,6 +226,7 @@ export const resources: Record<SupportedLanguages, Translations> = {
|
||||
invitation: invitation_ru,
|
||||
login: login_ru,
|
||||
logout: logout_ru,
|
||||
monetization: monetization_ru,
|
||||
signUp: signUp_ru,
|
||||
translation: translation_ru,
|
||||
updatePassword: updatePassword_ru,
|
||||
@ -233,6 +245,7 @@ export const resources: Record<SupportedLanguages, Translations> = {
|
||||
invitation: invitation_zh_cn,
|
||||
login: login_zh_cn,
|
||||
logout: logout_zh_cn,
|
||||
monetization: monetization_zh_cn,
|
||||
signUp: signUp_zh_cn,
|
||||
translation: translation_zh_cn,
|
||||
updatePassword: updatePassword_zh_cn,
|
||||
|
@ -37,7 +37,12 @@ const ContentSecurityPolicy = {
|
||||
"https://cdn.growthbook.io",
|
||||
"https://vitals.vercel-insights.com/v1/vitals",
|
||||
],
|
||||
"img-src": ["'self'", "https://www.gravatar.com","https://quivr-cms.s3.eu-west-3.amazonaws.com", "data:"],
|
||||
"img-src": [
|
||||
"'self'",
|
||||
"https://www.gravatar.com",
|
||||
"https://quivr-cms.s3.eu-west-3.amazonaws.com",
|
||||
"data:",
|
||||
],
|
||||
"media-src": [
|
||||
"'self'",
|
||||
"https://user-images.githubusercontent.com",
|
||||
@ -50,7 +55,9 @@ const ContentSecurityPolicy = {
|
||||
"https://va.vercel-scripts.com/",
|
||||
process.env.NEXT_PUBLIC_FRONTEND_URL,
|
||||
"https://www.google-analytics.com/",
|
||||
"https://js.stripe.com",
|
||||
],
|
||||
"frame-src": ["https://js.stripe.com"],
|
||||
"frame-ancestors": ["'none'"],
|
||||
"style-src": ["'unsafe-inline'", process.env.NEXT_PUBLIC_FRONTEND_URL],
|
||||
};
|
||||
|
4
frontend/public/locales/en/monetization.json
Normal file
4
frontend/public/locales/en/monetization.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"upgrade": "Upgrade to plus",
|
||||
"new": "New"
|
||||
}
|
4
frontend/public/locales/es/monetization.json
Normal file
4
frontend/public/locales/es/monetization.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"upgrade": "Actualizar a plus",
|
||||
"new": "Nuevo"
|
||||
}
|
4
frontend/public/locales/fr/monetization.json
Normal file
4
frontend/public/locales/fr/monetization.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"upgrade": "Passer à la version plus",
|
||||
"new": "Nouveau"
|
||||
}
|
4
frontend/public/locales/pt-br/monetization.json
Normal file
4
frontend/public/locales/pt-br/monetization.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"upgrade": "Atualizar para o plus",
|
||||
"new": "Novo"
|
||||
}
|
4
frontend/public/locales/ru/monetization.json
Normal file
4
frontend/public/locales/ru/monetization.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"upgrade": "Обновить до плюса",
|
||||
"new": "Новый"
|
||||
}
|
4
frontend/public/locales/zh-cn/monetization.json
Normal file
4
frontend/public/locales/zh-cn/monetization.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"upgrade": "升级至高级版",
|
||||
"new": "新"
|
||||
}
|
Loading…
Reference in New Issue
Block a user