mirror of
https://github.com/coaidev/coai.git
synced 2025-05-19 21:10:18 +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;
|
||||
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 {
|
||||
list-style: inherit;
|
||||
}
|
||||
|
@ -9,16 +9,22 @@ import rehypeRaw from "rehype-raw";
|
||||
import { parseFile } from "./plugins/file.tsx";
|
||||
import "@/assets/markdown/all.less";
|
||||
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 openSubscriptionDialog } from "@/store/subscription.ts";
|
||||
import { AppDispatch } from "@/store";
|
||||
import {
|
||||
CalendarPlus,
|
||||
Cloud,
|
||||
CloudCog,
|
||||
Cloudy,
|
||||
Codepen,
|
||||
Codesandbox,
|
||||
Copy,
|
||||
Github,
|
||||
Maximize,
|
||||
Package,
|
||||
Plus,
|
||||
RefreshCcwDot,
|
||||
Twitter,
|
||||
Wand2,
|
||||
@ -41,6 +47,8 @@ import {
|
||||
} from "@/components/ui/dialog.tsx";
|
||||
import { DialogClose } from "@radix-ui/react-dialog";
|
||||
import { posterEvent } from "@/events/poster.ts";
|
||||
import { appLogo } from "@/conf/env.ts";
|
||||
import { subscriptionDataSelector } from "@/store/globals.ts";
|
||||
|
||||
type MarkdownProps = {
|
||||
children: string;
|
||||
@ -109,6 +117,8 @@ function MarkdownContent({
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
const subscription = useSelector(subscriptionDataSelector);
|
||||
|
||||
useEffect(() => {
|
||||
document.querySelectorAll(".file-instance").forEach((el) => {
|
||||
const parent = el.parentElement as HTMLElement;
|
||||
@ -130,6 +140,81 @@ function MarkdownContent({
|
||||
children={children}
|
||||
skipHtml={!acceptHtml}
|
||||
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 }) {
|
||||
const url: string = href?.toString() || "";
|
||||
|
||||
|
@ -287,7 +287,13 @@ function QuotaDialog() {
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</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`}>
|
||||
<Input
|
||||
className={`redeem-input mr-2 text-center`}
|
||||
|
@ -48,6 +48,9 @@
|
||||
"i-know": "我已知晓",
|
||||
"empty": "空空如也",
|
||||
"exit": "离开",
|
||||
"model": "模型",
|
||||
"min-quota": "最低余额",
|
||||
"your-quota": "您的余额",
|
||||
"auth": {
|
||||
"username": "用户名",
|
||||
"username-placeholder": "请输入用户名",
|
||||
@ -184,6 +187,7 @@
|
||||
"success-prompt": "您已成功购买 {{amount}} 点数。",
|
||||
"redeem": "兑换",
|
||||
"redeem-placeholder": "请输入兑换码",
|
||||
"deeptrain-tip": "提示:在 Deeptrain 充值至钱包后,请返回此处,点击购买相应点数",
|
||||
"exchange-success": "兑换成功",
|
||||
"exchange-success-prompt": "您已成功兑换 {{amount}} 点数。",
|
||||
"failed": "购买失败",
|
||||
|
@ -139,7 +139,8 @@
|
||||
"exchange-success-prompt": "You have successfully redeemed {{amount}} credits.",
|
||||
"exchange-failed": "Failed",
|
||||
"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": {
|
||||
"title": "Packages",
|
||||
@ -710,5 +711,8 @@
|
||||
"i-know": "Yes, I understand.",
|
||||
"submit": "Send",
|
||||
"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-failed": "引き換えに失敗しました",
|
||||
"exchange-failed-prompt": "{{reason}}のため、引き換えに失敗しました",
|
||||
"buy-link": "購入しに行く"
|
||||
"buy-link": "購入しに行く",
|
||||
"deeptrain-tip": "ヒント: Deeptrainがウォレットにリロードされたら、ここに戻ってクリックして適切なクレジットを購入してください"
|
||||
},
|
||||
"pkg": {
|
||||
"title": "パック",
|
||||
@ -710,5 +711,8 @@
|
||||
"i-know": "私は知っています",
|
||||
"submit": "提出",
|
||||
"empty": "空",
|
||||
"exit": "離れる"
|
||||
"exit": "離れる",
|
||||
"model": "モデル",
|
||||
"min-quota": "最低残高",
|
||||
"your-quota": "残高"
|
||||
}
|
@ -139,7 +139,8 @@
|
||||
"exchange-success-prompt": "Вы успешно использовали {{amount}} кредита (-ов).",
|
||||
"exchange-failed": "Сбой обмена",
|
||||
"exchange-failed-prompt": "Не удалось погасить по {{reason}}",
|
||||
"buy-link": "Перейти к покупке"
|
||||
"buy-link": "Перейти к покупке",
|
||||
"deeptrain-tip": "Совет: как только Deeptrain перезагрузится на ваш кошелек, вернитесь сюда и нажмите, чтобы купить соответствующие кредиты"
|
||||
},
|
||||
"pkg": {
|
||||
"title": "Пакеты",
|
||||
@ -710,5 +711,8 @@
|
||||
"i-know": "Мне известно о",
|
||||
"submit": "передавать",
|
||||
"empty": "Пусто",
|
||||
"exit": "Закрыть"
|
||||
"exit": "Закрыть",
|
||||
"model": "модель",
|
||||
"min-quota": "Минимальный баланс",
|
||||
"your-quota": "Ваш баланс"
|
||||
}
|
Loading…
Reference in New Issue
Block a user