mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-25 16:26:51 +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:
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>;
|
||||
}
|
||||
Reference in New Issue
Block a user