mirror of
https://github.com/coaidev/coai.git
synced 2025-05-24 07:20:15 +09:00
feat: support quota is not enough dialog
This commit is contained in:
parent
66d3f7f7e6
commit
afa19d75f8
@ -176,6 +176,28 @@
|
|||||||
background: none !important;
|
background: none !important;
|
||||||
color: hsl(var(--text));
|
color: hsl(var(--text));
|
||||||
|
|
||||||
|
.prompt-row {
|
||||||
|
border: 1px solid var(--assistant-border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
margin: 0.25rem 0;
|
||||||
|
|
||||||
|
.grow {
|
||||||
|
min-width: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ol, ul, menu {
|
ol, ul, menu {
|
||||||
list-style: inherit;
|
list-style: inherit;
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,22 @@ import rehypeRaw from "rehype-raw";
|
|||||||
import { parseFile } from "./plugins/file.tsx";
|
import { parseFile } from "./plugins/file.tsx";
|
||||||
import "@/assets/markdown/all.less";
|
import "@/assets/markdown/all.less";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { openDialog as openQuotaDialog } from "@/store/quota.ts";
|
import { openDialog as openQuotaDialog } from "@/store/quota.ts";
|
||||||
import { openDialog as openSubscriptionDialog } from "@/store/subscription.ts";
|
import { openDialog as openSubscriptionDialog } from "@/store/subscription.ts";
|
||||||
import { AppDispatch } from "@/store";
|
import { AppDispatch } from "@/store";
|
||||||
import {
|
import {
|
||||||
|
CalendarPlus,
|
||||||
|
Cloud,
|
||||||
|
CloudCog,
|
||||||
|
Cloudy,
|
||||||
Codepen,
|
Codepen,
|
||||||
Codesandbox,
|
Codesandbox,
|
||||||
Copy,
|
Copy,
|
||||||
Github,
|
Github,
|
||||||
Maximize,
|
Maximize,
|
||||||
|
Package,
|
||||||
|
Plus,
|
||||||
RefreshCcwDot,
|
RefreshCcwDot,
|
||||||
Twitter,
|
Twitter,
|
||||||
Wand2,
|
Wand2,
|
||||||
@ -41,6 +47,8 @@ import {
|
|||||||
} from "@/components/ui/dialog.tsx";
|
} from "@/components/ui/dialog.tsx";
|
||||||
import { DialogClose } from "@radix-ui/react-dialog";
|
import { DialogClose } from "@radix-ui/react-dialog";
|
||||||
import { posterEvent } from "@/events/poster.ts";
|
import { posterEvent } from "@/events/poster.ts";
|
||||||
|
import { appLogo } from "@/conf/env.ts";
|
||||||
|
import { subscriptionDataSelector } from "@/store/globals.ts";
|
||||||
|
|
||||||
type MarkdownProps = {
|
type MarkdownProps = {
|
||||||
children: string;
|
children: string;
|
||||||
@ -109,6 +117,8 @@ function MarkdownContent({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const subscription = useSelector(subscriptionDataSelector);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.querySelectorAll(".file-instance").forEach((el) => {
|
document.querySelectorAll(".file-instance").forEach((el) => {
|
||||||
const parent = el.parentElement as HTMLElement;
|
const parent = el.parentElement as HTMLElement;
|
||||||
@ -130,6 +140,81 @@ function MarkdownContent({
|
|||||||
children={children}
|
children={children}
|
||||||
skipHtml={!acceptHtml}
|
skipHtml={!acceptHtml}
|
||||||
components={{
|
components={{
|
||||||
|
p({ children }) {
|
||||||
|
// if the format is `user quota is not enough error (model: gpt-3.5-turbo-1106, minimum quota: 0.01, your quota: -77.77)`, return special component
|
||||||
|
const match = children
|
||||||
|
.toString()
|
||||||
|
.match(
|
||||||
|
/user quota is not enough error \(model: (.*), minimum quota: (.*), your quota: (.*)\)/,
|
||||||
|
);
|
||||||
|
if (match) {
|
||||||
|
const [, model, minimum, quota] = match;
|
||||||
|
const plan = subscription
|
||||||
|
.flatMap((p) => p.items.map((i) => i.models.includes(model)))
|
||||||
|
.includes(true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex flex-col items-center w-[40vw] max-w-[320px] py-2`}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={appLogo}
|
||||||
|
alt={""}
|
||||||
|
className={`w-16 h-16 m-6 inline-block`}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={`prompt-row flex flex-row w-full items-center justify-center px-4 py-2`}
|
||||||
|
>
|
||||||
|
<Package className={`h-4 w-4 mr-1`} />
|
||||||
|
{t("model")}
|
||||||
|
<div className={`grow`} />
|
||||||
|
<p className={`value`}>{model}</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`prompt-row flex flex-row w-full items-center justify-center px-4 py-2`}
|
||||||
|
>
|
||||||
|
<Cloudy className={`h-4 w-4 mr-1`} />
|
||||||
|
{t("your-quota")}
|
||||||
|
<div className={`grow`} />
|
||||||
|
<p className={`value`}>
|
||||||
|
{quota}
|
||||||
|
<Cloud className={`h-4 w-4 ml-1`} />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`prompt-row flex flex-row w-full items-center justify-center px-4 py-2`}
|
||||||
|
>
|
||||||
|
<CloudCog className={`h-4 w-4 mr-1`} />
|
||||||
|
{t("min-quota")}
|
||||||
|
<div className={`grow`} />
|
||||||
|
<p className={`value`}>
|
||||||
|
{minimum}
|
||||||
|
<Cloud className={`h-4 w-4 ml-1`} />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
className={`mt-4 w-full`}
|
||||||
|
onClick={() => dispatch(openQuotaDialog())}
|
||||||
|
>
|
||||||
|
<Plus className={`h-4 w-4 mr-1`} />
|
||||||
|
{t("buy.dialog-title")}
|
||||||
|
</Button>
|
||||||
|
{plan && (
|
||||||
|
<Button
|
||||||
|
variant={`outline`}
|
||||||
|
className={`mt-2 w-full`}
|
||||||
|
onClick={() => dispatch(openSubscriptionDialog())}
|
||||||
|
>
|
||||||
|
<CalendarPlus className={`h-4 w-4 mr-1`} />
|
||||||
|
{t("sub.dialog-title")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <p>{children}</p>;
|
||||||
|
},
|
||||||
a({ href, children }) {
|
a({ href, children }) {
|
||||||
const url: string = href?.toString() || "";
|
const url: string = href?.toString() || "";
|
||||||
|
|
||||||
|
@ -287,7 +287,13 @@ function QuotaDialog() {
|
|||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
</div>
|
</div>
|
||||||
{!useDeeptrain && (
|
{useDeeptrain ? (
|
||||||
|
<div
|
||||||
|
className={`flex flex-row w-full justify-center items-center mt-2 select-none`}
|
||||||
|
>
|
||||||
|
{t("buy.deeptrain-tip")}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<div className={`flex flex-row w-full`}>
|
<div className={`flex flex-row w-full`}>
|
||||||
<Input
|
<Input
|
||||||
className={`redeem-input mr-2 text-center`}
|
className={`redeem-input mr-2 text-center`}
|
||||||
|
@ -48,6 +48,9 @@
|
|||||||
"i-know": "我已知晓",
|
"i-know": "我已知晓",
|
||||||
"empty": "空空如也",
|
"empty": "空空如也",
|
||||||
"exit": "离开",
|
"exit": "离开",
|
||||||
|
"model": "模型",
|
||||||
|
"min-quota": "最低余额",
|
||||||
|
"your-quota": "您的余额",
|
||||||
"auth": {
|
"auth": {
|
||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
"username-placeholder": "请输入用户名",
|
"username-placeholder": "请输入用户名",
|
||||||
@ -184,6 +187,7 @@
|
|||||||
"success-prompt": "您已成功购买 {{amount}} 点数。",
|
"success-prompt": "您已成功购买 {{amount}} 点数。",
|
||||||
"redeem": "兑换",
|
"redeem": "兑换",
|
||||||
"redeem-placeholder": "请输入兑换码",
|
"redeem-placeholder": "请输入兑换码",
|
||||||
|
"deeptrain-tip": "提示:在 Deeptrain 充值至钱包后,请返回此处,点击购买相应点数",
|
||||||
"exchange-success": "兑换成功",
|
"exchange-success": "兑换成功",
|
||||||
"exchange-success-prompt": "您已成功兑换 {{amount}} 点数。",
|
"exchange-success-prompt": "您已成功兑换 {{amount}} 点数。",
|
||||||
"failed": "购买失败",
|
"failed": "购买失败",
|
||||||
|
@ -139,7 +139,8 @@
|
|||||||
"exchange-success-prompt": "You have successfully redeemed {{amount}} credits.",
|
"exchange-success-prompt": "You have successfully redeemed {{amount}} credits.",
|
||||||
"exchange-failed": "Failed",
|
"exchange-failed": "Failed",
|
||||||
"exchange-failed-prompt": "Redemption failed for {{reason}}",
|
"exchange-failed-prompt": "Redemption failed for {{reason}}",
|
||||||
"buy-link": "Go to the deal"
|
"buy-link": "Go to the deal",
|
||||||
|
"deeptrain-tip": "Tip: Once Deeptrain has reloaded to your wallet, come back here and click to buy the appropriate credits"
|
||||||
},
|
},
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"title": "Packages",
|
"title": "Packages",
|
||||||
@ -710,5 +711,8 @@
|
|||||||
"i-know": "Yes, I understand.",
|
"i-know": "Yes, I understand.",
|
||||||
"submit": "Send",
|
"submit": "Send",
|
||||||
"empty": "empty",
|
"empty": "empty",
|
||||||
"exit": "Leave"
|
"exit": "Leave",
|
||||||
|
"model": "Model",
|
||||||
|
"min-quota": "Minimum Balance",
|
||||||
|
"your-quota": "Your balance"
|
||||||
}
|
}
|
@ -139,7 +139,8 @@
|
|||||||
"exchange-success-prompt": "{{amount}}クレジットを正常に引き換えました。",
|
"exchange-success-prompt": "{{amount}}クレジットを正常に引き換えました。",
|
||||||
"exchange-failed": "引き換えに失敗しました",
|
"exchange-failed": "引き換えに失敗しました",
|
||||||
"exchange-failed-prompt": "{{reason}}のため、引き換えに失敗しました",
|
"exchange-failed-prompt": "{{reason}}のため、引き換えに失敗しました",
|
||||||
"buy-link": "購入しに行く"
|
"buy-link": "購入しに行く",
|
||||||
|
"deeptrain-tip": "ヒント: Deeptrainがウォレットにリロードされたら、ここに戻ってクリックして適切なクレジットを購入してください"
|
||||||
},
|
},
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"title": "パック",
|
"title": "パック",
|
||||||
@ -710,5 +711,8 @@
|
|||||||
"i-know": "私は知っています",
|
"i-know": "私は知っています",
|
||||||
"submit": "提出",
|
"submit": "提出",
|
||||||
"empty": "空",
|
"empty": "空",
|
||||||
"exit": "離れる"
|
"exit": "離れる",
|
||||||
|
"model": "モデル",
|
||||||
|
"min-quota": "最低残高",
|
||||||
|
"your-quota": "残高"
|
||||||
}
|
}
|
@ -139,7 +139,8 @@
|
|||||||
"exchange-success-prompt": "Вы успешно использовали {{amount}} кредита (-ов).",
|
"exchange-success-prompt": "Вы успешно использовали {{amount}} кредита (-ов).",
|
||||||
"exchange-failed": "Сбой обмена",
|
"exchange-failed": "Сбой обмена",
|
||||||
"exchange-failed-prompt": "Не удалось погасить по {{reason}}",
|
"exchange-failed-prompt": "Не удалось погасить по {{reason}}",
|
||||||
"buy-link": "Перейти к покупке"
|
"buy-link": "Перейти к покупке",
|
||||||
|
"deeptrain-tip": "Совет: как только Deeptrain перезагрузится на ваш кошелек, вернитесь сюда и нажмите, чтобы купить соответствующие кредиты"
|
||||||
},
|
},
|
||||||
"pkg": {
|
"pkg": {
|
||||||
"title": "Пакеты",
|
"title": "Пакеты",
|
||||||
@ -710,5 +711,8 @@
|
|||||||
"i-know": "Мне известно о",
|
"i-know": "Мне известно о",
|
||||||
"submit": "передавать",
|
"submit": "передавать",
|
||||||
"empty": "Пусто",
|
"empty": "Пусто",
|
||||||
"exit": "Закрыть"
|
"exit": "Закрыть",
|
||||||
|
"model": "модель",
|
||||||
|
"min-quota": "Минимальный баланс",
|
||||||
|
"your-quota": "Ваш баланс"
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user