mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-12 01:49:02 +08:00
### What problem does this PR solve? Feat: Add ProfileSetting page #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
ee7fd71fdc
commit
ee33bf71eb
@ -13,7 +13,7 @@ const buttonVariants = cva(
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline:
|
||||
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
'border border-colors-outline-sentiment-primary bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
|
@ -10,14 +10,19 @@ export enum UserSettingRouteKey {
|
||||
Logout = 'logout',
|
||||
}
|
||||
|
||||
export const UserSettingRouteMap = {
|
||||
[UserSettingRouteKey.Profile]: 'Profile',
|
||||
[UserSettingRouteKey.Password]: 'Password',
|
||||
[UserSettingRouteKey.Model]: 'Model Providers',
|
||||
[UserSettingRouteKey.System]: 'System Version',
|
||||
[UserSettingRouteKey.Team]: 'Team',
|
||||
[UserSettingRouteKey.Logout]: 'Log out',
|
||||
};
|
||||
export const ProfileSettingBaseKey = 'profile-setting';
|
||||
|
||||
export enum ProfileSettingRouteKey {
|
||||
Profile = 'profile',
|
||||
Plan = 'plan',
|
||||
Model = 'model',
|
||||
System = 'system',
|
||||
Api = 'api',
|
||||
Team = 'team',
|
||||
Prompt = 'prompt',
|
||||
Chunk = 'chunk',
|
||||
Logout = 'logout',
|
||||
}
|
||||
|
||||
// Please lowercase the file name
|
||||
export const IconMap = {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Segmented, SegmentedValue } from '@/components/ui/segmented ';
|
||||
import { ChevronRight, Cpu, MessageSquare, Search } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
const applications = [
|
||||
{
|
||||
@ -34,24 +36,42 @@ const applications = [
|
||||
];
|
||||
|
||||
export function Applications() {
|
||||
const [val, setVal] = useState('all');
|
||||
const options = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
label: 'All',
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
label: 'Chat',
|
||||
value: 'chat',
|
||||
},
|
||||
{
|
||||
label: 'Search',
|
||||
value: 'search',
|
||||
},
|
||||
{
|
||||
label: 'Agent',
|
||||
value: 'agent',
|
||||
},
|
||||
];
|
||||
}, []);
|
||||
|
||||
const handleChange = (path: SegmentedValue) => {
|
||||
setVal(path as string);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="mt-12">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-2xl font-bold">Applications</h2>
|
||||
<div className="flex bg-colors-background-inverse-standard rounded-lg p-1">
|
||||
<Button variant="default" size="sm">
|
||||
All
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
Chat
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
Search
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
Agents
|
||||
</Button>
|
||||
</div>
|
||||
<Segmented
|
||||
options={options}
|
||||
value={val}
|
||||
onChange={handleChange}
|
||||
className="bg-colors-background-inverse-standard"
|
||||
></Segmented>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 gap-6">
|
||||
{[...Array(12)].map((_, i) => {
|
||||
|
@ -9,7 +9,7 @@ const datasets = [
|
||||
files: '1,242 files',
|
||||
size: '152 MB',
|
||||
created: '12.02.2024',
|
||||
image: '/image-3.png',
|
||||
image: 'https://github.com/shadcn.png',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
@ -17,7 +17,7 @@ const datasets = [
|
||||
files: '1,242 files',
|
||||
size: '152 MB',
|
||||
created: '12.02.2024',
|
||||
image: '/image.png',
|
||||
image: 'https://github.com/shadcn.png',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
@ -25,7 +25,7 @@ const datasets = [
|
||||
files: '1,242 files',
|
||||
size: '152 MB',
|
||||
created: '12.02.2024',
|
||||
image: '/rectangle-86.png',
|
||||
image: 'https://github.com/shadcn.png',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
@ -33,7 +33,7 @@ const datasets = [
|
||||
files: '1,242 files',
|
||||
size: '152 MB',
|
||||
created: '12.02.2024',
|
||||
image: '/image-2.png',
|
||||
image: 'https://github.com/shadcn.png',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -83,7 +83,7 @@ export function HomeHeader() {
|
||||
options={options}
|
||||
value={currentPath}
|
||||
onChange={handleChange}
|
||||
className="bg-backgroundInverseStandard text-backgroundInverseStandard-foreground"
|
||||
className="bg-colors-background-inverse-standard text-backgroundInverseStandard-foreground"
|
||||
></Segmented>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
|
20
web/src/pages/profile-setting/hooks.tsx
Normal file
20
web/src/pages/profile-setting/hooks.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { ProfileSettingRouteKey } from '@/constants/setting';
|
||||
import { useSecondPathName } from '@/hooks/route-hook';
|
||||
|
||||
export const useGetPageTitle = (): string => {
|
||||
const pathName = useSecondPathName();
|
||||
|
||||
const LabelMap = {
|
||||
[ProfileSettingRouteKey.Profile]: 'User profile',
|
||||
[ProfileSettingRouteKey.Plan]: 'Plan & balance',
|
||||
[ProfileSettingRouteKey.Model]: 'Model management',
|
||||
[ProfileSettingRouteKey.System]: 'System',
|
||||
[ProfileSettingRouteKey.Api]: 'Api',
|
||||
[ProfileSettingRouteKey.Team]: 'Team management',
|
||||
[ProfileSettingRouteKey.Prompt]: 'Prompt management',
|
||||
[ProfileSettingRouteKey.Chunk]: 'Chunk method',
|
||||
[ProfileSettingRouteKey.Logout]: 'Logout',
|
||||
};
|
||||
|
||||
return LabelMap[pathName as ProfileSettingRouteKey];
|
||||
};
|
34
web/src/pages/profile-setting/index.tsx
Normal file
34
web/src/pages/profile-setting/index.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import { Outlet } from 'umi';
|
||||
import { useGetPageTitle } from './hooks';
|
||||
import { SideBar } from './sidebar';
|
||||
|
||||
export default function ProfileSetting() {
|
||||
const title = useGetPageTitle();
|
||||
return (
|
||||
<div className="flex flex-col w-full h-screen bg-background text-foreground">
|
||||
<header className="flex items-center border-b">
|
||||
<div className="flex items-center border-r p-1.5">
|
||||
<Button variant="ghost" size="icon">
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<h1 className="text-2xl font-semibold tracking-tight">
|
||||
Profile & settings
|
||||
</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="flex flex-1 bg-muted/50">
|
||||
<SideBar></SideBar>
|
||||
|
||||
<main className="flex-1 p-10">
|
||||
<h1 className="text-3xl font-bold mb-6"> {title}</h1>
|
||||
<Outlet></Outlet>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
3
web/src/pages/profile-setting/plan/index.tsx
Normal file
3
web/src/pages/profile-setting/plan/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Plan() {
|
||||
return <div>plan</div>;
|
||||
}
|
72
web/src/pages/profile-setting/profile/index.tsx
Normal file
72
web/src/pages/profile-setting/profile/index.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
|
||||
export default function Profile() {
|
||||
return (
|
||||
<section>
|
||||
<Avatar className="w-[120px] h-[120px] mb-6">
|
||||
<AvatarImage
|
||||
src={
|
||||
'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg'
|
||||
}
|
||||
alt="Profile"
|
||||
/>
|
||||
<AvatarFallback>YW</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<div className="space-y-6 max-w-[600px]">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm text-muted-foreground">User name</label>
|
||||
<Input
|
||||
defaultValue="yifanwu92"
|
||||
className="bg-colors-background-inverse-weak"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm text-muted-foreground">Email</label>
|
||||
<Input
|
||||
defaultValue="yifanwu92@gmail.com"
|
||||
className="bg-colors-background-inverse-weak"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm text-muted-foreground">Language</label>
|
||||
<Select defaultValue="english">
|
||||
<SelectTrigger className="bg-colors-background-inverse-weak">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="english">English</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm text-muted-foreground">Timezone</label>
|
||||
<Select defaultValue="utc9">
|
||||
<SelectTrigger className="bg-colors-background-inverse-weak">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="utc9">UTC+9 Asia/Shanghai</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<Button variant="outline" className="mt-4">
|
||||
Change password
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
25
web/src/pages/profile-setting/sidebar/hooks.tsx
Normal file
25
web/src/pages/profile-setting/sidebar/hooks.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import {
|
||||
ProfileSettingBaseKey,
|
||||
ProfileSettingRouteKey,
|
||||
} from '@/constants/setting';
|
||||
import { useLogout } from '@/hooks/login-hooks';
|
||||
import { useCallback } from 'react';
|
||||
import { useNavigate } from 'umi';
|
||||
|
||||
export const useHandleMenuClick = () => {
|
||||
const navigate = useNavigate();
|
||||
const { logout } = useLogout();
|
||||
|
||||
const handleMenuClick = useCallback(
|
||||
(key: ProfileSettingRouteKey) => () => {
|
||||
if (key === ProfileSettingRouteKey.Logout) {
|
||||
logout();
|
||||
} else {
|
||||
navigate(`/${ProfileSettingBaseKey}/${key}`);
|
||||
}
|
||||
},
|
||||
[logout, navigate],
|
||||
);
|
||||
|
||||
return { handleMenuClick };
|
||||
};
|
92
web/src/pages/profile-setting/sidebar/index.tsx
Normal file
92
web/src/pages/profile-setting/sidebar/index.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { ProfileSettingRouteKey } from '@/constants/setting';
|
||||
import { useSecondPathName } from '@/hooks/route-hook';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
AlignEndVertical,
|
||||
Banknote,
|
||||
Box,
|
||||
FileCog,
|
||||
LayoutGrid,
|
||||
LogOut,
|
||||
User,
|
||||
} from 'lucide-react';
|
||||
import { useHandleMenuClick } from './hooks';
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
section: 'Account & collaboration',
|
||||
items: [
|
||||
{ icon: User, label: 'Profile', key: ProfileSettingRouteKey.Profile },
|
||||
{ icon: LayoutGrid, label: 'Team', key: ProfileSettingRouteKey.Team },
|
||||
{ icon: Banknote, label: 'Plan', key: ProfileSettingRouteKey.Plan },
|
||||
],
|
||||
},
|
||||
{
|
||||
section: 'System configurations',
|
||||
items: [
|
||||
{
|
||||
icon: Box,
|
||||
label: 'Model management',
|
||||
key: ProfileSettingRouteKey.Model,
|
||||
},
|
||||
{
|
||||
icon: FileCog,
|
||||
label: 'Prompt management',
|
||||
key: ProfileSettingRouteKey.Prompt,
|
||||
},
|
||||
{
|
||||
icon: AlignEndVertical,
|
||||
label: 'Chunking method',
|
||||
key: ProfileSettingRouteKey.Chunk,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function SideBar() {
|
||||
const pathName = useSecondPathName();
|
||||
const { handleMenuClick } = useHandleMenuClick();
|
||||
|
||||
return (
|
||||
<aside className="w-[303px] bg-background border-r">
|
||||
{menuItems.map((section, idx) => (
|
||||
<div key={idx}>
|
||||
<h2 className="p-6 text-sm font-semibold">{section.section}</h2>
|
||||
{section.items.map((item, itemIdx) => {
|
||||
const active = pathName === item.key;
|
||||
return (
|
||||
<Button
|
||||
key={itemIdx}
|
||||
variant={active ? 'secondary' : 'ghost'}
|
||||
className={cn('w-full justify-start gap-2.5 p-6 relative')}
|
||||
onClick={handleMenuClick(item.key)}
|
||||
>
|
||||
<item.icon className="w-6 h-6" />
|
||||
<span>{item.label}</span>
|
||||
{active && (
|
||||
<div className="absolute right-0 w-[5px] h-[66px] bg-primary rounded-l-xl shadow-[0_0_5.94px_#7561ff,0_0_11.88px_#7561ff,0_0_41.58px_#7561ff,0_0_83.16px_#7561ff,0_0_142.56px_#7561ff,0_0_249.48px_#7561ff]" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="p-6 mt-auto border-t">
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<Switch id="dark-mode" />
|
||||
<Label htmlFor="dark-mode" className="text-sm">
|
||||
Dark
|
||||
</Label>
|
||||
</div>
|
||||
<Button variant="outline" className="w-full gap-3">
|
||||
<LogOut className="w-6 h-6" />
|
||||
Logout
|
||||
</Button>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
3
web/src/pages/profile-setting/team/index.tsx
Normal file
3
web/src/pages/profile-setting/team/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Team() {
|
||||
return <div>team</div>;
|
||||
}
|
@ -131,6 +131,26 @@ const routes = [
|
||||
layout: false,
|
||||
component: '@/pages/home',
|
||||
},
|
||||
{
|
||||
path: '/profile-setting',
|
||||
layout: false,
|
||||
component: '@/pages/profile-setting',
|
||||
routes: [
|
||||
{ path: '/profile-setting', redirect: '/profile-setting/profile' },
|
||||
{
|
||||
path: '/profile-setting/profile',
|
||||
component: '@/pages/profile-setting/profile',
|
||||
},
|
||||
{
|
||||
path: '/profile-setting/team',
|
||||
component: '@/pages/profile-setting/team',
|
||||
},
|
||||
{
|
||||
path: '/profile-setting/plan',
|
||||
component: '@/pages/profile-setting/plan',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
@ -25,6 +25,10 @@ module.exports = {
|
||||
background: 'var(--background)',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
buttonBlueText: 'var(--button-blue-text)',
|
||||
|
||||
'colors-outline-sentiment-primary':
|
||||
'var(--colors-outline-sentiment-primary)',
|
||||
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
|
@ -39,6 +39,10 @@
|
||||
--background-inverse-standard-foreground: rgb(92, 81, 81);
|
||||
|
||||
--button-blue-text: rgb(22, 119, 255);
|
||||
|
||||
--colors-outline-sentiment-primary: rgba(127, 105, 255, 1);
|
||||
|
||||
--colors-text-core-standard: rgba(127, 105, 255, 1);
|
||||
}
|
||||
|
||||
.dark {
|
||||
@ -108,6 +112,10 @@
|
||||
--colors-background-neutral-standard: rgba(11, 10, 18, 1);
|
||||
--colors-background-neutral-strong: rgba(29, 26, 44, 1);
|
||||
--colors-background-neutral-weak: rgba(17, 16, 23, 1);
|
||||
|
||||
--colors-outline-sentiment-primary: rgba(146, 118, 255, 1);
|
||||
|
||||
--colors-text-core-standard: rgba(137, 126, 255, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user