mirror of
https://github.com/coaidev/coai.git
synced 2025-05-21 05:50:14 +09:00
use lazy loading and admin pages (in dev)
This commit is contained in:
parent
d5f917191e
commit
5c08d04b89
10
app/src/assets/admin/all.less
Normal file
10
app/src/assets/admin/all.less
Normal file
@ -0,0 +1,10 @@
|
||||
@import "menu";
|
||||
|
||||
.admin-page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 56px);
|
||||
height: max-content;
|
||||
}
|
59
app/src/assets/admin/menu.less
Normal file
59
app/src/assets/admin/menu.less
Normal file
@ -0,0 +1,59 @@
|
||||
.admin-menu {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: var(--background-sidebar);
|
||||
transition: 0.225s ease-in-out;
|
||||
min-height: calc(100vh - 56px);
|
||||
transition-property: width, background, box-shadow, opacity;
|
||||
border-right: 0;
|
||||
opacity: 0;
|
||||
|
||||
&.close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.open {
|
||||
width: 260px;
|
||||
border-right: 1px solid hsl(var(--border));
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: calc(100% - 1.5rem);
|
||||
height: max-content;
|
||||
padding: 0.75rem 1rem;
|
||||
align-items: center;
|
||||
background: var(--conversation-card);
|
||||
margin: 0.75rem 0.75rem -0.25rem;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease-in-out;
|
||||
border-radius: var(--radius);
|
||||
font-size: 16px;
|
||||
|
||||
&:hover {
|
||||
background: var(--conversation-card-hover);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--conversation-card-hover);
|
||||
}
|
||||
|
||||
& > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
& svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ function Markdown({ children, className }: MarkdownProps) {
|
||||
code({ inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
const language = match ? match[1] : "";
|
||||
if (language) return parseFile(children.toString());
|
||||
if (language === "file") return parseFile(children.toString());
|
||||
return !inline && match ? (
|
||||
<div className={`markdown-syntax`}>
|
||||
<div className={`markdown-syntax-header`}>
|
||||
|
50
app/src/components/admin/MenuBar.tsx
Normal file
50
app/src/components/admin/MenuBar.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import {useSelector} from "react-redux";
|
||||
import {selectMenu} from "@/store/menu.ts";
|
||||
import React, {useEffect, useMemo, useState} from "react";
|
||||
import {LayoutDashboard, Settings} from "lucide-react";
|
||||
import router from "@/router.tsx";
|
||||
import {useLocation} from "react-router-dom";
|
||||
|
||||
type MenuItemProps = {
|
||||
title: string;
|
||||
icon: React.ReactNode;
|
||||
path: string;
|
||||
}
|
||||
|
||||
function MenuItem({ title, icon, path }: MenuItemProps) {
|
||||
const location = useLocation();
|
||||
const active = useMemo(() => (
|
||||
location.pathname === `/admin${path}` || (location.pathname + "/") === `/admin${path}`
|
||||
), [location.pathname, path]);
|
||||
|
||||
return (
|
||||
<div className={`menu-item ${active ? "active" : ""}`}
|
||||
onClick={() => router.navigate(`/admin${path}`)}
|
||||
>
|
||||
<div className={`menu-item-icon`}>
|
||||
{icon}
|
||||
</div>
|
||||
<div className={`menu-item-title`}>
|
||||
{title}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function MenuBar() {
|
||||
const open = useSelector(selectMenu);
|
||||
const [close, setClose] = useState(false);
|
||||
useEffect(() => {
|
||||
if (open) setClose(false);
|
||||
else setTimeout(() => setClose(true), 200);
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<div className={`admin-menu ${open ? "open" : ""} ${close ? "close" : ""}`}>
|
||||
<MenuItem title={"Dashboard"} icon={<LayoutDashboard />} path={"/"} />
|
||||
<MenuItem title={"Dashboard"} icon={<Settings />} path={"/config"} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MenuBar;
|
@ -15,12 +15,12 @@ import { useToast } from "@/components/ui/use-toast.ts";
|
||||
import { ToastAction } from "@/components/ui/toast.tsx";
|
||||
import { alignSelector, contextSelector } from "@/store/settings.ts";
|
||||
import { FileArray } from "@/conversation/file.ts";
|
||||
import WebToggle from "@/components/home/components/WebToggle.tsx";
|
||||
import WebToggle from "@/components/home/assemblies/WebToggle.tsx";
|
||||
import ChatSpace from "@/components/home/ChatSpace.tsx";
|
||||
import ChatFooter from "@/components/home/ChatFooter.tsx";
|
||||
import SendButton from "@/components/home/components/SendButton.tsx";
|
||||
import ChatInput from "@/components/home/components/ChatInput.tsx";
|
||||
import ScrollAction from "@/components/home/components/ScrollAction.tsx";
|
||||
import SendButton from "@/components/home/assemblies/SendButton.tsx";
|
||||
import ChatInput from "@/components/home/assemblies/ChatInput.tsx";
|
||||
import ScrollAction from "@/components/home/assemblies/ScrollAction.tsx";
|
||||
|
||||
function ChatWrapper() {
|
||||
const { t } = useTranslation();
|
||||
@ -127,7 +127,7 @@ function ChatWrapper() {
|
||||
<FileProvider value={files} onChange={setFiles} />
|
||||
<ChatInput
|
||||
className={align ? "align" : ""}
|
||||
ref={target}
|
||||
target={target}
|
||||
value={input}
|
||||
onValueChange={setInput}
|
||||
onEnterPressed={async () => await handleSend(auth, model, web)}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState } from "@/store";
|
||||
import { selectAuthenticated, selectUsername } from "@/store/auth.ts";
|
||||
import { selectCurrent, selectHistory } from "@/store/chat.ts";
|
||||
import { useRef, useState } from "react";
|
||||
@ -17,7 +16,7 @@ import {
|
||||
updateConversationList,
|
||||
} from "@/conversation/history.ts";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { setMenu } from "@/store/menu.ts";
|
||||
import {selectMenu, setMenu} from "@/store/menu.ts";
|
||||
import {
|
||||
Copy,
|
||||
Eraser,
|
||||
@ -340,7 +339,7 @@ function SidebarMenu() {
|
||||
function SideBar() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const open = useSelector((state: RootState) => state.menu.open);
|
||||
const open = useSelector(selectMenu);
|
||||
const auth = useSelector(selectAuthenticated);
|
||||
const [operateConversation, setOperateConversation] = useState<Operation>({
|
||||
target: null,
|
||||
|
@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
type ChatInputProps = {
|
||||
className?: string;
|
||||
ref?: React.RefObject<HTMLInputElement>;
|
||||
target?: React.RefObject<HTMLInputElement>;
|
||||
value: string;
|
||||
onValueChange: (value: string) => void;
|
||||
onEnterPressed: () => void;
|
||||
@ -13,7 +13,7 @@ type ChatInputProps = {
|
||||
|
||||
function ChatInput({
|
||||
className,
|
||||
ref,
|
||||
target,
|
||||
value,
|
||||
onValueChange,
|
||||
onEnterPressed,
|
||||
@ -24,7 +24,7 @@ function ChatInput({
|
||||
<Input
|
||||
id={`input`}
|
||||
className={`input-box ${className || ""}`}
|
||||
ref={ref}
|
||||
ref={target}
|
||||
value={value}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onValueChange(e.target.value);
|
@ -8,7 +8,7 @@ import {
|
||||
} from "@/utils/env.ts";
|
||||
import { getMemory } from "@/utils/memory.ts";
|
||||
|
||||
export const version = "3.6.13rc";
|
||||
export const version = "3.6.13rc1";
|
||||
export const dev: boolean = getDev();
|
||||
export const deploy: boolean = true;
|
||||
export let rest_api: string = getRestApi(deploy);
|
||||
|
@ -2,9 +2,12 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import Home from "./routes/Home.tsx";
|
||||
import NotFound from "./routes/NotFound.tsx";
|
||||
import Auth from "./routes/Auth.tsx";
|
||||
import Generation from "./routes/Generation.tsx";
|
||||
import Sharing from "./routes/Sharing.tsx";
|
||||
import Article from "@/routes/Article.tsx";
|
||||
import { lazy, Suspense } from "react";
|
||||
|
||||
const Generation = lazy(() => import("@/routes/Generation.tsx"));
|
||||
const Sharing = lazy(() => import("@/routes/Sharing.tsx"));
|
||||
const Article = lazy(() => import("@/routes/Article.tsx"));
|
||||
const Admin = lazy(() => import("@/routes/Admin.tsx"));
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
@ -22,17 +25,43 @@ const router = createBrowserRouter([
|
||||
{
|
||||
id: "generation",
|
||||
path: "/generate",
|
||||
Component: Generation,
|
||||
element: (
|
||||
<Suspense>
|
||||
<Generation />
|
||||
</Suspense>
|
||||
),
|
||||
ErrorBoundary: NotFound,
|
||||
},
|
||||
{
|
||||
id: "share",
|
||||
path: "/share/:hash",
|
||||
Component: Sharing,
|
||||
element: (
|
||||
<Suspense>
|
||||
<Sharing />
|
||||
</Suspense>
|
||||
),
|
||||
ErrorBoundary: NotFound,
|
||||
},
|
||||
{
|
||||
id: "article",
|
||||
path: "/article",
|
||||
Component: Article,
|
||||
element: (
|
||||
<Suspense>
|
||||
<Article />
|
||||
</Suspense>
|
||||
),
|
||||
ErrorBoundary: NotFound,
|
||||
},
|
||||
{
|
||||
id: "admin",
|
||||
path: "/admin",
|
||||
element: (
|
||||
<Suspense>
|
||||
<Admin />
|
||||
</Suspense>
|
||||
),
|
||||
children: [],
|
||||
ErrorBoundary: NotFound,
|
||||
},
|
||||
]);
|
||||
|
||||
|
12
app/src/routes/Admin.tsx
Normal file
12
app/src/routes/Admin.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import "@/assets/admin/all.less";
|
||||
import MenuBar from "@/components/admin/MenuBar.tsx";
|
||||
|
||||
function Admin() {
|
||||
return (
|
||||
<div className={`admin-page`}>
|
||||
<MenuBar />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Admin;
|
@ -24,3 +24,5 @@ export const menuSlice = createSlice({
|
||||
|
||||
export const { toggleMenu, closeMenu, openMenu, setMenu } = menuSlice.actions;
|
||||
export default menuSlice.reducer;
|
||||
|
||||
export const selectMenu = (state: any) => state.menu.open;
|
||||
|
Loading…
Reference in New Issue
Block a user