Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add ProfileSetting page #3221 #3588

Merged
merged 2 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
21 changes: 13 additions & 8 deletions web/src/constants/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
48 changes: 34 additions & 14 deletions web/src/pages/home/applications.tsx
Original file line number Diff line number Diff line change
@@ -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 = [
{
Expand Down Expand Up @@ -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) => {
Expand Down
8 changes: 4 additions & 4 deletions web/src/pages/home/datasets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,31 @@ 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,
title: 'HR knowledge base',
files: '1,242 files',
size: '152 MB',
created: '12.02.2024',
image: '/image.png',
image: 'https://github.com/shadcn.png',
},
{
id: 3,
title: 'IT knowledge base',
files: '1,242 files',
size: '152 MB',
created: '12.02.2024',
image: '/rectangle-86.png',
image: 'https://github.com/shadcn.png',
},
{
id: 4,
title: 'Legal knowledge base',
files: '1,242 files',
size: '152 MB',
created: '12.02.2024',
image: '/image-2.png',
image: 'https://github.com/shadcn.png',
},
];

Expand Down
2 changes: 1 addition & 1 deletion web/src/pages/home/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
20 changes: 20 additions & 0 deletions web/src/pages/profile-setting/hooks.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 34 additions & 0 deletions web/src/pages/profile-setting/index.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions web/src/pages/profile-setting/plan/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Plan() {
return <div>plan</div>;
}
72 changes: 72 additions & 0 deletions web/src/pages/profile-setting/profile/index.tsx
Original file line number Diff line number Diff line change
@@ -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="[email protected]"
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 changes: 25 additions & 0 deletions web/src/pages/profile-setting/sidebar/hooks.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 92 additions & 0 deletions web/src/pages/profile-setting/sidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
}
Loading