Theme switch support (#3568)

### What problem does this PR solve?
- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: Yingfeng <yingfeng.zhang@gmail.com>
Co-authored-by: Jin Hai <haijin.chn@gmail.com>
This commit is contained in:
so95
2024-12-10 10:42:04 +07:00
committed by GitHub
parent 7d4f1c0645
commit d5a322a352
85 changed files with 1041 additions and 520 deletions

View File

@ -1,19 +1,21 @@
import { ReactComponent as ApiIcon } from '@/assets/svg/api.svg';
import { ReactComponent as LogoutIcon } from '@/assets/svg/logout.svg';
import { ReactComponent as ModelIcon } from '@/assets/svg/model-providers.svg';
import { ReactComponent as PasswordIcon } from '@/assets/svg/password.svg';
import { ReactComponent as ProfileIcon } from '@/assets/svg/profile.svg';
import { ReactComponent as TeamIcon } from '@/assets/svg/team.svg';
import {
ApiIcon,
LogOutIcon,
ModelProviderIcon,
PasswordIcon,
ProfileIcon,
TeamIcon,
} from '@/assets/icon/Icon';
import { UserSettingRouteKey } from '@/constants/setting';
import { MonitorOutlined } from '@ant-design/icons';
export const UserSettingIconMap = {
[UserSettingRouteKey.Profile]: <ProfileIcon />,
[UserSettingRouteKey.Password]: <PasswordIcon />,
[UserSettingRouteKey.Model]: <ModelIcon />,
[UserSettingRouteKey.Model]: <ModelProviderIcon />,
[UserSettingRouteKey.System]: <MonitorOutlined style={{ fontSize: 24 }} />,
[UserSettingRouteKey.Team]: <TeamIcon />,
[UserSettingRouteKey.Logout]: <LogoutIcon />,
[UserSettingRouteKey.Logout]: <LogOutIcon />,
[UserSettingRouteKey.Api]: <ApiIcon />,
};

View File

@ -0,0 +1,68 @@
import { Table } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import React from 'react';
type TranslationTableRow = {
key: string;
[language: string]: string;
};
interface TranslationTableProps {
data: TranslationTableRow[];
languages: string[];
}
const TranslationTable: React.FC<TranslationTableProps> = ({
data,
languages,
}) => {
// Define columns dynamically based on languages
const columns: ColumnsType<TranslationTableRow> = [
{
title: 'Key',
dataIndex: 'key',
key: 'key',
fixed: 'left',
width: 200,
sorter: (a, b) => a.key.localeCompare(b.key), // Sorting by key
},
...languages.map((lang) => ({
title: lang,
dataIndex: lang,
key: lang,
sorter: (a: any, b: any) => a[lang].localeCompare(b[lang]), // Sorting by language
// Example filter for each language
filters: [
{
text: 'Show Empty',
value: 'show_empty',
},
{
text: 'Show Non-Empty',
value: 'show_non_empty',
},
],
onFilter: (value: any, record: any) => {
if (value === 'show_empty') {
return !record[lang]; // Show rows with empty translations
}
if (value === 'show_non_empty') {
return record[lang] && record[lang].length > 0; // Show rows with non-empty translations
}
return true;
},
})),
];
return (
<Table
columns={columns}
dataSource={data}
rowKey="key"
pagination={{ pageSize: 10 }}
scroll={{ x: true }}
/>
);
};
export default TranslationTable;

View File

@ -0,0 +1,13 @@
import { translationTable } from '@/locales/config';
import TranslationTable from './TranslationTable';
function UserSettingLocale() {
return (
<TranslationTable
data={translationTable}
languages={['English', 'Vietnamese', 'Spanish', 'zh', 'zh-TRADITIONAL']}
/>
);
}
export default UserSettingLocale;

View File

@ -24,12 +24,30 @@
padding: 0;
}
}
.toBeAddedCardDark {
border-radius: 24px;
border: 1px solid #eaecf0;
background: #e3f0ff2a;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
:global(.ant-card-body) {
padding: 10px 24px;
}
.addButton {
padding: 0;
}
}
.addedCard {
border-radius: 18px;
border: 1px solid #eaecf0;
background: #e6e7eb;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
}
.addedCardDark {
border-radius: 18px;
border: 1px solid #eaecf0;
background: #e6e7eb21;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
}
.modelDivider {
margin: 0;
}

View File

@ -1,5 +1,6 @@
import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg';
import { LlmIcon } from '@/components/svg-icon';
import { useTheme } from '@/components/theme-provider';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import { LlmItem, useSelectLlmList } from '@/hooks/llm-hooks';
import { CloseCircleOutlined, SettingOutlined } from '@ant-design/icons';
@ -61,6 +62,7 @@ interface IModelCardProps {
const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
const { visible, switchVisible } = useSetModalState();
const { t } = useTranslate('setting');
const { theme } = useTheme();
const { handleDeleteLlm } = useHandleDeleteLlm(item.name);
const { handleDeleteFactory } = useHandleDeleteFactory(item.name);
@ -74,7 +76,9 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
return (
<List.Item>
<Card className={styles.addedCard}>
<Card
className={theme === 'dark' ? styles.addedCardDark : styles.addedCard}
>
<Row align={'middle'}>
<Col span={12}>
<Flex gap={'middle'} align="center">
@ -139,6 +143,7 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
const UserSettingModel = () => {
const { factoryList, myLlmList: llmList, loading } = useSelectLlmList();
const { theme } = useTheme();
const {
saveApiKeyLoading,
initialApiKey,
@ -313,7 +318,13 @@ const UserSettingModel = () => {
dataSource={factoryList}
renderItem={(item) => (
<List.Item>
<Card className={styles.toBeAddedCard}>
<Card
className={
theme === 'dark'
? styles.toBeAddedCardDark
: styles.toBeAddedCard
}
>
<Flex vertical gap={'middle'}>
<LlmIcon name={item.name} />
<Flex vertical gap={'middle'}>