mirror of
https://github.com/coaidev/coai.git
synced 2025-06-07 06:10:22 +09:00
update package
This commit is contained in:
parent
f9aac47b67
commit
0d53961c18
@ -28,7 +28,8 @@ import { Toaster } from "./components/ui/toaster.tsx";
|
|||||||
import {login, tokenField} from "./conf.ts";
|
import {login, tokenField} from "./conf.ts";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Quota from "./routes/Quota.tsx";
|
import Quota from "./routes/Quota.tsx";
|
||||||
import {openDialog} from "./store/quota.ts";
|
import { openDialog as openQuotaDialog } from "./store/quota.ts";
|
||||||
|
import { openDialog as openPackageDialog } from "./store/package.ts";
|
||||||
import Package from "./routes/Package.tsx";
|
import Package from "./routes/Package.tsx";
|
||||||
|
|
||||||
function Settings() {
|
function Settings() {
|
||||||
@ -49,7 +50,8 @@ function Settings() {
|
|||||||
{username}
|
{username}
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem onClick={() => dispatch(openDialog())}>{t("quota")}</DropdownMenuItem>
|
<DropdownMenuItem onClick={() => dispatch(openQuotaDialog())}>{t("quota")}</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem onClick={() => dispatch(openPackageDialog())}>{t("pkg.title")}</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Button
|
<Button
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
.package-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 24px 4px !important;
|
||||||
|
gap: 18px;
|
||||||
|
|
||||||
|
.package {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.package-title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: hsl(var(--text));
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-content {
|
||||||
|
font-size: 14px;
|
||||||
|
color: hsl(var(--text-secondary));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
app/src/components/ui/badge.tsx
Normal file
36
app/src/components/ui/badge.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "./lib/utils"
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||||
|
secondary:
|
||||||
|
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
destructive:
|
||||||
|
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||||
|
outline: "text-foreground",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export interface BadgeProps
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement>,
|
||||||
|
VariantProps<typeof badgeVariants> {}
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants }
|
@ -26,7 +26,11 @@ export async function buyQuota(
|
|||||||
export async function getPackage(): Promise<PackageResponse> {
|
export async function getPackage(): Promise<PackageResponse> {
|
||||||
try {
|
try {
|
||||||
const resp = await axios.get(`/package`);
|
const resp = await axios.get(`/package`);
|
||||||
return resp.data as PackageResponse;
|
return {
|
||||||
|
status: resp.data.status,
|
||||||
|
cert: resp.data.data.cert,
|
||||||
|
teenager: resp.data.data.teenager,
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.debug(e);
|
console.debug(e);
|
||||||
return { status: false, cert: false, teenager: false };
|
return { status: false, cert: false, teenager: false };
|
||||||
|
@ -77,12 +77,16 @@ const resources = {
|
|||||||
},
|
},
|
||||||
pkg: {
|
pkg: {
|
||||||
"title": "Packages",
|
"title": "Packages",
|
||||||
"go": "Go to",
|
"go": "Go to Verify",
|
||||||
"verify": "Verify",
|
|
||||||
"cert": "Certification Package",
|
"cert": "Certification Package",
|
||||||
"cert-desc": "After real-name certification, you can get 50 points (worth 5 CNY)",
|
"cert-desc": "After real-name certification, you can get 50 points (worth 5 CNY)",
|
||||||
"teen": "Teenager Package",
|
"teen": "Teenager Package",
|
||||||
"teen-desc": "After real-name certification, teenagers (18 years old and below) can get an additional 150 points (worth 15 CNY)",
|
"teen-desc": "After real-name certification, teenagers (18 years old and below) can get an additional 150 points (worth 15 CNY)",
|
||||||
|
"close": "Close",
|
||||||
|
state: {
|
||||||
|
"true": "Received",
|
||||||
|
"false": "Not Received",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -152,15 +156,19 @@ const resources = {
|
|||||||
},
|
},
|
||||||
pkg: {
|
pkg: {
|
||||||
"title": "礼包",
|
"title": "礼包",
|
||||||
"go": "前往",
|
"go": "前往实名认证",
|
||||||
"verify": "实名认证",
|
|
||||||
"cert": "实名认证礼包",
|
"cert": "实名认证礼包",
|
||||||
"cert-desc": "实名认证后可获得 50 点数 (价值 5 元)",
|
"cert-desc": "实名认证后可获得 50 点数 (价值 5 元)",
|
||||||
"teen": "未成年人福利",
|
"teen": "未成年人福利",
|
||||||
"teen-desc": "实名认证后未成年人(18 周岁及以下)可额外获得 150 点数 (价值 15 元)",
|
"teen-desc": "实名认证后未成年人(18 周岁及以下)可额外获得 150 点数 (价值 15 元)",
|
||||||
|
"close": "关闭",
|
||||||
|
state: {
|
||||||
|
"true": "已领取",
|
||||||
|
"false": "无法领取",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const supportedLanguages = ["en", "cn"];
|
export const supportedLanguages = ["en", "cn"];
|
||||||
|
@ -10,28 +10,64 @@ import {Button} from "../components/ui/button.tsx";
|
|||||||
import "../assets/package.less";
|
import "../assets/package.less";
|
||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useDispatch, useSelector} from "react-redux";
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
import {dialogSelector, refreshPackage} from "../store/package.ts";
|
import {
|
||||||
|
certSelector,
|
||||||
|
closeDialog,
|
||||||
|
dialogSelector,
|
||||||
|
refreshPackageTask,
|
||||||
|
setDialog,
|
||||||
|
teenagerSelector
|
||||||
|
} from "../store/package.ts";
|
||||||
import {useEffect} from "react";
|
import {useEffect} from "react";
|
||||||
|
import {Gift} from "lucide-react";
|
||||||
|
import {Separator} from "../components/ui/separator.tsx";
|
||||||
|
import {Badge} from "../components/ui/badge.tsx";
|
||||||
|
|
||||||
function Package() {
|
function Package() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const open = useSelector(dialogSelector);
|
const open = useSelector(dialogSelector);
|
||||||
|
const cert = useSelector(certSelector);
|
||||||
|
const teenager = useSelector(teenagerSelector);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshPackage(dispatch);
|
refreshPackageTask(dispatch);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog open={open} onOpenChange={
|
||||||
|
(open) => dispatch(setDialog(open))
|
||||||
|
}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Edit profile</DialogTitle>
|
<DialogTitle>{ t('pkg.title') }</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription asChild>
|
||||||
Make changes to your profile here. Click save when you're done.
|
<div className={`package-wrapper`}>
|
||||||
|
<div className={`package`}>
|
||||||
|
<div className={`package-title`}>
|
||||||
|
<Gift className={`h-4 w-4`} />
|
||||||
|
{ t('pkg.cert') }
|
||||||
|
<Badge variant={cert ? 'default' : 'outline'}>{ t(`pkg.state.${Boolean(cert)}`) }</Badge>
|
||||||
|
</div>
|
||||||
|
<div className={`package-content`}>{ t('pkg.cert-desc') }</div>
|
||||||
|
</div>
|
||||||
|
<Separator orientation={`horizontal`} />
|
||||||
|
<div className={`package`}>
|
||||||
|
<div className={`package-title`}>
|
||||||
|
<Gift className={`h-4 w-4`} />
|
||||||
|
{ t('pkg.teen') }
|
||||||
|
<Badge variant={teenager ? 'default' : 'outline'}>{ t(`pkg.state.${Boolean(teenager)}`) }</Badge>
|
||||||
|
</div>
|
||||||
|
<div className={`package-content`}>{ t('pkg.teen-desc') }</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button>Save changes</Button>
|
<Button variant={`outline`} onClick={() => dispatch(closeDialog())}>{ t('pkg.close') }</Button>
|
||||||
|
<Button variant={`default`} onClick={
|
||||||
|
() => window.open("https://deeptrain.lightxi.com/home/package")
|
||||||
|
}>{ t('pkg.go') }</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -35,13 +35,16 @@ export const dialogSelector = (state: any): boolean => state.package.dialog;
|
|||||||
export const certSelector = (state: any): boolean => state.package.cert;
|
export const certSelector = (state: any): boolean => state.package.cert;
|
||||||
export const teenagerSelector = (state: any): boolean => state.package.teenager;
|
export const teenagerSelector = (state: any): boolean => state.package.teenager;
|
||||||
|
|
||||||
export const refreshPackage = (dispatch: any) => {
|
const refreshPackage = async (dispatch: any) => {
|
||||||
setInterval(async () => {
|
|
||||||
const current = new Date().getTime(); //@ts-ignore
|
const current = new Date().getTime(); //@ts-ignore
|
||||||
if (window.hasOwnProperty("package") && (current - window.package < 2500)) return; //@ts-ignore
|
if (window.hasOwnProperty("package") && (current - window.package < 2500)) return; //@ts-ignore
|
||||||
window.package = current;
|
window.package = current;
|
||||||
|
|
||||||
const response = await getPackage();
|
const response = await getPackage();
|
||||||
if (response.status) dispatch(refreshState(response));
|
if (response.status) dispatch(refreshState(response));
|
||||||
}, 10000);
|
}
|
||||||
|
|
||||||
|
export const refreshPackageTask = (dispatch: any) => {
|
||||||
|
setInterval(() => refreshPackage(dispatch), 5000);
|
||||||
|
refreshPackage(dispatch).then();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user