mirror of
https://github.com/coaidev/coai.git
synced 2025-05-22 14:30:14 +09:00
update mobile adapter and store
This commit is contained in:
parent
3e03da83c6
commit
fcec3d8a4a
@ -61,7 +61,7 @@ export const modelColorMapper: Record<string, string> = {
|
|||||||
"code-llama-13b": "#01a9f0",
|
"code-llama-13b": "#01a9f0",
|
||||||
"code-llama-7b": "#01a9f0",
|
"code-llama-7b": "#01a9f0",
|
||||||
|
|
||||||
"hunyuan": "#0052d9"
|
hunyuan: "#0052d9",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getModelColor(model: string): string {
|
export function getModelColor(model: string): string {
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
height: max-content;
|
height: max-content;
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
|
|
||||||
@media (max-width: 668px) {
|
@media (max-width: 940px) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
||||||
@ -103,6 +103,7 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding: 1rem 1.5rem;
|
padding: 1rem 1.5rem;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.chart-box {
|
.chart-box {
|
||||||
width: calc(50% - 1rem);
|
width: calc(50% - 1rem);
|
||||||
@ -116,5 +117,13 @@
|
|||||||
border: 1px solid hsl(var(--border));
|
border: 1px solid hsl(var(--border));
|
||||||
user-select: none;
|
user-select: none;
|
||||||
box-shadow: 0 0 1rem 0 hsla(var(--foreground), 0.1);
|
box-shadow: 0 0 1rem 0 hsla(var(--foreground), 0.1);
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mobile {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -36,12 +36,35 @@
|
|||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 4px;
|
||||||
|
height: 2rem;
|
||||||
|
background: hsl(var(--text));
|
||||||
|
border-radius: var(--radius);
|
||||||
|
margin-right: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: 0.25s;
|
||||||
|
transition-property: opacity, margin-right;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--conversation-card-hover);
|
background: var(--conversation-card-hover);
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
opacity: .25;
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: var(--conversation-card-active);
|
background: var(--conversation-card-active);
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
opacity: 1;
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
--border: 37 26% 83%;
|
--border: 37 26% 83%;
|
||||||
--border-hover: 37 26% 78%;
|
--border-hover: 37 26% 78%;
|
||||||
|
--border-active: 37 26% 73%;
|
||||||
--input: 37 26% 90%;
|
--input: 37 26% 90%;
|
||||||
--input-unread: 37 26% 70%;
|
--input-unread: 37 26% 70%;
|
||||||
--ring: 222.2 84% 4.9%;
|
--ring: 222.2 84% 4.9%;
|
||||||
@ -80,6 +81,7 @@
|
|||||||
|
|
||||||
--border: 240 3.7% 15.9%;
|
--border: 240 3.7% 15.9%;
|
||||||
--border-hover: 240 3.7% 20.9%;
|
--border-hover: 240 3.7% 20.9%;
|
||||||
|
--border-active: 240 3.7% 25.9%;
|
||||||
--input: 240 3.7% 15.9%;
|
--input: 240 3.7% 15.9%;
|
||||||
--input-unread: 240 3.7% 50%;
|
--input-unread: 240 3.7% 50%;
|
||||||
--ring: 240 4.9% 83.9%;
|
--ring: 240 4.9% 83.9%;
|
||||||
|
@ -122,11 +122,11 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
border: 1px solid hsl(var(--border));
|
border: 1px solid hsl(var(--border-hover));
|
||||||
transition: 0.25s linear;
|
transition: 0.25s linear;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: hsl(var(--border-hover));
|
border-color: hsl(var(--border-active));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { selectMenu } from "@/store/menu.ts";
|
import { closeMenu, selectMenu } from "@/store/menu.ts";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { LayoutDashboard, Settings, Users } from "lucide-react";
|
import { LayoutDashboard, Settings, Users } from "lucide-react";
|
||||||
import router from "@/router.tsx";
|
import router from "@/router.tsx";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { mobile } from "@/utils/device.ts";
|
||||||
|
|
||||||
type MenuItemProps = {
|
type MenuItemProps = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -14,6 +15,7 @@ type MenuItemProps = {
|
|||||||
|
|
||||||
function MenuItem({ title, icon, path }: MenuItemProps) {
|
function MenuItem({ title, icon, path }: MenuItemProps) {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
const active = useMemo(
|
const active = useMemo(
|
||||||
() =>
|
() =>
|
||||||
location.pathname === `/admin${path}` ||
|
location.pathname === `/admin${path}` ||
|
||||||
@ -21,11 +23,13 @@ function MenuItem({ title, icon, path }: MenuItemProps) {
|
|||||||
[location.pathname, path],
|
[location.pathname, path],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const redirect = async () => {
|
||||||
|
if (mobile) dispatch(closeMenu());
|
||||||
|
await router.navigate(`/admin${path}`);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={`menu-item ${active ? "active" : ""}`} onClick={redirect}>
|
||||||
className={`menu-item ${active ? "active" : ""}`}
|
|
||||||
onClick={() => router.navigate(`/admin${path}`)}
|
|
||||||
>
|
|
||||||
<div className={`menu-item-icon`}>{icon}</div>
|
<div className={`menu-item-icon`}>{icon}</div>
|
||||||
<div className={`menu-item-title`}>{title}</div>
|
<div className={`menu-item-title`}>{title}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { logout, selectUsername } from "@/store/auth.ts";
|
import { logout, selectAdmin, selectUsername } from "@/store/auth.ts";
|
||||||
import { openDialog as openQuotaDialog, quotaSelector } from "@/store/quota.ts";
|
import { openDialog as openQuotaDialog, quotaSelector } from "@/store/quota.ts";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@ -19,12 +19,14 @@ import {
|
|||||||
Gift,
|
Gift,
|
||||||
ListStart,
|
ListStart,
|
||||||
Plug,
|
Plug,
|
||||||
|
Shield,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { openDialog as openSub } from "@/store/subscription.ts";
|
import { openDialog as openSub } from "@/store/subscription.ts";
|
||||||
import { openDialog as openPackageDialog } from "@/store/package.ts";
|
import { openDialog as openPackageDialog } from "@/store/package.ts";
|
||||||
import { openDialog as openInvitationDialog } from "@/store/invitation.ts";
|
import { openDialog as openInvitationDialog } from "@/store/invitation.ts";
|
||||||
import { openDialog as openSharingDialog } from "@/store/sharing.ts";
|
import { openDialog as openSharingDialog } from "@/store/sharing.ts";
|
||||||
import { openDialog as openApiDialog } from "@/store/api.ts";
|
import { openDialog as openApiDialog } from "@/store/api.ts";
|
||||||
|
import router from "@/router.tsx";
|
||||||
|
|
||||||
type MenuBarProps = {
|
type MenuBarProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -36,6 +38,7 @@ function MenuBar({ children, className }: MenuBarProps) {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const username = useSelector(selectUsername);
|
const username = useSelector(selectUsername);
|
||||||
const quota = useSelector(quotaSelector);
|
const quota = useSelector(quotaSelector);
|
||||||
|
const admin = useSelector(selectAdmin);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@ -71,6 +74,12 @@ function MenuBar({ children, className }: MenuBarProps) {
|
|||||||
<Plug className={`h-4 w-4 mr-1`} />
|
<Plug className={`h-4 w-4 mr-1`} />
|
||||||
{t("api.title")}
|
{t("api.title")}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
{admin && (
|
||||||
|
<DropdownMenuItem onClick={() => router.navigate("/admin")}>
|
||||||
|
<Shield className={`h-4 w-4 mr-1`} />
|
||||||
|
{t("admin.users")}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Button
|
<Button
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
FolderKanban,
|
FolderKanban,
|
||||||
Link,
|
Link,
|
||||||
Newspaper,
|
Newspaper,
|
||||||
Shield,
|
|
||||||
Users2,
|
Users2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import router from "@/router.tsx";
|
import router from "@/router.tsx";
|
||||||
@ -21,10 +20,8 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog.tsx";
|
} from "@/components/ui/dialog.tsx";
|
||||||
import { getLanguage } from "@/i18n.ts";
|
import { getLanguage } from "@/i18n.ts";
|
||||||
import { selectAdmin } from "@/store/auth.ts";
|
|
||||||
|
|
||||||
function ChatSpace() {
|
function ChatSpace() {
|
||||||
const admin = useSelector(selectAdmin);
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const subscription = useSelector(isSubscribedSelector);
|
const subscription = useSelector(isSubscribedSelector);
|
||||||
@ -85,12 +82,6 @@ function ChatSpace() {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<div className={`space-footer`}>
|
<div className={`space-footer`}>
|
||||||
{admin && (
|
|
||||||
<p>
|
|
||||||
<Shield className={`h-3 w-3 mr-1`} />
|
|
||||||
<a onClick={() => router.navigate("/admin")}>{t("admin.users")}</a>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<p>
|
<p>
|
||||||
<Link className={`h-3 w-3 mr-1`} />
|
<Link className={`h-3 w-3 mr-1`} />
|
||||||
<a
|
<a
|
||||||
|
@ -14,24 +14,22 @@ export function parseProgressbar(data: string) {
|
|||||||
<p className={`text-primary select-none text-center`}>
|
<p className={`text-primary select-none text-center`}>
|
||||||
Generating: {progress < 0 ? 0 : progress.toFixed()}%
|
Generating: {progress < 0 ? 0 : progress.toFixed()}%
|
||||||
</p>
|
</p>
|
||||||
{
|
{progress > 0 && (
|
||||||
progress > 0 && (
|
<div
|
||||||
<div
|
className={`progressbar relative h-4 w-full overflow-hidden rounded-full bg-muted min-w-[20vw]`}
|
||||||
className={`progressbar relative h-4 w-full overflow-hidden rounded-full bg-muted min-w-[20vw]`}
|
role={`progressbar`}
|
||||||
role={`progressbar`}
|
aria-valuemin={0}
|
||||||
aria-valuemin={0}
|
aria-valuemax={100}
|
||||||
aria-valuemax={100}
|
aria-valuenow={progress}
|
||||||
aria-valuenow={progress}
|
data-max={100}
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
className={`h-full w-full flex-1 bg-primary transition-all`}
|
||||||
|
style={{ transform: `translateX(-${100 - progress}%)` }}
|
||||||
data-max={100}
|
data-max={100}
|
||||||
>
|
/>
|
||||||
<p
|
</div>
|
||||||
className={`h-full w-full flex-1 bg-primary transition-all`}
|
)}
|
||||||
style={{ transform: `translateX(-${100 - progress}%)` }}
|
|
||||||
data-max={100}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from "@/utils/env.ts";
|
} from "@/utils/env.ts";
|
||||||
import { getMemory } from "@/utils/memory.ts";
|
import { getMemory } from "@/utils/memory.ts";
|
||||||
|
|
||||||
export const version = "3.6.22";
|
export const version = "3.6.23";
|
||||||
export const dev: boolean = getDev();
|
export const dev: boolean = getDev();
|
||||||
export const deploy: boolean = true;
|
export const deploy: boolean = true;
|
||||||
export let rest_api: string = getRestApi(deploy);
|
export let rest_api: string = getRestApi(deploy);
|
||||||
|
@ -14,14 +14,15 @@ import {
|
|||||||
certSelector,
|
certSelector,
|
||||||
closeDialog,
|
closeDialog,
|
||||||
dialogSelector,
|
dialogSelector,
|
||||||
refreshPackageTask,
|
refreshPackage,
|
||||||
setDialog,
|
setDialog,
|
||||||
teenagerSelector,
|
teenagerSelector,
|
||||||
} from "@/store/package.ts";
|
} from "@/store/package.ts";
|
||||||
import { useEffect } from "react";
|
|
||||||
import { Gift } from "lucide-react";
|
import { Gift } from "lucide-react";
|
||||||
import { Separator } from "@/components/ui/separator.tsx";
|
import { Separator } from "@/components/ui/separator.tsx";
|
||||||
import { Badge } from "@/components/ui/badge.tsx";
|
import { Badge } from "@/components/ui/badge.tsx";
|
||||||
|
import { useEffectAsync } from "@/utils/hook.ts";
|
||||||
|
import { selectAuthenticated } from "@/store/auth.ts";
|
||||||
|
|
||||||
function Package() {
|
function Package() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -29,10 +30,15 @@ function Package() {
|
|||||||
const open = useSelector(dialogSelector);
|
const open = useSelector(dialogSelector);
|
||||||
const cert = useSelector(certSelector);
|
const cert = useSelector(certSelector);
|
||||||
const teenager = useSelector(teenagerSelector);
|
const teenager = useSelector(teenagerSelector);
|
||||||
|
const auth = useSelector(selectAuthenticated);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffectAsync(async () => {
|
||||||
refreshPackageTask(dispatch);
|
if (!auth) return;
|
||||||
}, []);
|
const task = setInterval(() => refreshPackage(dispatch), 20000);
|
||||||
|
await refreshPackage(dispatch);
|
||||||
|
|
||||||
|
return () => clearInterval(task);
|
||||||
|
}, [auth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={(open) => dispatch(setDialog(open))}>
|
<Dialog open={open} onOpenChange={(open) => dispatch(setDialog(open))}>
|
||||||
|
@ -2,11 +2,11 @@ import { useDispatch, useSelector } from "react-redux";
|
|||||||
import {
|
import {
|
||||||
closeDialog,
|
closeDialog,
|
||||||
dialogSelector,
|
dialogSelector,
|
||||||
refreshQuotaTask,
|
refreshQuota,
|
||||||
setDialog,
|
setDialog,
|
||||||
} from "@/store/quota.ts";
|
} from "@/store/quota.ts";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@ -40,6 +40,8 @@ import {
|
|||||||
import { AlertDialogTitle } from "@radix-ui/react-alert-dialog";
|
import { AlertDialogTitle } from "@radix-ui/react-alert-dialog";
|
||||||
import { buyQuota } from "@/conversation/addition.ts";
|
import { buyQuota } from "@/conversation/addition.ts";
|
||||||
import { useToast } from "@/components/ui/use-toast.ts";
|
import { useToast } from "@/components/ui/use-toast.ts";
|
||||||
|
import { useEffectAsync } from "@/utils/hook.ts";
|
||||||
|
import { selectAuthenticated } from "@/store/auth.ts";
|
||||||
|
|
||||||
type AmountComponentProps = {
|
type AmountComponentProps = {
|
||||||
amount: number;
|
amount: number;
|
||||||
@ -78,10 +80,16 @@ function Quota() {
|
|||||||
const [current, setCurrent] = useState(1);
|
const [current, setCurrent] = useState(1);
|
||||||
const [amount, setAmount] = useState(10);
|
const [amount, setAmount] = useState(10);
|
||||||
const open = useSelector(dialogSelector);
|
const open = useSelector(dialogSelector);
|
||||||
|
const auth = useSelector(selectAuthenticated);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
useEffect(() => {
|
useEffectAsync(async () => {
|
||||||
refreshQuotaTask(dispatch);
|
if (!auth) return;
|
||||||
}, []);
|
const task = setInterval(() => refreshQuota(dispatch), 5000);
|
||||||
|
await refreshQuota(dispatch);
|
||||||
|
|
||||||
|
return () => clearInterval(task);
|
||||||
|
}, [auth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
expiredSelector,
|
expiredSelector,
|
||||||
isSubscribedSelector,
|
isSubscribedSelector,
|
||||||
refreshSubscription,
|
refreshSubscription,
|
||||||
refreshSubscriptionTask,
|
|
||||||
setDialog,
|
setDialog,
|
||||||
usageSelector,
|
usageSelector,
|
||||||
} from "@/store/subscription.ts";
|
} from "@/store/subscription.ts";
|
||||||
@ -20,7 +19,7 @@ import {
|
|||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useToast } from "@/components/ui/use-toast.ts";
|
import { useToast } from "@/components/ui/use-toast.ts";
|
||||||
import React, { useEffect } from "react";
|
import React from "react";
|
||||||
import "@/assets/pages/subscription.less";
|
import "@/assets/pages/subscription.less";
|
||||||
import {
|
import {
|
||||||
BookText,
|
BookText,
|
||||||
@ -52,6 +51,8 @@ import {
|
|||||||
} from "@/components/ui/select.tsx";
|
} from "@/components/ui/select.tsx";
|
||||||
import { Badge } from "@/components/ui/badge.tsx";
|
import { Badge } from "@/components/ui/badge.tsx";
|
||||||
import { buySubscription } from "@/conversation/addition.ts";
|
import { buySubscription } from "@/conversation/addition.ts";
|
||||||
|
import { useEffectAsync } from "@/utils/hook.ts";
|
||||||
|
import { selectAuthenticated } from "@/store/auth.ts";
|
||||||
|
|
||||||
function calc_prize(month: number): number {
|
function calc_prize(month: number): number {
|
||||||
const base = 32 * month;
|
const base = 32 * month;
|
||||||
@ -169,10 +170,16 @@ function Subscription() {
|
|||||||
const enterprise = useSelector(enterpriseSelector);
|
const enterprise = useSelector(enterpriseSelector);
|
||||||
const expired = useSelector(expiredSelector);
|
const expired = useSelector(expiredSelector);
|
||||||
const usage = useSelector(usageSelector);
|
const usage = useSelector(usageSelector);
|
||||||
|
const auth = useSelector(selectAuthenticated);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
useEffect(() => {
|
useEffectAsync(async () => {
|
||||||
refreshSubscriptionTask(dispatch);
|
if (!auth) return;
|
||||||
}, []);
|
const task = setInterval(() => refreshSubscription(dispatch), 10000);
|
||||||
|
await refreshSubscription(dispatch);
|
||||||
|
|
||||||
|
return () => clearInterval(task);
|
||||||
|
}, [auth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
|
@ -7,12 +7,13 @@ import {
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import InvitationTable from "@/components/admin/InvitationTable.tsx";
|
import InvitationTable from "@/components/admin/InvitationTable.tsx";
|
||||||
import UserTable from "@/components/admin/UserTable.tsx";
|
import UserTable from "@/components/admin/UserTable.tsx";
|
||||||
|
import {mobile} from "@/utils/device.ts";
|
||||||
|
|
||||||
function Users() {
|
function Users() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`user-interface`}>
|
<div className={`user-interface ${mobile ? 'mobile' : ''}`}>
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle>{t("admin.users")}</CardTitle>
|
<CardTitle>{t("admin.users")}</CardTitle>
|
||||||
|
@ -42,17 +42,7 @@ 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;
|
||||||
|
|
||||||
const refreshPackage = async (dispatch: AppDispatch) => {
|
export const refreshPackage = async (dispatch: AppDispatch) => {
|
||||||
const current = new Date().getTime(); //@ts-ignore
|
|
||||||
if (window.hasOwnProperty("package") && current - window.package < 2500)
|
|
||||||
return; //@ts-ignore
|
|
||||||
window.package = current;
|
|
||||||
|
|
||||||
const response = await getPackage();
|
const response = await getPackage();
|
||||||
if (response.status) dispatch(refreshState(response));
|
if (response.status) dispatch(refreshState(response));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const refreshPackageTask = (dispatch: AppDispatch) => {
|
|
||||||
setInterval(() => refreshPackage(dispatch), 20000);
|
|
||||||
refreshPackage(dispatch).then();
|
|
||||||
};
|
|
||||||
|
@ -50,16 +50,7 @@ export const quotaValueSelector = (state: RootState): number =>
|
|||||||
export const quotaSelector = (state: RootState): string =>
|
export const quotaSelector = (state: RootState): string =>
|
||||||
state.quota.quota.toFixed(2);
|
state.quota.quota.toFixed(2);
|
||||||
|
|
||||||
const refreshQuota = async (dispatch: AppDispatch) => {
|
export const refreshQuota = async (dispatch: AppDispatch) => {
|
||||||
const current = new Date().getTime(); //@ts-ignore
|
|
||||||
if (window.hasOwnProperty("quota") && current - window.quota < 5000) return; //@ts-ignore
|
|
||||||
window.quota = current;
|
|
||||||
|
|
||||||
const response = await axios.get("/quota");
|
const response = await axios.get("/quota");
|
||||||
if (response.data.status) dispatch(setQuota(response.data.quota));
|
if (response.data.status) dispatch(setQuota(response.data.quota));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const refreshQuotaTask = (dispatch: AppDispatch) => {
|
|
||||||
setInterval(() => refreshQuota(dispatch), 8000);
|
|
||||||
refreshQuota(dispatch).then();
|
|
||||||
};
|
|
||||||
|
@ -55,19 +55,6 @@ export const enterpriseSelector = (state: any): boolean =>
|
|||||||
state.subscription.enterprise;
|
state.subscription.enterprise;
|
||||||
|
|
||||||
export const refreshSubscription = async (dispatch: AppDispatch) => {
|
export const refreshSubscription = async (dispatch: AppDispatch) => {
|
||||||
const current = new Date().getTime(); //@ts-ignore
|
|
||||||
if (
|
|
||||||
window.hasOwnProperty("subscription") && //@ts-ignore
|
|
||||||
current - window.subscription < 15000
|
|
||||||
)
|
|
||||||
return; //@ts-ignore
|
|
||||||
window.subscription = current;
|
|
||||||
|
|
||||||
const response = await getSubscription();
|
const response = await getSubscription();
|
||||||
if (response.status) dispatch(updateSubscription(response));
|
if (response.status) dispatch(updateSubscription(response));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const refreshSubscriptionTask = (dispatch: AppDispatch) => {
|
|
||||||
setInterval(() => refreshSubscription(dispatch), 20000);
|
|
||||||
refreshSubscription(dispatch).then();
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user