feat: license upgrade confirm
This commit is contained in:
@@ -9,6 +9,7 @@ const BASE_URL = process.env.REACT_APP_RELEASE
|
||||
let prices: Price[] = [
|
||||
{
|
||||
type: "subscription",
|
||||
price: "",
|
||||
sub_dur: "year",
|
||||
// sub_type: "year", //day month year
|
||||
title: "Pro",
|
||||
@@ -18,6 +19,7 @@ let prices: Price[] = [
|
||||
},
|
||||
{
|
||||
type: "payment",
|
||||
price: "",
|
||||
title: "supreme",
|
||||
limit: 99999,
|
||||
pid: "price_1M5VoGGGoUDRyc3j6xhQou6D",
|
||||
@@ -31,12 +33,14 @@ export const getLicensePriceList = () => {
|
||||
case 0: {
|
||||
p.title = i18n.t("price.pro.title");
|
||||
p.desc = i18n.t("price.pro.desc");
|
||||
p.price = i18n.t("price.pro.price");
|
||||
}
|
||||
break;
|
||||
// supreme
|
||||
case 1: {
|
||||
p.title = i18n.t("price.supreme.title");
|
||||
p.desc = i18n.t("price.supreme.desc");
|
||||
p.price = i18n.t("price.supreme.price");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -51,6 +55,7 @@ export const getLicensePriceList = () => {
|
||||
...ps,
|
||||
{
|
||||
type: "payment",
|
||||
price: "1",
|
||||
title: "Test VoceChat Enterprise",
|
||||
limit: 99999,
|
||||
pid: "price_1LkQGpGGoUDRyc3jGTh3GYHw",
|
||||
@@ -58,6 +63,7 @@ export const getLicensePriceList = () => {
|
||||
},
|
||||
{
|
||||
title: "VoceChat Pro",
|
||||
price: "1",
|
||||
limit: 100,
|
||||
pid: "price_1MMNNCGGoUDRyc3jSIGIsb3C",
|
||||
desc: "test subscription price",
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { FC, useState } from "react";
|
||||
import { ChangeEvent, FC, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import Modal from "../../../common/component/Modal";
|
||||
import StyledModal from "../../../common/component/styled/Modal";
|
||||
import Button from "../../../common/component/styled/Button";
|
||||
import StyledRadio from "../../../common/component/styled/Radio";
|
||||
import Input from "../../../common/component/styled/Input";
|
||||
|
||||
import { useGetLicensePaymentUrlMutation } from "../../../app/services/server";
|
||||
import { getLicensePriceList } from "../../../app/config";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import dayjs from "dayjs";
|
||||
import { PriceSubscriptionDuration, PriceType } from "../../../types/common";
|
||||
import Tippy from "@tippyjs/react";
|
||||
// import { LicenseMetadata, RenewLicense } from "../../types/server";
|
||||
|
||||
interface Props {
|
||||
@@ -41,20 +43,26 @@ const LicensePriceListModal: FC<Props> = ({ closeModal }) => {
|
||||
const { t } = useTranslation("setting");
|
||||
const { t: ct } = useTranslation();
|
||||
const [getUrl, { isLoading, isSuccess }] = useGetLicensePaymentUrlMutation();
|
||||
const [host, setHost] = useState(location.host);
|
||||
const [popUpVisible, setPopUpVisible] = useState(false);
|
||||
const [selectPrice, setSelectPrice] = useState(
|
||||
`${LicensePriceList[0].pid}|${LicensePriceList[0].limit}|${LicensePriceList[0].type}|${LicensePriceList[0].sub_dur || ""}`
|
||||
);
|
||||
const handleRenew = async () => {
|
||||
const [priceId, user_limit, type, sub_dur = "month"] = selectPrice.split("|") as [string, string, PriceType, PriceSubscriptionDuration];
|
||||
const metadata = {
|
||||
user_limit: Number(user_limit),
|
||||
expire: type == "subscription" ? getExpireDay(sub_dur) : getExpireDay("year"),
|
||||
// 本地,则*通配符
|
||||
domain: host.startsWith("localhost") ? "*" : host
|
||||
};
|
||||
console.log(metadata);
|
||||
// return;
|
||||
|
||||
const resp = await getUrl({
|
||||
type,
|
||||
priceId,
|
||||
metadata: {
|
||||
user_limit: Number(user_limit),
|
||||
expire: type == "subscription" ? getExpireDay(sub_dur) : getExpireDay("year"),
|
||||
// 本地,则*通配符
|
||||
domain: location.host.startsWith("localhost") ? "*" : location.host
|
||||
},
|
||||
metadata,
|
||||
cancel_url: location.href,
|
||||
success_url: `${location.origin}/#/cb/payment_success`
|
||||
});
|
||||
@@ -69,9 +77,16 @@ const LicensePriceListModal: FC<Props> = ({ closeModal }) => {
|
||||
const handlePriceSelect = (price: string) => {
|
||||
setSelectPrice(price);
|
||||
};
|
||||
const handleUpdateHost = (evt: ChangeEvent<HTMLInputElement>) => {
|
||||
setHost(evt.target.value);
|
||||
};
|
||||
const togglePopUpVisible = () => {
|
||||
setPopUpVisible(prev => !prev);
|
||||
};
|
||||
return (
|
||||
<Modal id="modal-modal">
|
||||
<StyledModal
|
||||
className="!min-w-[480px]"
|
||||
title={t("license.renew")}
|
||||
description={t("license.renew_select")}
|
||||
buttons={
|
||||
@@ -79,14 +94,42 @@ const LicensePriceListModal: FC<Props> = ({ closeModal }) => {
|
||||
<Button onClick={closeModal} className="ghost">
|
||||
{ct("action.cancel")}
|
||||
</Button>
|
||||
<Button disabled={isLoading || isSuccess} onClick={handleRenew} >
|
||||
{isLoading ? "Initialize Payment Url" : isSuccess ? "Redirecting" : t("license.renew")}
|
||||
</Button>
|
||||
<Tippy
|
||||
visible={popUpVisible}
|
||||
interactive
|
||||
placement="top-end"
|
||||
offset={[0, -40]}
|
||||
trigger="click"
|
||||
content={
|
||||
<div className="p-3 rounded-lg border border-solid border-gray-200 flex flex-col items-start gap-3 w-[340px] bg-white shadow shadow-gray-200 drop-shadow-xl">
|
||||
<div className="text-gray-500 text-sm">
|
||||
{t("license.tip_domain")}
|
||||
</div>
|
||||
<Input value={host} onChange={handleUpdateHost} />
|
||||
<div className="flex justify-between items-center w-full mt-4">
|
||||
<span className="text-xs text-orange-500"> {t("license.tip_port")}</span>
|
||||
<div className="flex gap-3">
|
||||
<Button className="cancel mini" onClick={togglePopUpVisible}>
|
||||
{ct("action.cancel")}
|
||||
</Button>
|
||||
<Button className="mini" disabled={isLoading || isSuccess} onClick={handleRenew}>
|
||||
{isLoading ? "Initialize Payment Url" : isSuccess ? "Redirecting" : t("license.tip_confirm")}
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button onClick={togglePopUpVisible}>
|
||||
{t("license.renew")}
|
||||
</Button>
|
||||
</Tippy>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<StyledRadio
|
||||
options={LicensePriceList.map(({ title, desc }) => `${title} [${desc}]`)}
|
||||
options={LicensePriceList.map(({ title, desc, price }) => `${title} [${desc}][${price}]`)}
|
||||
values={LicensePriceList.map(({ pid, limit, type = "payment", sub_dur = "month" }) => `${pid}|${limit}|${type}|${sub_dur}`)}
|
||||
value={selectPrice}
|
||||
onChange={(v) => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import LicensePriceListModal from "./LicensePriceListModal";
|
||||
import clsx from "clsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import UpdateLicenseModal from "./UpdateLicenseModal";
|
||||
import Tippy from "@tippyjs/react";
|
||||
|
||||
interface ItemProps extends HTMLAttributes<HTMLSpanElement> { label: string, data?: string | number | string[], foldable?: boolean }
|
||||
const Item = ({ label, data, foldable = false, ...rest }: ItemProps) => {
|
||||
@@ -25,6 +26,7 @@ const Item = ({ label, data, foldable = false, ...rest }: ItemProps) => {
|
||||
};
|
||||
export default function License() {
|
||||
const { t, i18n } = useTranslation("setting");
|
||||
const { t: ct } = useTranslation();
|
||||
const { license: licenseInfo, reachLimit, upsertLicense, upserting, upserted } = useLicense();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [updateVisible, setUpdateVisible] = useState(false);
|
||||
@@ -4,7 +4,7 @@ import Logins from "./config/Logins";
|
||||
import ConfigFirebase from "./config/Firebase";
|
||||
import ConfigSMTP from "./config/SMTP";
|
||||
import APIConfig from "./APIConfig";
|
||||
import License from "./License/License";
|
||||
import License from "./License";
|
||||
import Widget from "./Widget";
|
||||
import BotConfig from "./BotConfig";
|
||||
import APIDocument from "./APIDocument";
|
||||
|
||||
@@ -6,6 +6,7 @@ export type PriceType = "subscription" | "payment";
|
||||
export type PriceSubscriptionDuration = "month" | "quarter" | "year";
|
||||
export type Price = {
|
||||
title: string,
|
||||
price: string,
|
||||
limit: number,
|
||||
pid: string,
|
||||
desc: string,
|
||||
|
||||
Reference in New Issue
Block a user