mirror of
https://github.com/coaidev/coai.git
synced 2025-05-29 18:00:14 +09:00
chore: update style of admin pages
This commit is contained in:
parent
4a3c957284
commit
fa8f5b5b64
@ -16,6 +16,14 @@
|
|||||||
height: max-content;
|
height: max-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-card {
|
||||||
|
border: 0 !important;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.object-id {
|
.object-id {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
border: 1px solid hsl(var(--border));
|
border: 1px solid hsl(var(--border));
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
|
||||||
|
.channel-id {
|
||||||
|
color: hsl(var(--text-secondary));
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: hsl(var(--border-hover));
|
border-color: hsl(var(--border-hover));
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
color: hsl(var(--text-secondary));
|
color: hsl(var(--text-secondary));
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: hsl(var(--card-hover));
|
color: hsl(var(--text));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
@ -54,12 +54,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-item-icon {
|
.menu-item-icon {
|
||||||
width: 1.5rem;
|
width: 1.25rem;
|
||||||
height: 1.5rem;
|
height: 1.25rem;
|
||||||
scale: 0.95;
|
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
--text: 0 0% 0%;
|
--text: 0 0% 0%;
|
||||||
--text-dark: 0 0% 100%;
|
--text-dark: 0 0% 100%;
|
||||||
--text-secondary: 0 0% 20%;
|
--text-secondary: 0 0% 35%;
|
||||||
--text-secondary-dark: 0 0% 80%;
|
--text-secondary-dark: 0 0% 80%;
|
||||||
|
|
||||||
--selection: 212 100% 41%;
|
--selection: 212 100% 41%;
|
||||||
|
@ -81,7 +81,9 @@ function ChannelTable({ display, setId, setEnabled }: ChannelTableProps) {
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{(data || []).map((chan, idx) => (
|
{(data || []).map((chan, idx) => (
|
||||||
<TableRow key={idx}>
|
<TableRow key={idx}>
|
||||||
<TableCell>{chan.id}</TableCell>
|
<TableCell className={`channel-id select-none`}>
|
||||||
|
#{chan.id}
|
||||||
|
</TableCell>
|
||||||
<TableCell>{chan.name}</TableCell>
|
<TableCell>{chan.name}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<TypeBadge type={chan.type} />
|
<TypeBadge type={chan.type} />
|
||||||
|
@ -3,6 +3,7 @@ import { useMemo } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { getModelColor } from "@/admin/colors.ts";
|
import { getModelColor } from "@/admin/colors.ts";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
|
import Tips from "@/components/Tips.tsx";
|
||||||
|
|
||||||
type ModelChartProps = {
|
type ModelChartProps = {
|
||||||
labels: string[];
|
labels: string[];
|
||||||
@ -70,7 +71,10 @@ function ModelChart({ labels, datasets, dark }: ModelChartProps) {
|
|||||||
return (
|
return (
|
||||||
<div className={`chart`}>
|
<div className={`chart`}>
|
||||||
<p className={`chart-title mb-2`}>
|
<p className={`chart-title mb-2`}>
|
||||||
<p>{t("admin.model-chart")}</p>
|
<p className={`flex flex-row items-center`}>
|
||||||
|
{t("admin.model-chart")}
|
||||||
|
<Tips content={t("admin.model-chart-tip")} />
|
||||||
|
</p>
|
||||||
{labels.length === 0 && (
|
{labels.length === 0 && (
|
||||||
<Loader2 className={`h-4 w-4 inline-block animate-spin`} />
|
<Loader2 className={`h-4 w-4 inline-block animate-spin`} />
|
||||||
)}
|
)}
|
||||||
|
@ -349,6 +349,7 @@
|
|||||||
"subscription-users": "订阅用户",
|
"subscription-users": "订阅用户",
|
||||||
"seat": "位",
|
"seat": "位",
|
||||||
"model-chart": "模型使用统计",
|
"model-chart": "模型使用统计",
|
||||||
|
"model-chart-tip": "Token 用量",
|
||||||
"request-chart": "请求量统计",
|
"request-chart": "请求量统计",
|
||||||
"billing-chart": "收入统计",
|
"billing-chart": "收入统计",
|
||||||
"error-chart": "错误统计",
|
"error-chart": "错误统计",
|
||||||
|
@ -467,7 +467,8 @@
|
|||||||
"custom-image": "Custom Image",
|
"custom-image": "Custom Image",
|
||||||
"custom-image-placeholder": "Please enter an image link",
|
"custom-image-placeholder": "Please enter an image link",
|
||||||
"update": "Update"
|
"update": "Update"
|
||||||
}
|
},
|
||||||
|
"model-chart-tip": "Token usage"
|
||||||
},
|
},
|
||||||
"mask": {
|
"mask": {
|
||||||
"title": "Mask Settings",
|
"title": "Mask Settings",
|
||||||
|
@ -467,7 +467,8 @@
|
|||||||
"custom-image": "カスタム画像",
|
"custom-image": "カスタム画像",
|
||||||
"custom-image-placeholder": "画像リンクを入力してください",
|
"custom-image-placeholder": "画像リンクを入力してください",
|
||||||
"update": "更新"
|
"update": "更新"
|
||||||
}
|
},
|
||||||
|
"model-chart-tip": "トークンの使用状況"
|
||||||
},
|
},
|
||||||
"mask": {
|
"mask": {
|
||||||
"title": "プリセット設定",
|
"title": "プリセット設定",
|
||||||
|
@ -467,7 +467,8 @@
|
|||||||
"custom-image": "Пользовательское изображение",
|
"custom-image": "Пользовательское изображение",
|
||||||
"custom-image-placeholder": "Введите ссылку на изображение",
|
"custom-image-placeholder": "Введите ссылку на изображение",
|
||||||
"update": "Обновить"
|
"update": "Обновить"
|
||||||
}
|
},
|
||||||
|
"model-chart-tip": "Использование токенов"
|
||||||
},
|
},
|
||||||
"mask": {
|
"mask": {
|
||||||
"title": "Настройки маски",
|
"title": "Настройки маски",
|
||||||
|
@ -13,7 +13,7 @@ import Register from "@/routes/Register.tsx";
|
|||||||
import Forgot from "@/routes/Forgot.tsx";
|
import Forgot from "@/routes/Forgot.tsx";
|
||||||
import { lazyFactor } from "@/utils/loader.tsx";
|
import { lazyFactor } from "@/utils/loader.tsx";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectAdmin, selectAuthenticated } from "@/store/auth.ts";
|
import { selectAdmin, selectAuthenticated, selectInit } from "@/store/auth.ts";
|
||||||
|
|
||||||
const Generation = lazyFactor(() => import("@/routes/Generation.tsx"));
|
const Generation = lazyFactor(() => import("@/routes/Generation.tsx"));
|
||||||
const Sharing = lazyFactor(() => import("@/routes/Sharing.tsx"));
|
const Sharing = lazyFactor(() => import("@/routes/Sharing.tsx"));
|
||||||
@ -183,43 +183,46 @@ const router = createBrowserRouter(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export function AuthRequired({ children }: { children: React.ReactNode }) {
|
export function AuthRequired({ children }: { children: React.ReactNode }) {
|
||||||
|
const init = useSelector(selectInit);
|
||||||
const authenticated = useSelector(selectAuthenticated);
|
const authenticated = useSelector(selectAuthenticated);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!authenticated) {
|
if (init && !authenticated) {
|
||||||
navigate("/login", { state: { from: location.pathname } });
|
navigate("/login", { state: { from: location.pathname } });
|
||||||
}
|
}
|
||||||
}, [authenticated]);
|
}, [init, authenticated]);
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AuthForbidden({ children }: { children: React.ReactNode }) {
|
export function AuthForbidden({ children }: { children: React.ReactNode }) {
|
||||||
|
const init = useSelector(selectInit);
|
||||||
const authenticated = useSelector(selectAuthenticated);
|
const authenticated = useSelector(selectAuthenticated);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (authenticated) {
|
if (init && authenticated) {
|
||||||
navigate("/", { state: { from: location.pathname } });
|
navigate("/", { state: { from: location.pathname } });
|
||||||
}
|
}
|
||||||
}, [authenticated]);
|
}, [init, authenticated]);
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AdminRequired({ children }: { children: React.ReactNode }) {
|
export function AdminRequired({ children }: { children: React.ReactNode }) {
|
||||||
|
const init = useSelector(selectInit);
|
||||||
const admin = useSelector(selectAdmin);
|
const admin = useSelector(selectAdmin);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!admin) {
|
if (init && !admin) {
|
||||||
navigate("/", { state: { from: location.pathname } });
|
navigate("/", { state: { from: location.pathname } });
|
||||||
}
|
}
|
||||||
}, [admin]);
|
}, [init, admin]);
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ function Broadcast() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div className={`broadcast`}>
|
<div className={`broadcast`}>
|
||||||
<Card className={`broadcast-card`}>
|
<Card className={`admin-card broadcast-card`}>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle>{t("admin.broadcast")}</CardTitle>
|
<CardTitle>{t("admin.broadcast")}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
@ -12,7 +12,7 @@ function Channel() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`channel`}>
|
<div className={`channel`}>
|
||||||
<Card className={`channel-card`}>
|
<Card className={`admin-card channel-card`}>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle>{t("admin.channel")}</CardTitle>
|
<CardTitle>{t("admin.channel")}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
@ -12,7 +12,7 @@ function Charge() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`charge`}>
|
<div className={`charge`}>
|
||||||
<Card className={`charge-card`}>
|
<Card className={`admin-card charge-card`}>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle>{t("admin.prize")}</CardTitle>
|
<CardTitle>{t("admin.prize")}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
@ -384,7 +384,7 @@ function Market() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`market`}>
|
<div className={`market`}>
|
||||||
<Card className={`market-card`}>
|
<Card className={`admin-card market-card`}>
|
||||||
<CardHeader className={`flex flex-row items-center select-none`}>
|
<CardHeader className={`flex flex-row items-center select-none`}>
|
||||||
<CardTitle>{t("admin.market.title")}</CardTitle>
|
<CardTitle>{t("admin.market.title")}</CardTitle>
|
||||||
<Button
|
<Button
|
||||||
|
@ -411,7 +411,7 @@ function System() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`system`}>
|
<div className={`system`}>
|
||||||
<Card className={`system-card`}>
|
<Card className={`admin-card system-card`}>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle>{t("admin.settings")}</CardTitle>
|
<CardTitle>{t("admin.settings")}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
@ -16,7 +16,7 @@ function Users() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`user-interface ${mobile ? "mobile" : ""}`}>
|
<div className={`user-interface ${mobile ? "mobile" : ""}`}>
|
||||||
<Card>
|
<Card className={`admin-card`}>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle>{t("admin.user")}</CardTitle>
|
<CardTitle>{t("admin.user")}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@ -24,7 +24,7 @@ function Users() {
|
|||||||
<UserTable />
|
<UserTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
<Card className={`admin-card`}>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle className={`flex items-center`}>
|
<CardTitle className={`flex items-center`}>
|
||||||
{t("admin.invitation-manage")}
|
{t("admin.invitation-manage")}
|
||||||
@ -38,7 +38,7 @@ function Users() {
|
|||||||
<InvitationTable />
|
<InvitationTable />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
<Card className={`admin-card`}>
|
||||||
<CardHeader className={`select-none`}>
|
<CardHeader className={`select-none`}>
|
||||||
<CardTitle className={`flex items-center`}>
|
<CardTitle className={`flex items-center`}>
|
||||||
{t("admin.invitation")}
|
{t("admin.invitation")}
|
||||||
|
Loading…
Reference in New Issue
Block a user