diff --git a/frontend/providers/aiproxy/.prettierignore b/frontend/providers/aiproxy/.prettierignore index 0d960f1ce..8ae2ecde4 100644 --- a/frontend/providers/aiproxy/.prettierignore +++ b/frontend/providers/aiproxy/.prettierignore @@ -28,4 +28,5 @@ pnpm-lock.yaml public/ .git/ .vscode/ -.idea/ \ No newline at end of file +.idea/ +deploy/ \ No newline at end of file diff --git a/frontend/providers/aiproxy/.prettierrc.json b/frontend/providers/aiproxy/.prettierrc.json index 20738da32..92b2c11eb 100644 --- a/frontend/providers/aiproxy/.prettierrc.json +++ b/frontend/providers/aiproxy/.prettierrc.json @@ -11,6 +11,18 @@ "jsxBracketSameLine": false, "embeddedLanguageFormatting": "auto", "overrides": [ + { + "files": "*.tsx", + "options": { + "semi": true + } + }, + { + "files": "*.jsx", + "options": { + "semi": true + } + }, { "files": ["*.json", "*.jsonc"], "options": { diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/ChannelTable.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/ChannelTable.tsx index 12040f4e9..b21c4f598 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/ChannelTable.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/ChannelTable.tsx @@ -1,5 +1,5 @@ -'use client' -import { useEffect, useMemo, useState } from 'react' +'use client'; +import { useEffect, useMemo, useState } from 'react'; import { Box, Checkbox, @@ -18,51 +18,51 @@ import { Thead, Tr, useDisclosure, -} from '@chakra-ui/react' -import { useMessage } from '@sealos/ui' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +} from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Column, createColumnHelper, flexRender, getCoreRowModel, useReactTable, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; import { deleteChannel, getChannels, getChannelTypeNames, updateChannelStatus, -} from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import SwitchPage from '@/components/common/SwitchPage' -import { useI18n } from '@/providers/i18n/i18nContext' -import { ChannelInfo, ChannelStatus, ChannelType } from '@/types/admin/channels/channelInfo' -import { QueryKey } from '@/types/query-key' -import { downloadJson } from '@/utils/common' +} from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import SwitchPage from '@/components/common/SwitchPage'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { ChannelInfo, ChannelStatus, ChannelType } from '@/types/admin/channels/channelInfo'; +import { QueryKey } from '@/types/query-key'; +import { downloadJson } from '@/utils/common'; -import UpdateChannelModal from './UpdateChannelModal' +import UpdateChannelModal from './UpdateChannelModal'; export default function ChannelTable({ exportData, }: { - exportData: (data: ChannelInfo[]) => void + exportData: (data: ChannelInfo[]) => void; }) { - const { isOpen, onOpen, onClose } = useDisclosure() - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const [operationType, setOperationType] = useState<'create' | 'update'>('update') - const [channelInfo, setChannelInfo] = useState(undefined) - const [total, setTotal] = useState(0) - const [page, setPage] = useState(1) - const [pageSize, setPageSize] = useState(10) + const { isOpen, onOpen, onClose } = useDisclosure(); + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const [operationType, setOperationType] = useState<'create' | 'update'>('update'); + const [channelInfo, setChannelInfo] = useState(undefined); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); - const [selectedChannels, setSelectedChannels] = useState([]) + const [selectedChannels, setSelectedChannels] = useState([]); useEffect(() => { - exportData(selectedChannels) - }, [selectedChannels]) + exportData(selectedChannels); + }, [selectedChannels]); const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -71,23 +71,23 @@ export default function ChannelTable({ successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); - const queryClient = useQueryClient() + const queryClient = useQueryClient(); const { isLoading: isChannelTypeNamesLoading, data: channelTypeNames } = useQuery({ queryKey: [QueryKey.GetChannelTypeNames], queryFn: () => getChannelTypeNames(), - }) + }); const { data, isLoading: isChannelsLoading } = useQuery({ queryKey: [QueryKey.GetChannels, page, pageSize], queryFn: () => getChannels({ page, perPage: pageSize }), refetchOnReconnect: true, onSuccess(data) { - setTotal(data?.total || 0) + setTotal(data?.total || 0); }, - }) + }); const updateChannelStatusMutation = useMutation( ({ id, status }: { id: string; status: number }) => updateChannelStatus(id, status), @@ -99,9 +99,9 @@ export default function ChannelTable({ isClosable: true, duration: 2000, position: 'top', - }) - queryClient.invalidateQueries([QueryKey.GetChannels]) - queryClient.invalidateQueries([QueryKey.GetChannelTypeNames]) + }); + queryClient.invalidateQueries([QueryKey.GetChannels]); + queryClient.invalidateQueries([QueryKey.GetChannelTypeNames]); }, onError(err: any) { message({ @@ -110,10 +110,10 @@ export default function ChannelTable({ description: err?.message || t('channel.updateFailed'), isClosable: true, position: 'top', - }) + }); }, } - ) + ); const deleteChannelMutation = useMutation(({ id }: { id: string }) => deleteChannel(id), { onSuccess() { message({ @@ -122,9 +122,9 @@ export default function ChannelTable({ isClosable: true, duration: 2000, position: 'top', - }) - queryClient.invalidateQueries([QueryKey.GetChannels]) - queryClient.invalidateQueries([QueryKey.GetChannelTypeNames]) + }); + queryClient.invalidateQueries([QueryKey.GetChannels]); + queryClient.invalidateQueries([QueryKey.GetChannelTypeNames]); }, onError(err: any) { message({ @@ -133,42 +133,42 @@ export default function ChannelTable({ description: err?.message || t('channel.deleteFailed'), isClosable: true, position: 'top', - }) + }); }, - }) + }); // Update the button click handlers in the table actions column: const handleStatusUpdate = (id: string, currentStatus: number) => { const newStatus = currentStatus === ChannelStatus.ChannelStatusDisabled ? ChannelStatus.ChannelStatusEnabled - : ChannelStatus.ChannelStatusDisabled - updateChannelStatusMutation.mutate({ id, status: newStatus }) - } + : ChannelStatus.ChannelStatusDisabled; + updateChannelStatusMutation.mutate({ id, status: newStatus }); + }; - const columnHelper = createColumnHelper() + const columnHelper = createColumnHelper(); const handleHeaderCheckboxChange = (isChecked: boolean) => { if (isChecked) { - setSelectedChannels(data?.channels || []) + setSelectedChannels(data?.channels || []); } else { - setSelectedChannels([]) + setSelectedChannels([]); } - } + }; const handleRowCheckboxChange = (channel: ChannelInfo, isChecked: boolean) => { if (isChecked) { - setSelectedChannels([...selectedChannels, channel]) + setSelectedChannels([...selectedChannels, channel]); } else { - setSelectedChannels(selectedChannels.filter((c) => c.id !== channel.id)) + setSelectedChannels(selectedChannels.filter((c) => c.id !== channel.id)); } - } + }; const handleExportRow = (channel: ChannelInfo) => { - const channels = [channel] - const filename = `channel_${channels[0].id}_${new Date().toISOString()}.json` - downloadJson(channels, filename) - } + const channels = [channel]; + const filename = `channel_${channels[0].id}_${new Date().toISOString()}.json`; + downloadJson(channels, filename); + }; const columns = [ columnHelper.accessor((row) => row.id, { @@ -345,26 +345,26 @@ export default function ChannelTable({ ), cell: (info) => { - const status = info.getValue() - let statusText = '' - let statusColor = '' + const status = info.getValue(); + let statusText = ''; + let statusColor = ''; switch (status) { case ChannelStatus.ChannelStatusEnabled: - statusText = t('keystatus.enabled') - statusColor = 'green.600' - break + statusText = t('keystatus.enabled'); + statusColor = 'green.600'; + break; case ChannelStatus.ChannelStatusDisabled: - statusText = t('keystatus.disabled') - statusColor = 'red.600' - break + statusText = t('keystatus.disabled'); + statusColor = 'red.600'; + break; case ChannelStatus.ChannelStatusAutoDisabled: - statusText = t('channelStatus.autoDisabled') - statusColor = 'orange.500' - break + statusText = t('channelStatus.autoDisabled'); + statusColor = 'orange.500'; + break; default: - statusText = t('keystatus.unknown') - statusColor = 'gray.500' + statusText = t('keystatus.unknown'); + statusColor = 'gray.500'; } return ( @@ -378,7 +378,7 @@ export default function ChannelTable({ > {statusText} - ) + ); }, }), @@ -570,9 +570,9 @@ export default function ChannelTable({ color: 'brightBlue.600', }} onClick={() => { - setOperationType('update') - setChannelInfo(info.row.original) - onOpen() + setOperationType('update'); + setChannelInfo(info.row.original); + onOpen(); }} > ), }), - ] + ]; - const tableData = useMemo(() => data?.channels || [], [data]) + const tableData = useMemo(() => data?.channels || [], [data]); const table = useReactTable({ data: tableData, columns, getCoreRowModel: getCoreRowModel(), - }) + }); return ( - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/UpdateChannelModal.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/UpdateChannelModal.tsx index 4c5e95b26..232bab944 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/UpdateChannelModal.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/components/UpdateChannelModal.tsx @@ -1,6 +1,6 @@ -'use client' -import { Dispatch, SetStateAction, useEffect } from 'react' -import { Controller, FieldErrors, useForm } from 'react-hook-form' +'use client'; +import { Dispatch, SetStateAction, useEffect } from 'react'; +import { Controller, FieldErrors, useForm } from 'react-hook-form'; import { Badge, Button, @@ -20,30 +20,30 @@ import { Spinner, Text, VStack, -} from '@chakra-ui/react' -import { zodResolver } from '@hookform/resolvers/zod' -import { useMessage } from '@sealos/ui' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { z } from 'zod' +} from '@chakra-ui/react'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { z } from 'zod'; import { getChannelBuiltInSupportModels, getChannelDefaultModelAndDefaultModeMapping, getChannelTypeNames, -} from '@/api/platform' -import { createChannel, updateChannel } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import ConstructModeMappingComponent from '@/components/common/ConstructModeMappingComponent' -import { MultiSelectCombobox } from '@/components/common/MultiSelectCombobox' -import { SingleSelectCombobox } from '@/components/common/SingleSelectCombobox' -import { useI18n } from '@/providers/i18n/i18nContext' -import { ChannelInfo, ChannelStatus, ChannelType } from '@/types/admin/channels/channelInfo' -import { QueryKey } from '@/types/query-key' +} from '@/api/platform'; +import { createChannel, updateChannel } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import ConstructModeMappingComponent from '@/components/common/ConstructModeMappingComponent'; +import { MultiSelectCombobox } from '@/components/common/MultiSelectCombobox'; +import { SingleSelectCombobox } from '@/components/common/SingleSelectCombobox'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { ChannelInfo, ChannelStatus, ChannelType } from '@/types/admin/channels/channelInfo'; +import { QueryKey } from '@/types/query-key'; type Model = { - name: string - isDefault: boolean -} + name: string; + isDefault: boolean; +}; export const UpdateChannelModal = function ({ isOpen, @@ -51,14 +51,14 @@ export const UpdateChannelModal = function ({ operationType, channelInfo, }: { - isOpen: boolean - onClose: () => void - operationType: 'create' | 'update' - channelInfo?: ChannelInfo + isOpen: boolean; + onClose: () => void; + operationType: 'create' | 'update'; + channelInfo?: ChannelInfo; }): JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const queryClient = useQueryClient() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const queryClient = useQueryClient(); const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -68,30 +68,30 @@ export const UpdateChannelModal = function ({ successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); const { isLoading: isChannelTypeNamesLoading, data: channelTypeNames } = useQuery({ queryKey: [QueryKey.GetChannelTypeNames], queryFn: () => getChannelTypeNames(), - }) + }); const { isLoading: isBuiltInSupportModelsLoading, data: builtInSupportModels } = useQuery({ queryKey: [QueryKey.GetAllChannelModes], queryFn: () => getChannelBuiltInSupportModels(), - }) + }); const { isLoading: isDefaultEnabledModelsLoading, data: defaultEnabledModels } = useQuery({ queryKey: [QueryKey.GetDefaultModelAndModeMapping], queryFn: () => getChannelDefaultModelAndDefaultModeMapping(), - }) + }); // model type select combobox const handleModelTypeDropdownItemFilter = (dropdownItems: string[], inputValue: string) => { - const lowerCasedInput = inputValue.toLowerCase() + const lowerCasedInput = inputValue.toLowerCase(); return dropdownItems.filter( (item) => !inputValue || item.toLowerCase().includes(lowerCasedInput) - ) - } + ); + }; const handleModelTypeDropdownItemDisplay = (dropdownItem: string) => { return ( @@ -106,8 +106,8 @@ export const UpdateChannelModal = function ({ > {dropdownItem} - ) - } + ); + }; // model select combobox const handleModelFilteredDropdownItems = ( @@ -115,17 +115,17 @@ export const UpdateChannelModal = function ({ selectedItems: Model[], inputValue: string ) => { - const lowerCasedInputValue = inputValue.toLowerCase() + const lowerCasedInputValue = inputValue.toLowerCase(); // First filter out items that are already selected const unselectedItems = dropdownItems.filter( (dropdownItem) => !selectedItems.some((selectedItem) => selectedItem.name === dropdownItem.name) - ) + ); // Then filter by input value - return unselectedItems.filter((item) => item.name.toLowerCase().includes(lowerCasedInputValue)) - } + return unselectedItems.filter((item) => item.name.toLowerCase().includes(lowerCasedInputValue)); + }; const handleModelDropdownItemDisplay = (dropdownItem: Model) => { if (dropdownItem.isDefault) { @@ -190,7 +190,7 @@ export const UpdateChannelModal = function ({ - ) + ); } return ( {dropdownItem.name} - ) - } + ); + }; const handleModelSelectedItemDisplay = (selectedItem: Model) => { if (selectedItem.isDefault) { @@ -259,7 +259,7 @@ export const UpdateChannelModal = function ({ - ) + ); } return ( {selectedItem.name} - ) - } + ); + }; const handleSetCustomModel = ( selectedItems: Model[], @@ -286,16 +286,16 @@ export const UpdateChannelModal = function ({ const newModel: Model = { name: customModeName.trim(), isDefault: false, - } + }; - const exists = selectedItems.some((item) => item.name === customModeName.trim()) + const exists = selectedItems.some((item) => item.name === customModeName.trim()); if (!exists) { - setSelectedItems([...selectedItems, newModel]) - setCustomModeName('') + setSelectedItems([...selectedItems, newModel]); + setCustomModeName(''); } } - } + }; // form schema const schema = z.object({ @@ -306,10 +306,10 @@ export const UpdateChannelModal = function ({ base_url: z.string(), models: z.array(z.string()).default([]), model_mapping: z.record(z.string(), z.any()).default({}), - }) + }); - const id = channelInfo?.id - type FormData = z.infer + const id = channelInfo?.id; + type FormData = z.infer; const { register, @@ -332,18 +332,18 @@ export const UpdateChannelModal = function ({ }, mode: 'onChange', reValidateMode: 'onChange', - }) + }); useEffect(() => { if (channelInfo) { - const { id, type, name, key, base_url, models, model_mapping } = channelInfo - reset({ id, type, name, key, base_url, models, model_mapping }) + const { id, type, name, key, base_url, models, model_mapping } = channelInfo; + reset({ id, type, name, key, base_url, models, model_mapping }); } - }, [channelInfo]) + }, [channelInfo]); const resetModalState = () => { - reset() - } + reset(); + }; const createChannelMutation = useMutation({ mutationFn: createChannel, @@ -351,9 +351,9 @@ export const UpdateChannelModal = function ({ message({ title: t('channels.createSuccess'), status: 'success', - }) + }); }, - }) + }); const updateChannelMutation = useMutation({ mutationFn: (data: FormData) => @@ -372,9 +372,9 @@ export const UpdateChannelModal = function ({ message({ title: t('channels.updateSuccess'), status: 'success', - }) + }); }, - }) + }); const onValidate = async (data: FormData) => { try { @@ -387,18 +387,18 @@ export const UpdateChannelModal = function ({ base_url: data.base_url, models: data.models, model_mapping: data.model_mapping, - }) - break + }); + break; case 'update': - await updateChannelMutation.mutateAsync(data) - break + await updateChannelMutation.mutateAsync(data); + break; } - queryClient.invalidateQueries({ queryKey: [QueryKey.GetChannels] }) - queryClient.invalidateQueries({ queryKey: [QueryKey.GetDefaultModelAndModeMapping] }) - queryClient.invalidateQueries({ queryKey: [QueryKey.GetChannelTypeNames] }) - queryClient.invalidateQueries({ queryKey: [QueryKey.GetAllChannelModes] }) - resetModalState() - onClose() + queryClient.invalidateQueries({ queryKey: [QueryKey.GetChannels] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.GetDefaultModelAndModeMapping] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.GetChannelTypeNames] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.GetAllChannelModes] }); + resetModalState(); + onClose(); } catch (error) { switch (operationType) { case 'create': @@ -409,8 +409,8 @@ export const UpdateChannelModal = function ({ duration: 2000, isClosable: true, description: error instanceof Error ? error.message : t('channels.createFailed'), - }) - break + }); + break; case 'update': message({ title: t('channels.updateFailed'), @@ -419,14 +419,14 @@ export const UpdateChannelModal = function ({ duration: 2000, isClosable: true, description: error instanceof Error ? error.message : t('channels.updateFailed'), - }) - break + }); + break; } } - } + }; const onInvalid = (errors: FieldErrors): void => { - const firstErrorMessage = Object.values(errors)[0]?.message + const firstErrorMessage = Object.values(errors)[0]?.message; if (firstErrorMessage) { message({ title: firstErrorMessage as string, @@ -434,11 +434,11 @@ export const UpdateChannelModal = function ({ position: 'top', duration: 2000, isClosable: true, - }) + }); } - } + }; - const onSubmit = handleSubmit(onValidate, onInvalid) + const onSubmit = handleSubmit(onValidate, onInvalid); return ( @@ -611,11 +611,11 @@ export const UpdateChannelModal = function ({ render={({ field }) => { const availableChannels = Object.entries(channelTypeNames) .filter(([channel]) => channel in builtInSupportModels) - .map(([_, name]) => name) + .map(([_, name]) => name); const initSelectedItem = field.value ? channelTypeNames[String(field.value) as ChannelType] - : undefined + : undefined; return ( @@ -625,20 +625,20 @@ export const UpdateChannelModal = function ({ if (channelName) { const channelType = Object.entries(channelTypeNames).find( ([_, name]) => name === channelName - )?.[0] + )?.[0]; if (channelType) { - const numericChannel = Number(channelType) - field.onChange(numericChannel) - setValue('models', []) - setValue('model_mapping', {}) + const numericChannel = Number(channelType); + field.onChange(numericChannel); + setValue('models', []); + setValue('model_mapping', {}); } } }} handleDropdownItemFilter={handleModelTypeDropdownItemFilter} handleDropdownItemDisplay={handleModelTypeDropdownItemDisplay} /> - ) + ); }} /> {errors.type && {errors.type.message}} @@ -649,35 +649,35 @@ export const UpdateChannelModal = function ({ name="models" control={control} render={({ field }) => { - const channelType = String(watch('type')) as ChannelType + const channelType = String(watch('type')) as ChannelType; const builtInModes = - builtInSupportModels[channelType]?.map((mode) => mode.model) || [] - const defaultModes = defaultEnabledModels.models[channelType] || [] + builtInSupportModels[channelType]?.map((mode) => mode.model) || []; + const defaultModes = defaultEnabledModels.models[channelType] || []; const allModes: Model[] = builtInModes.map((modeName) => ({ name: modeName, isDefault: defaultModes.includes(modeName), - })) + })); const selectedModels: Model[] = field.value.map((modeName) => ({ name: modeName, isDefault: defaultModes.includes(modeName), - })) + })); return ( dropdownItems={allModes} selectedItems={selectedModels} setSelectedItems={(models) => { - field.onChange((models as Model[]).map((m) => m.name)) + field.onChange((models as Model[]).map((m) => m.name)); }} handleFilteredDropdownItems={handleModelFilteredDropdownItems} handleDropdownItemDisplay={handleModelDropdownItemDisplay} handleSelectedItemDisplay={handleModelSelectedItemDisplay} handleSetCustomSelectedItem={handleSetCustomModel} /> - ) + ); }} /> {errors.models && {errors.models.message}} @@ -688,24 +688,24 @@ export const UpdateChannelModal = function ({ name="model_mapping" control={control} render={({ field }) => { - const channelType = String(watch('type')) as ChannelType + const channelType = String(watch('type')) as ChannelType; - const selectedModels = watch('models') - const defaultModes = defaultEnabledModels.models[channelType] || [] + const selectedModels = watch('models'); + const defaultModes = defaultEnabledModels.models[channelType] || []; const covertedSelectedModels: Model[] = selectedModels.map((modeName) => ({ name: modeName, isDefault: defaultModes.includes(modeName), - })) + })); return ( { - field.onChange(mapping) + field.onChange(mapping); }} /> - ) + ); }} /> {errors.model_mapping?.message && ( @@ -849,7 +849,7 @@ export const UpdateChannelModal = function ({ ))} - ) -} + ); +}; -export default UpdateChannelModal +export default UpdateChannelModal; diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/page.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/page.tsx index 3cc9aaaa4..22a1ef80d 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/dashboard/page.tsx @@ -1,26 +1,26 @@ -'use client' -import { useRef, useState } from 'react' -import { Button, Flex, Text, useDisclosure, useToast } from '@chakra-ui/react' -import { useMessage } from '@sealos/ui' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +'use client'; +import { useRef, useState } from 'react'; +import { Button, Flex, Text, useDisclosure, useToast } from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { getAllChannels, uploadChannels } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { ChannelInfo } from '@/types/admin/channels/channelInfo' -import { QueryKey } from '@/types/query-key' -import { downloadJson } from '@/utils/common' +import { getAllChannels, uploadChannels } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { ChannelInfo } from '@/types/admin/channels/channelInfo'; +import { QueryKey } from '@/types/query-key'; +import { downloadJson } from '@/utils/common'; -import ChannelTable from './components/ChannelTable' -import UpdateChannelModal from './components/UpdateChannelModal' +import ChannelTable from './components/ChannelTable'; +import UpdateChannelModal from './components/UpdateChannelModal'; export default function DashboardPage() { - const { isOpen, onOpen, onClose } = useDisclosure() - const [operationType, setOperationType] = useState<'create' | 'update'>('create') - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const [exportData, setExportData] = useState([]) - const fileInputRef = useRef(null) + const { isOpen, onOpen, onClose } = useDisclosure(); + const [operationType, setOperationType] = useState<'create' | 'update'>('create'); + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const [exportData, setExportData] = useState([]); + const fileInputRef = useRef(null); const { message } = useMessage({ warningBoxBg: 'var(--Yellow-50, #FFFAEB)', @@ -30,9 +30,9 @@ export default function DashboardPage() { successBoxBg: 'var(--Green-50, #EDFBF3)', successIconBg: 'var(--Green-600, #039855)', successIconFill: 'white', - }) + }); - const queryClient = useQueryClient() + const queryClient = useQueryClient(); const { data: allChannels, @@ -43,57 +43,57 @@ export default function DashboardPage() { queryFn: getAllChannels, refetchOnReconnect: false, enabled: false, - }) + }); const uploadMutation = useMutation({ mutationFn: uploadChannels, - }) + }); const handleExport = async () => { if (exportData.length === 0) { - const result = await refetch() - const dataToExport = result.data || [] - downloadJson(dataToExport, 'channels') + const result = await refetch(); + const dataToExport = result.data || []; + downloadJson(dataToExport, 'channels'); } else { - downloadJson(exportData, 'channels') + downloadJson(exportData, 'channels'); } - } + }; const handleImport = () => { - fileInputRef.current?.click() - } + fileInputRef.current?.click(); + }; const handleFileChange = async (event: React.ChangeEvent) => { - const file = event.target.files?.[0] - if (!file) return + const file = event.target.files?.[0]; + if (!file) return; - const formData = new FormData() - formData.append('file', file) + const formData = new FormData(); + formData.append('file', file); try { - await uploadMutation.mutateAsync(formData) + await uploadMutation.mutateAsync(formData); message({ title: t('dashboard.importSuccess'), status: 'success', duration: 3000, isClosable: true, - }) - queryClient.invalidateQueries([QueryKey.GetChannels]) - queryClient.invalidateQueries([QueryKey.GetChannelTypeNames]) + }); + queryClient.invalidateQueries([QueryKey.GetChannels]); + queryClient.invalidateQueries([QueryKey.GetChannelTypeNames]); } catch (error) { - console.error('Import error:', error) + console.error('Import error:', error); message({ title: t('dashboard.importError'), status: 'error', duration: 3000, isClosable: true, - }) + }); } finally { if (fileInputRef.current) { - fileInputRef.current.value = '' + fileInputRef.current.value = ''; } } - } + }; return ( @@ -158,8 +158,8 @@ export default function DashboardPage() { }, }} onClick={() => { - setOperationType('create') - onOpen() + setOperationType('create'); + onOpen(); }} > - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/CommonConfig.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/CommonConfig.tsx index 8ae1ca5e2..aa3d6c0e9 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/CommonConfig.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/CommonConfig.tsx @@ -1,17 +1,17 @@ -'use client' -import { useState } from 'react' -import { Button, Flex, Text, useDisclosure } from '@chakra-ui/react' -import { Switch } from '@chakra-ui/react' -import { useMessage } from '@sealos/ui' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { produce } from 'immer' +'use client'; +import { useState } from 'react'; +import { Button, Flex, Text, useDisclosure } from '@chakra-ui/react'; +import { Switch } from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { produce } from 'immer'; -import { getOption, updateOption } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { QueryKey } from '@/types/query-key' +import { getOption, updateOption } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { QueryKey } from '@/types/query-key'; -import { EditableText } from './EditableText' +import { EditableText } from './EditableText'; export enum CommonConfigKey { GlobalApiRateLimitNum = 'GlobalApiRateLimitNum', @@ -21,25 +21,25 @@ export enum CommonConfigKey { } type CommonConfig = { - [CommonConfigKey.GlobalApiRateLimitNum]: string - [CommonConfigKey.DisableServe]: string - [CommonConfigKey.RetryTimes]: string - [CommonConfigKey.GroupMaxTokenNum]: string -} + [CommonConfigKey.GlobalApiRateLimitNum]: string; + [CommonConfigKey.DisableServe]: string; + [CommonConfigKey.RetryTimes]: string; + [CommonConfigKey.GroupMaxTokenNum]: string; +}; const CommonConfig = () => { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const queryClient = useQueryClient() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const queryClient = useQueryClient(); const [commonConfig, setCommonConfig] = useState(() => produce({} as CommonConfig, (draft) => { - draft.GlobalApiRateLimitNum = '' - draft.DisableServe = '' - draft.RetryTimes = '' - draft.GroupMaxTokenNum = '' + draft.GlobalApiRateLimitNum = ''; + draft.DisableServe = ''; + draft.RetryTimes = ''; + draft.GroupMaxTokenNum = ''; }) - ) + ); const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -48,24 +48,24 @@ const CommonConfig = () => { successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); const { isLoading: isOptionLoading, data: optionData } = useQuery({ queryKey: [QueryKey.GetCommonConfig], queryFn: () => getOption(), onSuccess: (data) => { - if (!data) return + if (!data) return; setCommonConfig( produce(commonConfig, (draft) => { - draft.GlobalApiRateLimitNum = data.GlobalApiRateLimitNum || '' - draft.DisableServe = data.DisableServe || '' - draft.RetryTimes = data.RetryTimes || '' - draft.GroupMaxTokenNum = data.GroupMaxTokenNum || '' + draft.GlobalApiRateLimitNum = data.GlobalApiRateLimitNum || ''; + draft.DisableServe = data.DisableServe || ''; + draft.RetryTimes = data.RetryTimes || ''; + draft.GroupMaxTokenNum = data.GroupMaxTokenNum || ''; }) - ) + ); }, - }) + }); const updateOptionMutation = useMutation({ mutationFn: (params: { key: string; value: string }) => updateOption(params), @@ -73,30 +73,30 @@ const CommonConfig = () => { message({ title: t('globalConfigs.saveCommonConfigSuccess'), status: 'success', - }) - queryClient.invalidateQueries({ queryKey: [QueryKey.GetCommonConfig] }) + }); + queryClient.invalidateQueries({ queryKey: [QueryKey.GetCommonConfig] }); }, onError: () => { message({ title: t('globalConfigs.saveCommonConfigFailed'), status: 'error', - }) + }); }, - }) + }); const updateConfigField = (field: CommonConfigKey, value: string) => { setCommonConfig( produce((draft) => { - draft[field] = value + draft[field] = value; }) - ) - updateOptionMutation.mutate({ key: field, value }) - } + ); + updateOptionMutation.mutate({ key: field, value }); + }; const handleDisableServeChange = (checked: boolean) => { - const value = checked ? 'true' : 'false' - updateConfigField(CommonConfigKey.DisableServe, value) - } + const value = checked ? 'true' : 'false'; + updateConfigField(CommonConfigKey.DisableServe, value); + }; return ( /* @@ -163,7 +163,7 @@ const CommonConfig = () => { {/* -- config end */} - ) -} + ); +}; -export default CommonConfig +export default CommonConfig; diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/EditableText.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/EditableText.tsx index 14b2d0b8a..97e027358 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/EditableText.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/EditableText.tsx @@ -1,6 +1,6 @@ -'use client' -import React, { useState } from 'react' -import { CheckIcon, CloseIcon } from '@chakra-ui/icons' +'use client'; +import React, { useState } from 'react'; +import { CheckIcon, CloseIcon } from '@chakra-ui/icons'; import { Box, Button, @@ -14,29 +14,29 @@ import { PopoverTrigger, Text, useDisclosure, -} from '@chakra-ui/react' +} from '@chakra-ui/react'; interface EditableTextProps { - value: string | number - label: string - onSubmit: (value: string) => void - flexProps?: FlexProps + value: string | number; + label: string; + onSubmit: (value: string) => void; + flexProps?: FlexProps; } export const EditableText = ({ value, label, onSubmit, flexProps }: EditableTextProps) => { - const [editValue, setEditValue] = useState(String(value)) - const { isOpen, onOpen, onClose } = useDisclosure() + const [editValue, setEditValue] = useState(String(value)); + const { isOpen, onOpen, onClose } = useDisclosure(); const handleSubmit = () => { - onSubmit(editValue) - onClose() - } + onSubmit(editValue); + onClose(); + }; const handleCancel = () => { // 关闭时 恢复到传递来的初始值 - setEditValue(String(value)) - onClose() - } + setEditValue(String(value)); + onClose(); + }; return ( @@ -187,5 +187,5 @@ export const EditableText = ({ value, label, onSubmit, flexProps }: EditableText - ) -} + ); +}; diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/ModelConfig.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/ModelConfig.tsx index b4b3df708..e12b2b49a 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/ModelConfig.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/components/ModelConfig.tsx @@ -1,32 +1,32 @@ -'use client' -import { Dispatch, SetStateAction, useEffect, useState } from 'react' -import { Controller, FieldError, FieldErrors, FieldErrorsImpl, useForm } from 'react-hook-form' -import { Button, Flex, FormControl, Skeleton, Text, VStack } from '@chakra-ui/react' -import { zodResolver } from '@hookform/resolvers/zod' -import { useMessage } from '@sealos/ui' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { z } from 'zod' +'use client'; +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { Controller, FieldError, FieldErrors, FieldErrorsImpl, useForm } from 'react-hook-form'; +import { Button, Flex, FormControl, Skeleton, Text, VStack } from '@chakra-ui/react'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { z } from 'zod'; import { batchOption, getChannelBuiltInSupportModels, getChannelTypeNames, getOption, -} from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import ConstructMappingComponent from '@/components/common/ConstructMappingComponent' -import { MultiSelectCombobox } from '@/components/common/MultiSelectCombobox' -import { SingleSelectCombobox } from '@/components/common/SingleSelectCombobox' -import { useI18n } from '@/providers/i18n/i18nContext' -import { ChannelType } from '@/types/admin/channels/channelInfo' -import { DefaultChannelModel, DefaultChannelModelMapping } from '@/types/admin/option' -import { BatchOptionData } from '@/types/admin/option' -import { QueryKey } from '@/types/query-key' +} from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import ConstructMappingComponent from '@/components/common/ConstructMappingComponent'; +import { MultiSelectCombobox } from '@/components/common/MultiSelectCombobox'; +import { SingleSelectCombobox } from '@/components/common/SingleSelectCombobox'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { ChannelType } from '@/types/admin/channels/channelInfo'; +import { DefaultChannelModel, DefaultChannelModelMapping } from '@/types/admin/option'; +import { BatchOptionData } from '@/types/admin/option'; +import { QueryKey } from '@/types/query-key'; const ModelConfig = () => { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const queryClient = useQueryClient() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const queryClient = useQueryClient(); const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -36,51 +36,51 @@ const ModelConfig = () => { successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); - const [allSupportChannel, setAllSupportChannel] = useState([]) + const [allSupportChannel, setAllSupportChannel] = useState([]); const [allSupportChannelWithMode, setAllSupportChannelWithMode] = useState<{ - [key in ChannelType]: string[] - }>({}) + [key in ChannelType]: string[]; + }>({}); - const [defaultModel, setDefaultModel] = useState({}) - const [defaultModelMapping, setDefaultModelMapping] = useState({}) + const [defaultModel, setDefaultModel] = useState({}); + const [defaultModelMapping, setDefaultModelMapping] = useState({}); const { isLoading: isChannelTypeNamesLoading, data: channelTypeNames } = useQuery({ queryKey: [QueryKey.GetChannelTypeNames], queryFn: () => getChannelTypeNames(), - }) + }); const { isLoading: isBuiltInSupportModelsLoading, data: builtInSupportModels } = useQuery({ queryKey: [QueryKey.GetAllChannelModes], queryFn: () => getChannelBuiltInSupportModels(), - }) + }); const { isLoading: isOptionLoading, data: optionData } = useQuery({ queryKey: [QueryKey.GetOption], queryFn: () => getOption(), onSuccess: (data) => { - if (!data) return + if (!data) return; - const defaultModels: DefaultChannelModel = JSON.parse(data.DefaultChannelModels) + const defaultModels: DefaultChannelModel = JSON.parse(data.DefaultChannelModels); const defaultModelMappings: DefaultChannelModelMapping = JSON.parse( data.DefaultChannelModelMapping - ) + ); - setDefaultModel(defaultModels) - setDefaultModelMapping(defaultModelMappings) + setDefaultModel(defaultModels); + setDefaultModelMapping(defaultModelMappings); }, - }) + }); useEffect(() => { - if (!channelTypeNames || !builtInSupportModels) return + if (!channelTypeNames || !builtInSupportModels) return; // 1. 处理 allSupportChannel const supportedChannels = Object.entries(channelTypeNames) .filter(([channel]) => channel in builtInSupportModels) - .map(([_, name]) => name) + .map(([_, name]) => name); - setAllSupportChannel(supportedChannels) + setAllSupportChannel(supportedChannels); // 2. 处理 allSupportChannelWithMode // 渠道类型可能出现在 channelTypeNames 中,但不在 builtInSupportModels 中,所以需要过滤 @@ -88,17 +88,17 @@ const ModelConfig = () => { const channelWithModes = Object.entries(channelTypeNames) .filter(([channelType, _]) => channelType in builtInSupportModels) .reduce((acc, [channelType, channelName]) => { - const modelInfos = builtInSupportModels[channelType as ChannelType] || [] - const models = [...new Set(modelInfos.map((info) => info.model))] + const modelInfos = builtInSupportModels[channelType as ChannelType] || []; + const models = [...new Set(modelInfos.map((info) => info.model))]; return { ...acc, [channelType]: models, - } - }, {} as { [key in ChannelType]: string[] }) + }; + }, {} as { [key in ChannelType]: string[] }); - setAllSupportChannelWithMode(channelWithModes) - }, [channelTypeNames, builtInSupportModels]) + setAllSupportChannelWithMode(channelWithModes); + }, [channelTypeNames, builtInSupportModels]); // form schema const itemSchema = z.object({ @@ -108,15 +108,15 @@ const ModelConfig = () => { .record(z.string(), z.string()) .refine((mapping) => { // 检查所有值不能为空字符串 - return Object.values(mapping).every((value) => value.trim() !== '') + return Object.values(mapping).every((value) => value.trim() !== ''); }) .default({}), - }) + }); - const schema = z.array(itemSchema) + const schema = z.array(itemSchema); - type ConfigItem = z.infer - type FormData = ConfigItem[] + type ConfigItem = z.infer; + type FormData = ConfigItem[]; const { register, @@ -131,12 +131,12 @@ const ModelConfig = () => { defaultValues: [], mode: 'onChange', reValidateMode: 'onChange', - }) + }); useEffect(() => { - if (!defaultModel || !defaultModelMapping) return + if (!defaultModel || !defaultModelMapping) return; // Only proceed if both defaultModel and defaultModelMapping are available - if (Object.keys(defaultModel).length === 0) return + if (Object.keys(defaultModel).length === 0) return; // Transform the data into form format const formData: FormData = Object.entries(defaultModel).map(([channelType, modes]) => { @@ -144,33 +144,33 @@ const ModelConfig = () => { type: Number(channelType), defaultMode: modes || [], // Using first mode as default defaultModeMapping: defaultModelMapping[channelType as ChannelType] || {}, - } - }) + }; + }); // Reset form with the new values - reset(formData) - }, [defaultModel, defaultModelMapping, reset]) + reset(formData); + }, [defaultModel, defaultModelMapping, reset]); const handleAddDefaultModel = () => { const newItem = { type: undefined, // Default type value defaultMode: [], defaultModeMapping: {}, - } + }; // Get current form values - const currentValues = watch() + const currentValues = watch(); // Create new array with new item at the beginning - const newValues = [newItem, ...Object.values(currentValues)] + const newValues = [newItem, ...Object.values(currentValues)]; // Reset form with new values - reset(newValues) - } + reset(newValues); + }; - const formValues = watch() + const formValues = watch(); const formValuesArray: FormData = Array.isArray(formValues) ? formValues - : Object.values(formValues) + : Object.values(formValues); const batchOptionMutation = useMutation({ mutationFn: batchOption, @@ -178,66 +178,66 @@ const ModelConfig = () => { message({ title: t('globalConfigs.saveDefaultModelSuccess'), status: 'success', - }) + }); }, - }) + }); const transformFormDataToConfig = (formData: FormData): BatchOptionData => { // 初始化两个对象 - const defaultChannelModelMapping: Record> = {} - const defaultChannelModels: Record = {} + const defaultChannelModelMapping: Record> = {}; + const defaultChannelModels: Record = {}; // 遍历 FormData formData.forEach((item) => { - const type = item.type.toString() + const type = item.type.toString(); // 处理 DefaultChannelModelMapping if (Object.keys(item.defaultModeMapping).length > 0) { - defaultChannelModelMapping[type] = item.defaultModeMapping + defaultChannelModelMapping[type] = item.defaultModeMapping; } // 处理 DefaultChannelModels if (item.defaultMode.length > 0) { - defaultChannelModels[type] = item.defaultMode + defaultChannelModels[type] = item.defaultMode; } - }) + }); return { // 转换为 JSON 字符串 DefaultChannelModelMapping: JSON.stringify(defaultChannelModelMapping), DefaultChannelModels: JSON.stringify(defaultChannelModels), - } - } + }; + }; const resetForm = () => { - reset() - } + reset(); + }; type FieldErrorType = | FieldError | FieldErrorsImpl<{ - type: number - defaultMode: string[] - defaultModeMapping: Record - }> + type: number; + defaultMode: string[]; + defaultModeMapping: Record; + }>; const getFirstErrorMessage = (errors: FieldErrors): string => { // Iterate through top-level errors for (const index in errors) { - const fieldError = errors[index] as FieldErrorType - if (!fieldError) continue + const fieldError = errors[index] as FieldErrorType; + if (!fieldError) continue; // Check if error is an object if (typeof fieldError === 'object') { // If it has a direct message property if ('message' in fieldError && fieldError.message) { - return `Item ${Number(index) + 1}: ${fieldError.message}` + return `Item ${Number(index) + 1}: ${fieldError.message}`; } // Iterate through nested field errors - const errorKeys = Object.keys(fieldError) as Array + const errorKeys = Object.keys(fieldError) as Array; for (const fieldName of errorKeys) { - const nestedError = fieldError[fieldName] + const nestedError = fieldError[fieldName]; if (nestedError && typeof nestedError === 'object' && 'message' in nestedError) { // Map field names to their display labels const fieldLabel = @@ -245,25 +245,25 @@ const ModelConfig = () => { type: 'Type', defaultMode: 'Default Mode', defaultModeMapping: 'Model Mapping', - }[fieldName as string] || fieldName + }[fieldName as string] || fieldName; - return `Item ${Number(index) + 1} ${fieldLabel}: ${nestedError.message}` + return `Item ${Number(index) + 1} ${fieldLabel}: ${nestedError.message}`; } } } } - return 'Form validation failed' - } + return 'Form validation failed'; + }; const onValidate = async (data: FormData) => { try { - const batchOptionData: BatchOptionData = transformFormDataToConfig(data) - await batchOptionMutation.mutateAsync(batchOptionData) + const batchOptionData: BatchOptionData = transformFormDataToConfig(data); + await batchOptionMutation.mutateAsync(batchOptionData); - queryClient.invalidateQueries({ queryKey: [QueryKey.GetOption] }) - queryClient.invalidateQueries({ queryKey: [QueryKey.GetChannelTypeNames] }) - queryClient.invalidateQueries({ queryKey: [QueryKey.GetAllChannelModes] }) - resetForm() + queryClient.invalidateQueries({ queryKey: [QueryKey.GetOption] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.GetChannelTypeNames] }); + queryClient.invalidateQueries({ queryKey: [QueryKey.GetAllChannelModes] }); + resetForm(); } catch (error) { message({ title: t('globalConfigs.saveDefaultModelFailed'), @@ -273,15 +273,15 @@ const ModelConfig = () => { isClosable: true, description: error instanceof Error ? error.message : t('globalConfigs.saveDefaultModelFailed'), - }) - console.error(error) + }); + console.error(error); } - } + }; const onInvalid = (errors: FieldErrors): void => { - console.error('errors', errors) + console.error('errors', errors); - const errorMessage = getFirstErrorMessage(errors) + const errorMessage = getFirstErrorMessage(errors); message({ title: errorMessage, @@ -289,10 +289,10 @@ const ModelConfig = () => { position: 'top', duration: 2000, isClosable: true, - }) - } + }); + }; - const onSubmit = handleSubmit(onValidate, onInvalid) + const onSubmit = handleSubmit(onValidate, onInvalid); return ( /* 顶级 Flex 容器的高度: calc(100vh - 16px - 24px - 12px - 32px - 36px) @@ -471,9 +471,9 @@ const ModelConfig = () => { color: '#D92D20', }} onClick={() => { - const currentValues = watch() - const newValues = Object.values(currentValues).filter((_, i) => i !== index) - reset(newValues) + const currentValues = watch(); + const newValues = Object.values(currentValues).filter((_, i) => i !== index); + reset(newValues); }} > { (type): type is ChannelType => type !== undefined && type in channelTypeNames ) - .map((type) => channelTypeNames[type]) + .map((type) => channelTypeNames[type]); // Filter available types const availableTypes = allSupportChannel.filter( @@ -521,11 +521,11 @@ const ModelConfig = () => { (field.value && channelTypeNames[String(field.value) as ChannelType] === channelType) - ) + ); const initSelectedItem = field.value ? channelTypeNames[String(field.value) as ChannelType] - : undefined + : undefined; return ( @@ -535,20 +535,20 @@ const ModelConfig = () => { if (channelName) { const channelType = Object.entries(channelTypeNames).find( ([_, name]) => name === channelName - )?.[0] + )?.[0]; if (channelType) { const defaultModelField = - defaultModel[channelType as ChannelType] + defaultModel[channelType as ChannelType]; const defaultModelMappingField = - defaultModelMapping[channelType as ChannelType] + defaultModelMapping[channelType as ChannelType]; - field.onChange(Number(channelType)) - setValue(`${index}.defaultMode`, defaultModelField || []) + field.onChange(Number(channelType)); + setValue(`${index}.defaultMode`, defaultModelField || []); setValue( `${index}.defaultModeMapping`, defaultModelMappingField || {} - ) + ); } } }} @@ -556,11 +556,11 @@ const ModelConfig = () => { dropdownItems: string[], inputValue: string ) => { - const lowerCasedInput = inputValue.toLowerCase() + const lowerCasedInput = inputValue.toLowerCase(); return dropdownItems.filter( (item) => !inputValue || item.toLowerCase().includes(lowerCasedInput) - ) + ); }} handleDropdownItemDisplay={(item: string) => ( { )} /> - ) + ); }} /> @@ -587,10 +587,10 @@ const ModelConfig = () => { control={control} render={({ field }) => { // Get the current type value from the form - const currentType = watch(`${index}.type`) + const currentType = watch(`${index}.type`); const dropdownItems = currentType ? allSupportChannelWithMode[String(currentType) as ChannelType] || [] - : [] + : []; const handleSetCustomModel = ( selectedItems: string[], @@ -601,35 +601,35 @@ const ModelConfig = () => { if (customModeName.trim()) { const exists = field.value.some( (item) => item === customModeName.trim() - ) + ); if (!exists) { - field.onChange([...field.value, customModeName.trim()]) - setCustomModeName('') + field.onChange([...field.value, customModeName.trim()]); + setCustomModeName(''); } } - } + }; const handleModelFilteredDropdownItems = ( dropdownItems: string[], selectedItems: string[], inputValue: string ) => { - const lowerCasedInputValue = inputValue.toLowerCase() + const lowerCasedInputValue = inputValue.toLowerCase(); return dropdownItems.filter( (item) => !selectedItems.includes(item) && item.toLowerCase().includes(lowerCasedInputValue) - ) - } + ); + }; return ( dropdownItems={dropdownItems || []} selectedItems={field.value || []} // Use field.value for selected items setSelectedItems={(models) => { - field.onChange(models) + field.onChange(models); }} handleFilteredDropdownItems={handleModelFilteredDropdownItems} handleDropdownItemDisplay={(item) => ( @@ -659,7 +659,7 @@ const ModelConfig = () => { )} handleSetCustomSelectedItem={handleSetCustomModel} /> - ) + ); }} /> @@ -669,31 +669,31 @@ const ModelConfig = () => { name={`${index}.defaultModeMapping`} control={control} render={({ field }) => { - const defaultMode = watch(`${index}.defaultMode`) - const defaultModeMapping = watch(`${index}.defaultModeMapping`) + const defaultMode = watch(`${index}.defaultMode`); + const defaultModeMapping = watch(`${index}.defaultModeMapping`); return ( { - field.onChange(mapping) + field.onChange(mapping); }} /> - ) + ); }} /> - ) + ); }) )} {/* -- config end */} - ) -} + ); +}; -export default ModelConfig +export default ModelConfig; diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/page.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/page.tsx index 8cb0e24b9..d435c0d40 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/global-configs/page.tsx @@ -1,23 +1,23 @@ -'use client' -import { useRef } from 'react' -import { Button, Divider, Flex, Text, useDisclosure } from '@chakra-ui/react' -import { useMessage } from '@sealos/ui/src/components' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +'use client'; +import { useRef } from 'react'; +import { Button, Divider, Flex, Text, useDisclosure } from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui/src/components'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { getOption, uploadOptions } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { QueryKey } from '@/types/query-key' -import { downloadJson } from '@/utils/common' +import { getOption, uploadOptions } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { QueryKey } from '@/types/query-key'; +import { downloadJson } from '@/utils/common'; -import CommonConfig from './components/CommonConfig' -import ModelConfig from './components/ModelConfig' +import CommonConfig from './components/CommonConfig'; +import ModelConfig from './components/ModelConfig'; export default function GlobalConfigPage() { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); - const fileInputRef = useRef(null) + const fileInputRef = useRef(null); const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -27,9 +27,9 @@ export default function GlobalConfigPage() { successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); - const queryClient = useQueryClient() + const queryClient = useQueryClient(); const { isFetching: isOptionFetching, @@ -40,53 +40,53 @@ export default function GlobalConfigPage() { queryFn: () => getOption(), refetchOnReconnect: false, enabled: false, - }) + }); const uploadMutation = useMutation({ mutationFn: uploadOptions, - }) + }); const handleExport = async () => { - const result = await refetch() - const dataToExport = result.data || [] - downloadJson(dataToExport, 'global_configs') - } + const result = await refetch(); + const dataToExport = result.data || []; + downloadJson(dataToExport, 'global_configs'); + }; const handleImport = () => { - fileInputRef.current?.click() - } + fileInputRef.current?.click(); + }; const handleFileChange = async (event: React.ChangeEvent) => { - const file = event.target.files?.[0] - if (!file) return + const file = event.target.files?.[0]; + if (!file) return; - const formData = new FormData() - formData.append('file', file) + const formData = new FormData(); + formData.append('file', file); try { - await uploadMutation.mutateAsync(formData) + await uploadMutation.mutateAsync(formData); message({ title: t('dashboard.importSuccess'), status: 'success', duration: 3000, isClosable: true, - }) - queryClient.invalidateQueries([QueryKey.GetOption]) - queryClient.invalidateQueries([QueryKey.GetCommonConfig]) + }); + queryClient.invalidateQueries([QueryKey.GetOption]); + queryClient.invalidateQueries([QueryKey.GetCommonConfig]); } catch (error) { - console.error('Import error:', error) + console.error('Import error:', error); message({ title: t('dashboard.importError'), status: 'error', duration: 3000, isClosable: true, - }) + }); } finally { if (fileInputRef.current) { - fileInputRef.current.value = '' + fileInputRef.current.value = ''; } } - } + }; return ( @@ -265,5 +265,5 @@ export default function GlobalConfigPage() { {/* -- config end */} - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/global-logs/page.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/global-logs/page.tsx index 1356ec88b..ac6546089 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/global-logs/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/global-logs/page.tsx @@ -1,42 +1,42 @@ -'use client' +'use client'; -import { useMemo, useState } from 'react' -import { Box, Button, Flex, Icon, Input, Text } from '@chakra-ui/react' -import { CurrencySymbol, MyTooltip } from '@sealos/ui' -import { useQuery } from '@tanstack/react-query' -import { ColumnDef, getCoreRowModel, useReactTable } from '@tanstack/react-table' +import { useMemo, useState } from 'react'; +import { Box, Button, Flex, Icon, Input, Text } from '@chakra-ui/react'; +import { CurrencySymbol, MyTooltip } from '@sealos/ui'; +import { useQuery } from '@tanstack/react-query'; +import { ColumnDef, getCoreRowModel, useReactTable } from '@tanstack/react-table'; -import { getEnabledMode, getGlobalLogs } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import SelectDateRange from '@/components/common/SelectDateRange' -import { SingleSelectComboboxUnstyle } from '@/components/common/SingleSelectComboboxUnStyle' -import SwitchPage from '@/components/common/SwitchPage' -import { BaseTable } from '@/components/table/BaseTable' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { QueryKey } from '@/types/query-key' -import { GlobalLogItem } from '@/types/user/logs' +import { getEnabledMode, getGlobalLogs } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import SelectDateRange from '@/components/common/SelectDateRange'; +import { SingleSelectComboboxUnstyle } from '@/components/common/SingleSelectComboboxUnStyle'; +import SwitchPage from '@/components/common/SwitchPage'; +import { BaseTable } from '@/components/table/BaseTable'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { QueryKey } from '@/types/query-key'; +import { GlobalLogItem } from '@/types/user/logs'; export default function Home(): React.JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { currencySymbol } = useBackendStore() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { currencySymbol } = useBackendStore(); const [startTime, setStartTime] = useState(() => { - const currentDate = new Date() - currentDate.setMonth(currentDate.getMonth() - 1) - return currentDate - }) - const [endTime, setEndTime] = useState(new Date()) - const [groupId, setGroupId] = useState('') - const [name, setName] = useState('') - const [modelName, setModelName] = useState('') - const [page, setPage] = useState(1) - const [pageSize, setPageSize] = useState(10) - const [logData, setLogData] = useState([]) - const [total, setTotal] = useState(0) + const currentDate = new Date(); + currentDate.setMonth(currentDate.getMonth() - 1); + return currentDate; + }); + const [endTime, setEndTime] = useState(new Date()); + const [groupId, setGroupId] = useState(''); + const [name, setName] = useState(''); + const [modelName, setModelName] = useState(''); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [logData, setLogData] = useState([]); + const [total, setTotal] = useState(0); - const { data: models = [] } = useQuery([QueryKey.GetEnabledModels], () => getEnabledMode()) + const { data: models = [] } = useQuery([QueryKey.GetEnabledModels], () => getEnabledMode()); const { isLoading } = useQuery( [QueryKey.GetGlobalLogs, page, pageSize, name, modelName, startTime, endTime, groupId], @@ -53,15 +53,15 @@ export default function Home(): React.JSX.Element { { onSuccess: (data) => { if (!data?.logs) { - setLogData([]) - setTotal(0) - return + setLogData([]); + setTotal(0); + return; } - setLogData(data?.logs || []) - setTotal(data?.total || 0) + setLogData(data?.logs || []); + setTotal(data?.total || 0); }, } - ) + ); const columns = useMemo[]>(() => { return [ @@ -94,7 +94,7 @@ export default function Home(): React.JSX.Element { header: t('logs.status'), accessorFn: (row) => (row.code === 200 ? t('logs.success') : t('logs.failed')), cell: ({ getValue }) => { - const value = getValue() as string + const value = getValue() as string; return ( {value} - ) + ); }, id: 'status', }, @@ -142,17 +142,17 @@ export default function Home(): React.JSX.Element { - ) + ); }, }, - ] - }, []) + ]; + }, []); const table = useReactTable({ data: logData, columns, getCoreRowModel: getCoreRowModel(), - }) + }); return ( { - setName('') - setModelName('') + setName(''); + setModelName(''); }} > item.model)]} setSelectedItem={(value) => { if (value === 'all') { - setModelName('') + setModelName(''); } else { - setModelName(value) + setModelName(value); } }} handleDropdownItemFilter={(dropdownItems, inputValue) => { - const lowerCasedInput = inputValue.toLowerCase() + const lowerCasedInput = inputValue.toLowerCase(); return dropdownItems.filter( (item) => !inputValue || item.toLowerCase().includes(lowerCasedInput) - ) + ); }} handleDropdownItemDisplay={(dropdownItem) => { return ( @@ -339,7 +339,7 @@ export default function Home(): React.JSX.Element { > {dropdownItem} - ) + ); }} flexProps={{ w: '500px' }} placeholder={t('GlobalLogs.selectModel')} @@ -447,5 +447,5 @@ export default function Home(): React.JSX.Element { {/* -- table end */} - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/layout.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/layout.tsx index ea4220e63..3f26ae594 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/layout.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/layout.tsx @@ -1,6 +1,6 @@ -import { Box, Flex } from '@chakra-ui/react' +import { Box, Flex } from '@chakra-ui/react'; -import SideBar from '@/components/admin/Sidebar' +import SideBar from '@/components/admin/Sidebar'; export default function UserLayout({ children }: { children: React.ReactNode }) { return ( @@ -13,5 +13,5 @@ export default function UserLayout({ children }: { children: React.ReactNode }) {children} - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(admin)/ns-manager/page.tsx b/frontend/providers/aiproxy/app/[lng]/(admin)/ns-manager/page.tsx index d28d603cd..2dfb14e54 100644 --- a/frontend/providers/aiproxy/app/[lng]/(admin)/ns-manager/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(admin)/ns-manager/page.tsx @@ -1,6 +1,6 @@ -'use client' +'use client'; -import { useMemo, useState } from 'react' +import { useMemo, useState } from 'react'; import { Box, Button, @@ -12,31 +12,31 @@ import { MenuItem, MenuList, Text, -} from '@chakra-ui/react' -import { CurrencySymbol } from '@sealos/ui' -import { useMessage } from '@sealos/ui' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +} from '@chakra-ui/react'; +import { CurrencySymbol } from '@sealos/ui'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { ColumnDef, createColumnHelper, getCoreRowModel, useReactTable, -} from '@tanstack/react-table' +} from '@tanstack/react-table'; -import { deleteGroup, getGroups, updateGroupQpm, updateGroupStatus } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { EditableTextNoLable } from '@/components/common/EditableTextNoLable' -import SwitchPage from '@/components/common/SwitchPage' -import { BaseTable } from '@/components/table/BaseTable' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { GroupInfo, GroupStatus } from '@/types/admin/group' -import { QueryKey } from '@/types/query-key' +import { deleteGroup, getGroups, updateGroupQpm, updateGroupStatus } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { EditableTextNoLable } from '@/components/common/EditableTextNoLable'; +import SwitchPage from '@/components/common/SwitchPage'; +import { BaseTable } from '@/components/table/BaseTable'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { GroupInfo, GroupStatus } from '@/types/admin/group'; +import { QueryKey } from '@/types/query-key'; export default function Home(): React.JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { currencySymbol } = useBackendStore() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { currencySymbol } = useBackendStore(); const { message } = useMessage({ warningBoxBg: '#FFFAEB', warningIconBg: '#F79009', @@ -44,15 +44,15 @@ export default function Home(): React.JSX.Element { successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); - const queryClient = useQueryClient() + const queryClient = useQueryClient(); - const [groupId, setGroupId] = useState('') - const [page, setPage] = useState(1) - const [pageSize, setPageSize] = useState(10) - const [groupData, setGroupData] = useState([]) - const [total, setTotal] = useState(0) + const [groupId, setGroupId] = useState(''); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [groupData, setGroupData] = useState([]); + const [total, setTotal] = useState(0); const { isLoading } = useQuery( [QueryKey.GetGroups, page, pageSize, groupId], @@ -65,15 +65,15 @@ export default function Home(): React.JSX.Element { { onSuccess: (data) => { if (!data?.groups) { - setGroupData([]) - setTotal(0) - return + setGroupData([]); + setTotal(0); + return; } - setGroupData(data?.groups || []) - setTotal(data?.total || 0) + setGroupData(data?.groups || []); + setTotal(data?.total || 0); }, } - ) + ); const deleteGroupMutation = useMutation(({ id }: { id: string }) => deleteGroup(id), { onSuccess() { @@ -83,8 +83,8 @@ export default function Home(): React.JSX.Element { isClosable: true, duration: 2000, position: 'top', - }) - queryClient.invalidateQueries([QueryKey.GetGroups]) + }); + queryClient.invalidateQueries([QueryKey.GetGroups]); }, onError(err: any) { message({ @@ -93,9 +93,9 @@ export default function Home(): React.JSX.Element { description: err?.message || t('nsManager.deleteGroupFailed'), isClosable: true, position: 'top', - }) + }); }, - }) + }); const updateGroupStatusMutation = useMutation( ({ id, status }: { id: string; status: number }) => updateGroupStatus(id, status), @@ -107,8 +107,8 @@ export default function Home(): React.JSX.Element { isClosable: true, duration: 2000, position: 'top', - }) - queryClient.invalidateQueries([QueryKey.GetGroups]) + }); + queryClient.invalidateQueries([QueryKey.GetGroups]); }, onError(err: any) { message({ @@ -117,10 +117,10 @@ export default function Home(): React.JSX.Element { description: err?.message || t('nsManager.updateGroupStatusFailed'), isClosable: true, position: 'top', - }) + }); }, } - ) + ); const updateGroupQpmMutation = useMutation( ({ id, qpm }: { id: string; qpm: number }) => updateGroupQpm(id, qpm), @@ -132,8 +132,8 @@ export default function Home(): React.JSX.Element { isClosable: true, duration: 2000, position: 'top', - }) - queryClient.invalidateQueries([QueryKey.GetGroups]) + }); + queryClient.invalidateQueries([QueryKey.GetGroups]); }, onError(err: any) { message({ @@ -142,18 +142,18 @@ export default function Home(): React.JSX.Element { description: err?.message || t('nsManager.updateGroupQpmFailed'), isClosable: true, position: 'top', - }) + }); }, } - ) + ); // Update the button click handlers in the table actions column: const handleStatusUpdate = (id: string, currentStatus: number) => { const newStatus = - currentStatus === GroupStatus.DISABLED ? GroupStatus.ENABLED : GroupStatus.DISABLED - updateGroupStatusMutation.mutate({ id, status: newStatus }) - } - const columnHelper = createColumnHelper() + currentStatus === GroupStatus.DISABLED ? GroupStatus.ENABLED : GroupStatus.DISABLED; + updateGroupStatusMutation.mutate({ id, status: newStatus }); + }; + const columnHelper = createColumnHelper(); const columns = useMemo[]>(() => { return [ @@ -186,10 +186,10 @@ export default function Home(): React.JSX.Element { header: t('nsManager.accessed_at'), accessorFn: (row) => { if (row.accessed_at && row.accessed_at < 0) { - return t('key.unused') + return t('key.unused'); } - return new Date(row.accessed_at).toLocaleString() + return new Date(row.accessed_at).toLocaleString(); }, id: 'accessed_at', }, @@ -202,7 +202,7 @@ export default function Home(): React.JSX.Element { accessorFn: (row) => row.status === GroupStatus.ENABLED ? t('nsManager.enabled') : t('nsManager.disabled'), cell: ({ getValue }) => { - const value = getValue() as string + const value = getValue() as string; return ( {value} - ) + ); }, id: 'status', }, @@ -243,7 +243,7 @@ export default function Home(): React.JSX.Element { - ) + ); }, }, columnHelper.display({ @@ -409,14 +409,14 @@ export default function Home(): React.JSX.Element { ), }), - ] - }, []) + ]; + }, []); const table = useReactTable({ data: groupData, columns, getCoreRowModel: getCoreRowModel(), - }) + }); return ( { - setGroupId('') + setGroupId(''); }} > - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/home/components/RequestDataChart.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/home/components/RequestDataChart.tsx index dd6046755..b6ec7a78a 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/home/components/RequestDataChart.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/home/components/RequestDataChart.tsx @@ -1,52 +1,52 @@ -import { useEffect, useRef } from 'react' -import { Box, Flex, Text } from '@chakra-ui/react' -import * as echarts from 'echarts' +import { useEffect, useRef } from 'react'; +import { Box, Flex, Text } from '@chakra-ui/react'; +import * as echarts from 'echarts'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { ChartDataItem } from '@/types/user/dashboard' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { ChartDataItem } from '@/types/user/dashboard'; export default function RequestDataChart({ data }: { data: ChartDataItem[] }): React.JSX.Element { - const costChartRef = useRef(null) - const requestChartRef = useRef(null) - const costChartInstance = useRef() - const requestChartInstance = useRef() - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { currencySymbol } = useBackendStore() + const costChartRef = useRef(null); + const requestChartRef = useRef(null); + const costChartInstance = useRef(); + const requestChartInstance = useRef(); + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { currencySymbol } = useBackendStore(); // Add helper function to determine date format const getDateFormat = (timestamps: number[]) => { - if (timestamps.length < 2) return 'detailed' + if (timestamps.length < 2) return 'detailed'; - const timeDiff = timestamps[timestamps.length - 1] - timestamps[0] + const timeDiff = timestamps[timestamps.length - 1] - timestamps[0]; // If time difference is more than 15 days (1296000 seconds), show daily format - return timeDiff > 1296000 ? 'daily' : 'detailed' - } + return timeDiff > 1296000 ? 'daily' : 'detailed'; + }; // 初始化图表 useEffect(() => { if (costChartRef.current && requestChartRef.current) { costChartInstance.current = echarts.init(costChartRef.current, undefined, { renderer: 'svg', - }) + }); requestChartInstance.current = echarts.init(requestChartRef.current, undefined, { renderer: 'svg', - }) + }); } return () => { - costChartInstance.current?.dispose() - requestChartInstance.current?.dispose() - costChartInstance.current = undefined - requestChartInstance.current = undefined - } - }, []) + costChartInstance.current?.dispose(); + requestChartInstance.current?.dispose(); + costChartInstance.current = undefined; + requestChartInstance.current = undefined; + }; + }, []); // 配置图表选项 useEffect(() => { - if (!costChartInstance.current || !requestChartInstance.current) return + if (!costChartInstance.current || !requestChartInstance.current) return; const commonTooltipStyle: echarts.EChartsOption['tooltip'] = { trigger: 'axis', @@ -63,7 +63,7 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R color: '#111824', fontSize: 12, }, - } + }; const commonXAxis: echarts.EChartsOption['xAxis'] = { type: 'time', @@ -94,8 +94,8 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R show: true, color: '#667085', formatter: (value: number) => { - const date = new Date(value * 1000) - const format = getDateFormat(data.map((item) => item.timestamp)) + const date = new Date(value * 1000); + const format = getDateFormat(data.map((item) => item.timestamp)); return date .toLocaleString(lng, { @@ -107,12 +107,12 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R hour12: false, }), }) - .replace(/\//g, '-') + .replace(/\//g, '-'); }, margin: 14, align: 'left', }, - } + }; // 成本图表配置 const costOption: echarts.EChartsOption = { @@ -123,22 +123,22 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R | echarts.DefaultLabelFormatterCallbackParams | echarts.DefaultLabelFormatterCallbackParams[] ) { - if (!params) return '' - const paramArray = Array.isArray(params) ? params : [params] - if (paramArray.length === 0) return '' + if (!params) return ''; + const paramArray = Array.isArray(params) ? params : [params]; + if (paramArray.length === 0) return ''; - const time = new Date((paramArray[0].value as [number, number])[0] * 1000) + const time = new Date((paramArray[0].value as [number, number])[0] * 1000); const timeStr = time.toLocaleString(lng, { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', - }) + }); let result = `
${timeStr}
- ` + `; const currency = currencySymbol === 'shellCoin' @@ -200,14 +200,14 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R ` : currencySymbol === 'cny' ? '¥' - : '$' + : '$'; paramArray.forEach((param) => { - const value = (param.value as [number, number])[1] + const value = (param.value as [number, number])[1]; const formattedValue = Number(value).toLocaleString(lng, { minimumFractionDigits: 0, maximumFractionDigits: 4, - }) + }); result += `
@@ -217,10 +217,10 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R
${formattedValue}
- ` - }) + `; + }); - return result + return result; }, }, legend: { @@ -269,32 +269,32 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R }, }, ], - } + }; // 请求数图表配置 const requestOption: echarts.EChartsOption = { tooltip: { ...commonTooltipStyle, formatter: function (params) { - if (!params) return '' - const paramArray = Array.isArray(params) ? params : [params] - if (paramArray.length === 0) return '' + if (!params) return ''; + const paramArray = Array.isArray(params) ? params : [params]; + if (paramArray.length === 0) return ''; - const time = new Date((paramArray[0].value as [number, number])[0] * 1000) + const time = new Date((paramArray[0].value as [number, number])[0] * 1000); const timeStr = time.toLocaleString(lng, { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', - }) + }); let result = `
${timeStr}
- ` + `; paramArray.forEach((param) => { - const value = (param.value as [number, number])[1] + const value = (param.value as [number, number])[1]; result += `
@@ -303,10 +303,10 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R
${value}
- ` - }) + `; + }); - return result + return result; }, }, legend: { @@ -363,31 +363,31 @@ export default function RequestDataChart({ data }: { data: ChartDataItem[] }): R }, }, ], - } + }; // 设置图表选项 - costChartInstance.current.setOption(costOption) - requestChartInstance.current.setOption(requestOption) + costChartInstance.current.setOption(costOption); + requestChartInstance.current.setOption(requestOption); // 图表联动 - costChartInstance.current.group = 'request-data' - requestChartInstance.current.group = 'request-data' - echarts.connect('request-data') - }, [data, t, lng]) + costChartInstance.current.group = 'request-data'; + requestChartInstance.current.group = 'request-data'; + echarts.connect('request-data'); + }, [data, t, lng]); // 处理窗口大小变化 useEffect(() => { const handleResize = () => { - costChartInstance.current?.resize() - requestChartInstance.current?.resize() - } + costChartInstance.current?.resize(); + requestChartInstance.current?.resize(); + }; - window.addEventListener('resize', handleResize) + window.addEventListener('resize', handleResize); return () => { - window.removeEventListener('resize', handleResize) - } - }, []) + window.removeEventListener('resize', handleResize); + }; + }, []); return ( - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx index ade129ea0..3ef570431 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/home/page.tsx @@ -1,27 +1,27 @@ -'use client' +'use client'; -import { useState } from 'react' -import { Box, Button, Center, Flex, Text } from '@chakra-ui/react' -import { CurrencySymbol, MySelect } from '@sealos/ui' -import { useQuery, UseQueryResult } from '@tanstack/react-query' +import { useState } from 'react'; +import { Box, Button, Center, Flex, Text } from '@chakra-ui/react'; +import { CurrencySymbol, MySelect } from '@sealos/ui'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; -import { getDashboardData } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { QueryKey } from '@/types/query-key' -import { DashboardResponse } from '@/types/user/dashboard' +import { getDashboardData } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { QueryKey } from '@/types/query-key'; +import { DashboardResponse } from '@/types/user/dashboard'; -import RequestDataChart from './components/RequestDataChart' +import RequestDataChart from './components/RequestDataChart'; export default function Home(): React.JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { currencySymbol } = useBackendStore() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { currencySymbol } = useBackendStore(); - const [tokenName, setTokenName] = useState('') - const [model, setModel] = useState('') - const [type, setType] = useState<'week' | 'day' | 'two_week' | 'month'>('week') // default is week + const [tokenName, setTokenName] = useState(''); + const [model, setModel] = useState(''); + const [type, setType] = useState<'week' | 'day' | 'two_week' | 'month'>('week'); // default is week const { data: dashboardData, isLoading }: UseQueryResult = useQuery( [QueryKey.GetDashboardData, type, tokenName, model], @@ -31,7 +31,7 @@ export default function Home(): React.JSX.Element { ...(tokenName && { token_name: tokenName }), ...(model && { model }), }) - ) + ); return ( { if (token === 'all') { - setTokenName('') + setTokenName(''); } else { - setTokenName(token) + setTokenName(token); } }} /> @@ -125,9 +125,9 @@ export default function Home(): React.JSX.Element { ]} onchange={(model: string) => { if (model === 'all') { - setModel('') + setModel(''); } else { - setModel(model) + setModel(model); } }} /> @@ -734,5 +734,5 @@ export default function Home(): React.JSX.Element { - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/key/page.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/key/page.tsx index 795978d94..37ea8a3d0 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/key/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/key/page.tsx @@ -1,24 +1,24 @@ -'use client' +'use client'; -import { Button, Flex, Text } from '@chakra-ui/react' +import { Button, Flex, Text } from '@chakra-ui/react'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { MyTooltip } from '@/components/common/MyTooltip' -import KeyList from '@/components/user/KeyList' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { useSessionStore } from '@/store/session' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { MyTooltip } from '@/components/common/MyTooltip'; +import KeyList from '@/components/user/KeyList'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { useSessionStore } from '@/store/session'; export default function Key(): JSX.Element { - const { isInvitationActive } = useBackendStore() - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { session } = useSessionStore.getState() - const { invitationUrl } = useBackendStore() - let userInvitationUrl = '' + const { isInvitationActive } = useBackendStore(); + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { session } = useSessionStore.getState(); + const { invitationUrl } = useBackendStore(); + let userInvitationUrl = ''; if (isInvitationActive && invitationUrl) { - const userId = session?.user.id - const baseUrl = new URL(invitationUrl).origin - userInvitationUrl = `${baseUrl}/?uid=${userId}` + const userId = session?.user.id; + const baseUrl = new URL(invitationUrl).origin; + userInvitationUrl = `${baseUrl}/?uid=${userId}`; } return ( @@ -115,7 +115,7 @@ export default function Key(): JSX.Element { textUnderlineOffset="auto" cursor="pointer" onClick={() => { - navigator.clipboard.writeText(userInvitationUrl) + navigator.clipboard.writeText(userInvitationUrl); }} > {userInvitationUrl} @@ -139,7 +139,7 @@ export default function Key(): JSX.Element { bgGradient="linear(90deg, #1058FF 0%, #838AF1 57.5%, #FF80E6 100%)" color="white" onClick={() => { - window.open(invitationUrl, '_blank') + window.open(invitationUrl, '_blank'); }} > )} - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/layout.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/layout.tsx index fae3b4718..7b16d426a 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/layout.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/layout.tsx @@ -1,6 +1,6 @@ -import { Box, Flex } from '@chakra-ui/react' +import { Box, Flex } from '@chakra-ui/react'; -import SideBar from '@/components/user/Sidebar' +import SideBar from '@/components/user/Sidebar'; export default function UserLayout({ children }: { children: React.ReactNode }) { return ( @@ -13,5 +13,5 @@ export default function UserLayout({ children }: { children: React.ReactNode }) {children} - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/log/components/LogDetailModal.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/log/components/LogDetailModal.tsx index 15b4c4be1..2c988e1be 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/log/components/LogDetailModal.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/log/components/LogDetailModal.tsx @@ -1,6 +1,6 @@ -'use client' +'use client'; -import ReactJson, { OnCopyProps } from 'react-json-view' +import ReactJson, { OnCopyProps } from 'react-json-view'; import { Box, Center, @@ -14,45 +14,45 @@ import { ModalOverlay, Spinner, Text, -} from '@chakra-ui/react' -import { CurrencySymbol } from '@sealos/ui' -import { useMessage } from '@sealos/ui' -import { useQuery } from '@tanstack/react-query' +} from '@chakra-ui/react'; +import { CurrencySymbol } from '@sealos/ui'; +import { useMessage } from '@sealos/ui'; +import { useQuery } from '@tanstack/react-query'; -import { getUserLogDetail } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { MyTooltip } from '@/components/common/MyTooltip' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { QueryKey } from '@/types/query-key' -import { LogItem } from '@/types/user/logs' -import { getTranslationWithFallback } from '@/utils/common' +import { getUserLogDetail } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { MyTooltip } from '@/components/common/MyTooltip'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { QueryKey } from '@/types/query-key'; +import { LogItem } from '@/types/user/logs'; +import { getTranslationWithFallback } from '@/utils/common'; -import { getTimeDiff } from '../tools/handleTime' +import { getTimeDiff } from '../tools/handleTime'; export default function LogDetailModal({ isOpen, onClose, rowData, }: { - isOpen: boolean - onClose: () => void - rowData: LogItem | null + isOpen: boolean; + onClose: () => void; + rowData: LogItem | null; }): React.JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { currencySymbol } = useBackendStore() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { currencySymbol } = useBackendStore(); const { data: logDetail, isLoading } = useQuery({ queryKey: [QueryKey.GetUserLogDetail, rowData?.request_detail?.log_id], queryFn: () => { - if (!rowData?.request_detail?.log_id) throw new Error('No log ID') - return getUserLogDetail(rowData.request_detail.log_id) + if (!rowData?.request_detail?.log_id) throw new Error('No log ID'); + return getUserLogDetail(rowData.request_detail.log_id); }, enabled: !!rowData?.request_detail?.log_id, - }) + }); - const isDetailLoading = !!rowData?.request_detail?.log_id && isLoading + const isDetailLoading = !!rowData?.request_detail?.log_id && isLoading; const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -61,14 +61,14 @@ export default function LogDetailModal({ successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); // 定义默认的网格配置 const gridConfig = { labelWidth: '153px', rowHeight: '48px', jsonContentHeight: '122px', - } + }; const renderDetailRow = ( leftLabel: string | React.ReactNode | null, @@ -76,15 +76,15 @@ export default function LogDetailModal({ rightLabel?: string | React.ReactNode | null, rightValue?: string | number | React.ReactNode | undefined, options?: { - labelWidth?: string - rowHeight?: string - isFirst?: boolean - isLast?: boolean + labelWidth?: string; + rowHeight?: string; + isFirst?: boolean; + isLast?: boolean; } ) => { // 辅助函数:渲染标签 const renderLabel = (label: string | React.ReactNode | null) => { - if (label === null) return null + if (label === null) return null; if (typeof label === 'string') { return ( {label} - ) + ); } - return label - } + return label; + }; // 辅助函数:渲染值 const renderValue = (value: string | number | React.ReactNode | undefined) => { @@ -115,17 +115,17 @@ export default function LogDetailModal({ > {value} - ) + ); } - return value - } + return value; + }; // 判断是否显示左侧或右侧列 - const showLeftSection = leftLabel !== null && leftLabel !== undefined - const showRightSection = rightLabel !== null && rightLabel !== undefined + const showLeftSection = leftLabel !== null && leftLabel !== undefined; + const showRightSection = rightLabel !== null && rightLabel !== undefined; // 根据显示的列数设置模板 - const gridTemplateColumns = showLeftSection && showRightSection ? '1fr 1fr' : '1fr' + const gridTemplateColumns = showLeftSection && showRightSection ? '1fr 1fr' : '1fr'; return ( )} - ) - } + ); + }; const renderJsonContent = ( label: string, content: string | undefined, options?: { - labelWidth?: string - contentHeight?: string - isFirst?: boolean - isLast?: boolean + labelWidth?: string; + contentHeight?: string; + isFirst?: boolean; + isLast?: boolean; } ) => { - if (!content) return null + if (!content) return null; const handleCopy = (copy: OnCopyProps) => { - if (typeof window === 'undefined') return + if (typeof window === 'undefined') return; const copyText = - typeof copy.src === 'object' ? JSON.stringify(copy.src, null, 2) : String(copy.src) + typeof copy.src === 'object' ? JSON.stringify(copy.src, null, 2) : String(copy.src); - navigator.clipboard.writeText(copyText) - } + navigator.clipboard.writeText(copyText); + }; - let parsed = null + let parsed = null; try { - parsed = JSON.parse(content) + parsed = JSON.parse(content); } catch { - parsed = content + parsed = content; } return ( - ) - } + ); + }; return isDetailLoading ? ( @@ -522,7 +522,7 @@ export default function LogDetailModal({ isClosable: true, duration: 2000, position: 'top', - }) + }); }, (err) => { message({ @@ -531,9 +531,9 @@ export default function LogDetailModal({ description: err?.message || t('copyFailed'), isClosable: true, position: 'top', - }) + }); } - ) + ); }} > {rowData.content} @@ -771,5 +771,5 @@ export default function LogDetailModal({ - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/log/page.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/log/page.tsx index 195ef1e72..6a72fbfc7 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/log/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/log/page.tsx @@ -1,6 +1,6 @@ -'use client' +'use client'; -import { useMemo, useState } from 'react' +import { useMemo, useState } from 'react'; import { Box, Button, @@ -11,51 +11,51 @@ import { InputRightElement, Text, useDisclosure, -} from '@chakra-ui/react' -import { CurrencySymbol, MySelect, MyTooltip } from '@sealos/ui' -import { useQuery } from '@tanstack/react-query' -import { createColumnHelper, getCoreRowModel, useReactTable } from '@tanstack/react-table' -import dynamic from 'next/dynamic' +} from '@chakra-ui/react'; +import { CurrencySymbol, MySelect, MyTooltip } from '@sealos/ui'; +import { useQuery } from '@tanstack/react-query'; +import { createColumnHelper, getCoreRowModel, useReactTable } from '@tanstack/react-table'; +import dynamic from 'next/dynamic'; -import { getEnabledMode, getTokens, getUserLogs } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import SelectDateRange from '@/components/common/SelectDateRange' -import { SingleSelectComboboxUnstyle } from '@/components/common/SingleSelectComboboxUnStyle' -import SwitchPage from '@/components/common/SwitchPage' -import { BaseTable } from '@/components/table/BaseTable' -import { useDebounce } from '@/hooks/useDebounce' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { QueryKey } from '@/types/query-key' -import { LogItem } from '@/types/user/logs' +import { getEnabledMode, getTokens, getUserLogs } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import SelectDateRange from '@/components/common/SelectDateRange'; +import { SingleSelectComboboxUnstyle } from '@/components/common/SingleSelectComboboxUnStyle'; +import SwitchPage from '@/components/common/SwitchPage'; +import { BaseTable } from '@/components/table/BaseTable'; +import { useDebounce } from '@/hooks/useDebounce'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { QueryKey } from '@/types/query-key'; +import { LogItem } from '@/types/user/logs'; -import { getTimeDiff } from './tools/handleTime' +import { getTimeDiff } from './tools/handleTime'; const LogDetailModal = dynamic( () => import('./components/LogDetailModal'), { ssr: false } // 禁用服务端渲染 -) +); export default function Logs(): React.JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { currencySymbol } = useBackendStore() - const { isOpen, onOpen, onClose } = useDisclosure() - const [selectedRow, setSelectedRow] = useState(null) + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { currencySymbol } = useBackendStore(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const [selectedRow, setSelectedRow] = useState(null); const [startTime, setStartTime] = useState(() => { - const currentDate = new Date() - currentDate.setDate(currentDate.getDate() - 3) - return currentDate - }) - const [endTime, setEndTime] = useState(new Date()) - const [keyName, setKeyName] = useState('') - const [codeType, setCodeType] = useState('all') - const [modelName, setModelName] = useState('') - const [page, setPage] = useState(1) - const [pageSize, setPageSize] = useState(10) - const [inputKeyword, setInputKeyword] = useState('') - const debouncedKeyword = useDebounce(inputKeyword, 500) // 500ms 延迟 0.5s + const currentDate = new Date(); + currentDate.setDate(currentDate.getDate() - 3); + return currentDate; + }); + const [endTime, setEndTime] = useState(new Date()); + const [keyName, setKeyName] = useState(''); + const [codeType, setCodeType] = useState('all'); + const [modelName, setModelName] = useState(''); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [inputKeyword, setInputKeyword] = useState(''); + const debouncedKeyword = useDebounce(inputKeyword, 500); // 500ms 延迟 0.5s const { data: logData, isLoading } = useQuery( [ @@ -80,9 +80,9 @@ export default function Logs(): React.JSX.Element { start_timestamp: startTime.getTime().toString(), end_timestamp: endTime.getTime().toString(), }) - ) + ); - const columnHelper = createColumnHelper() + const columnHelper = createColumnHelper(); const columns = useMemo( () => [ @@ -189,7 +189,7 @@ export default function Logs(): React.JSX.Element { ), cell: ({ getValue, row }) => { - const code = getValue() + const code = getValue(); return ( )} - ) + ); }, id: 'status', }), @@ -256,7 +256,7 @@ export default function Logs(): React.JSX.Element { - ) + ); }, id: 'used_amount', }), @@ -277,8 +277,8 @@ export default function Logs(): React.JSX.Element { cell: ({ row }) => ( - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/McpReadme.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/McpReadme.tsx index 7a948521f..1c4775a3d 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/McpReadme.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/McpReadme.tsx @@ -1,21 +1,21 @@ -'use client' -import Markdown from 'react-markdown' -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' -import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism' -import { Box, Text } from '@chakra-ui/react' -import remarkGfm from 'remark-gfm' +'use client'; +import Markdown from 'react-markdown'; +import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; +import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'; +import { Box, Text } from '@chakra-ui/react'; +import remarkGfm from 'remark-gfm'; -import { useI18n } from '@/providers/i18n/i18nContext' -import { McpDetail } from '@/types/mcp' +import { useI18n } from '@/providers/i18n/i18nContext'; +import { McpDetail } from '@/types/mcp'; export interface McpReadmeProps { - mcpDetail: McpDetail + mcpDetail: McpDetail; } export default function McpReadme({ mcpDetail }: McpReadmeProps) { - const { lng } = useI18n() + const { lng } = useI18n(); - const readmeContent = lng === 'zh' ? mcpDetail.readme_cn || mcpDetail.readme : mcpDetail.readme + const readmeContent = lng === 'zh' ? mcpDetail.readme_cn || mcpDetail.readme : mcpDetail.readme; return ( @@ -276,8 +276,8 @@ export default function McpReadme({ mcpDetail }: McpReadmeProps) { remarkPlugins={[remarkGfm]} components={{ code(props) { - const { children, className, node, inline, ...rest } = props - const match = /language-(\w+)/.exec(className || '') + const { children, className, node, inline, ...rest } = props; + const match = /language-(\w+)/.exec(className || ''); return !inline && match ? ( {children} - ) + ); }, }} > @@ -369,5 +369,5 @@ export default function McpReadme({ mcpDetail }: McpReadmeProps) { )} - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/SearchFilter.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/SearchFilter.tsx index 1cc340997..a9e8622e4 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/SearchFilter.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/components/SearchFilter.tsx @@ -1,15 +1,15 @@ -'use client' -import { useEffect, useState } from 'react' -import { Button, Flex, HStack, Input, InputGroup, InputLeftElement, Text } from '@chakra-ui/react' +'use client'; +import { useEffect, useState } from 'react'; +import { Button, Flex, HStack, Input, InputGroup, InputLeftElement, Text } from '@chakra-ui/react'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; export interface SearchFilterProps { - searchTerm: string - serviceType: 'hosted' | 'local' | '' - onSearchChange: (value: string) => void - onServiceTypeChange: (value: string) => void + searchTerm: string; + serviceType: 'hosted' | 'local' | ''; + onSearchChange: (value: string) => void; + onServiceTypeChange: (value: string) => void; } export default function SearchFilter({ @@ -18,58 +18,58 @@ export default function SearchFilter({ onSearchChange, onServiceTypeChange, }: SearchFilterProps) { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); // 本地输入状态 - const [inputValue, setInputValue] = useState(searchTerm) + const [inputValue, setInputValue] = useState(searchTerm); // 本地多选状态管理 - 完全独立的视觉状态 const [selectedTypes, setSelectedTypes] = useState>(() => { - return new Set<'hosted' | 'local'>() // 默认都不选 - }) + return new Set<'hosted' | 'local'>(); // 默认都不选 + }); // 当外部searchTerm变化时,同步到本地状态 useEffect(() => { - setInputValue(searchTerm) - }, [searchTerm]) + setInputValue(searchTerm); + }, [searchTerm]); const handleKeyPress = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { - onSearchChange(inputValue) + onSearchChange(inputValue); } - } + }; const handleInputBlur = () => { // 当失去焦点时也触发搜索,提供更好的用户体验 if (inputValue !== searchTerm) { - onSearchChange(inputValue) + onSearchChange(inputValue); } - } + }; const handleServiceTypeToggle = (type: 'hosted' | 'local') => { - const newSelectedTypes = new Set(selectedTypes) + const newSelectedTypes = new Set(selectedTypes); if (newSelectedTypes.has(type)) { // 取消选择该类型 - newSelectedTypes.delete(type) + newSelectedTypes.delete(type); } else { // 选择该类型 - newSelectedTypes.add(type) + newSelectedTypes.add(type); } - setSelectedTypes(newSelectedTypes) + setSelectedTypes(newSelectedTypes); // 根据选择状态决定传递给父组件的值 if (newSelectedTypes.size === 0 || newSelectedTypes.size === 2) { // 没有选择任何类型或选择了所有类型,都表示显示全部 - onServiceTypeChange('') + onServiceTypeChange(''); } else if (newSelectedTypes.has('hosted')) { - onServiceTypeChange('hosted') + onServiceTypeChange('hosted'); } else if (newSelectedTypes.has('local')) { - onServiceTypeChange('local') + onServiceTypeChange('local'); } - } + }; return ( @@ -212,5 +212,5 @@ export default function SearchFilter({ - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/page.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/page.tsx index ae1fc6e95..948dfbd2c 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/mcp-hub/page.tsx @@ -1,25 +1,25 @@ -'use client' -import { useState } from 'react' -import { Box, Flex, Text } from '@chakra-ui/react' -import { useQuery } from '@tanstack/react-query' +'use client'; +import { useState } from 'react'; +import { Box, Flex, Text } from '@chakra-ui/react'; +import { useQuery } from '@tanstack/react-query'; -import { getMcpList } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import SwitchPage from '@/components/common/SwitchPage' -import { useI18n } from '@/providers/i18n/i18nContext' -import { QueryKey } from '@/types/query-key' +import { getMcpList } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import SwitchPage from '@/components/common/SwitchPage'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { QueryKey } from '@/types/query-key'; -import McpList from './components/McpList' -import SearchFilter from './components/SearchFilter' +import McpList from './components/McpList'; +import SearchFilter from './components/SearchFilter'; export default function McpHubPage() { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); - const [searchTerm, setSearchTerm] = useState('') - const [serviceType, setServiceType] = useState<'hosted' | 'local' | ''>('') - const [page, setPage] = useState(1) - const [pageSize] = useState(28) // 固定每页28个 + const [searchTerm, setSearchTerm] = useState(''); + const [serviceType, setServiceType] = useState<'hosted' | 'local' | ''>(''); + const [page, setPage] = useState(1); + const [pageSize] = useState(28); // 固定每页28个 const { data: mcpListData, isLoading } = useQuery({ queryKey: [ @@ -33,11 +33,11 @@ export default function McpHubPage() { type: serviceType, keyword: searchTerm, }), - }) + }); - const mcps = mcpListData?.mcps || [] - const total = mcpListData?.total || 0 - const totalPages = Math.ceil(total / pageSize) + const mcps = mcpListData?.mcps || []; + const total = mcpListData?.total || 0; + const totalPages = Math.ceil(total / pageSize); return ( @@ -62,12 +62,12 @@ export default function McpHubPage() { searchTerm={searchTerm} serviceType={serviceType} onSearchChange={(value) => { - setSearchTerm(value) - setPage(1) // 搜索时重置到第一页 + setSearchTerm(value); + setPage(1); // 搜索时重置到第一页 }} onServiceTypeChange={(type) => { - setServiceType(type as 'hosted' | 'local' | '') - setPage(1) // 筛选类型时重置到第一页 + setServiceType(type as 'hosted' | 'local' | ''); + setPage(1); // 筛选类型时重置到第一页 }} /> @@ -105,5 +105,5 @@ export default function McpHubPage() { - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/price/component/ApiDoc.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/price/component/ApiDoc.tsx index 04bb4e8b0..7ad4741dc 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/price/component/ApiDoc.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/price/component/ApiDoc.tsx @@ -1,5 +1,5 @@ -'use client' -import React from 'react' +'use client'; +import React from 'react'; import { Badge, Box, @@ -11,38 +11,38 @@ import { DrawerOverlay, Flex, Text, -} from '@chakra-ui/react' -import { useMessage } from '@sealos/ui' +} from '@chakra-ui/react'; +import { useMessage } from '@sealos/ui'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { ModelConfig } from '@/types/models/model' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { ModelConfig } from '@/types/models/model'; -import CodeBlock from './CodeHight' -import { ModelComponent } from './Model' +import CodeBlock from './CodeHight'; +import { ModelComponent } from './Model'; interface ApiDocContent { - title: string - endpoint: string - method: string - requestExample: string - responseExample: string - responseFormat: string + title: string; + endpoint: string; + method: string; + requestExample: string; + responseExample: string; + responseFormat: string; requestAdditionalInfo?: { - voices?: string[] - formats?: string[] - } + voices?: string[]; + formats?: string[]; + }; responseAdditionalInfo?: { - voices?: string[] - formats?: string[] - } + voices?: string[]; + formats?: string[]; + }; } interface ApiDocDrawerProps { - isOpen: boolean - onClose: () => void - modelConfig: ModelConfig + isOpen: boolean; + onClose: () => void; + modelConfig: ModelConfig; } const getApiDocContent = ( @@ -93,7 +93,7 @@ const getApiDocContent = ( "total_tokens": 70 } }`, - } + }; case 3: return { title: t('modeType.3'), @@ -129,7 +129,7 @@ const getApiDocContent = ( "total_tokens": 4 } }`, - } + }; case 5: return { title: t('modeType.5'), @@ -162,7 +162,7 @@ const getApiDocContent = ( } } }`, - } + }; case 7: return { title: t('modeType.7'), @@ -192,7 +192,7 @@ ${ voices: modelConfig?.config?.support_voices, formats: modelConfig?.config?.support_formats, }, - } + }; case 8: return { title: t('modeType.8'), @@ -208,7 +208,7 @@ ${ responseExample: `{ "text": "" }`, - } + }; case 10: return { title: t('modeType.10'), @@ -258,7 +258,7 @@ ${ } } }`, - } + }; case 11: return { title: t('modeType.11'), @@ -275,7 +275,7 @@ ${ "pages": 1, "markdown": "sf ad fda daf da \\\\( f \\\\) ds f sd fs d afdas fsd asfad f\\n\\n\\n\\n![img](data:image/jpeg;base64,/9...)\\n\\n| sadsa | | |\\n| --- | --- | --- |\\n| | sadasdsa | sad |\\n| | | dsadsadsa |\\n| | | |\\n\\n\\n\\na fda" }`, - } + }; default: return { title: t('modeType.0'), @@ -284,16 +284,16 @@ ${ responseFormat: 'json', requestExample: 'unknown', responseExample: 'unknown', - } + }; } -} +}; const ApiDocDrawer: React.FC = ({ isOpen, onClose, modelConfig }) => { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const aiproxyBackend = useBackendStore((state) => state.aiproxyBackend) - const { docUrl } = useBackendStore() - const apiDoc = getApiDocContent(modelConfig, aiproxyBackend, t) + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const aiproxyBackend = useBackendStore((state) => state.aiproxyBackend); + const { docUrl } = useBackendStore(); + const apiDoc = getApiDocContent(modelConfig, aiproxyBackend, t); const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -302,7 +302,7 @@ const ApiDocDrawer: React.FC = ({ isOpen, onClose, modelConfi successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); return ( @@ -363,7 +363,7 @@ const ApiDocDrawer: React.FC = ({ isOpen, onClose, modelConfi - ) + ); }, id: 'detail', }), - ] + ]; const tableData = useMemo(() => { - return modelConfigs - }, [modelConfigs]) + return modelConfigs; + }, [modelConfigs]); const table = useReactTable({ data: tableData, columns, getCoreRowModel: getCoreRowModel(), - }) + }); return ( <> @@ -514,5 +514,5 @@ export function PriceTable({ )} - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/(user)/price/page.tsx b/frontend/providers/aiproxy/app/[lng]/(user)/price/page.tsx index 165d7f049..570f40a04 100644 --- a/frontend/providers/aiproxy/app/[lng]/(user)/price/page.tsx +++ b/frontend/providers/aiproxy/app/[lng]/(user)/price/page.tsx @@ -1,5 +1,5 @@ -'use client' -import { useMemo, useState } from 'react' +'use client'; +import { useMemo, useState } from 'react'; import { Box, Button, @@ -9,36 +9,36 @@ import { InputGroup, InputRightElement, Text, -} from '@chakra-ui/react' -import { useQuery, UseQueryResult } from '@tanstack/react-query' -import Image from 'next/image' +} from '@chakra-ui/react'; +import { useQuery, UseQueryResult } from '@tanstack/react-query'; +import Image from 'next/image'; -import { getEnabledMode } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { SingleSelectComboboxUnstyle } from '@/components/common/SingleSelectComboboxUnStyle' -import { useDebounce } from '@/hooks/useDebounce' -import { useI18n } from '@/providers/i18n/i18nContext' -import { ModelConfig } from '@/types/models/model' -import { QueryKey } from '@/types/query-key' -import { getTranslationWithFallback } from '@/utils/common' +import { getEnabledMode } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { SingleSelectComboboxUnstyle } from '@/components/common/SingleSelectComboboxUnStyle'; +import { useDebounce } from '@/hooks/useDebounce'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { ModelConfig } from '@/types/models/model'; +import { QueryKey } from '@/types/query-key'; +import { getTranslationWithFallback } from '@/utils/common'; -import { getModelIcon } from './component/Model' -import { PriceTable } from './component/PriceTable' +import { getModelIcon } from './component/Model'; +import { PriceTable } from './component/PriceTable'; export default function Price() { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); - const [modelOwner, setModelOwner] = useState('') - const [modelType, setModelType] = useState('') + const [modelOwner, setModelOwner] = useState(''); + const [modelType, setModelType] = useState(''); - const [searchInput, setSearchInput] = useState('') - const debouncedSearch = useDebounce(searchInput, 500) + const [searchInput, setSearchInput] = useState(''); + const debouncedSearch = useDebounce(searchInput, 500); interface FilterParams { - owner: string - type: string - name: string + owner: string; + type: string; + name: string; } const filterModels = (modelConfigs: ModelConfig[], filterParams: FilterParams): ModelConfig[] => { @@ -50,34 +50,34 @@ export default function Price() { `modeOwner.${String(config.owner)}`, 'modeOwner.unknown', t as any - ) === filterParams.owner + ) === filterParams.owner; const typeMatch = !filterParams.type || filterParams.type === t('price.all') || getTranslationWithFallback(`modeType.${String(config.type)}`, 'modeType.0', t as any) === - filterParams.type + filterParams.type; const nameMatch = - !filterParams.name || config.model.toLowerCase().includes(filterParams.name.toLowerCase()) + !filterParams.name || config.model.toLowerCase().includes(filterParams.name.toLowerCase()); - return ownerMatch && typeMatch && nameMatch - }) - } + return ownerMatch && typeMatch && nameMatch; + }); + }; const { isLoading, data: modelConfigs = [] as ModelConfig[], refetch, - }: UseQueryResult = useQuery([QueryKey.GetEnabledModels], () => getEnabledMode()) + }: UseQueryResult = useQuery([QueryKey.GetEnabledModels], () => getEnabledMode()); const filteredModelConfigs = useMemo(() => { return filterModels(modelConfigs, { owner: modelOwner, type: modelType, name: debouncedSearch, - }) - }, [modelConfigs, modelOwner, modelType, debouncedSearch]) + }); + }, [modelConfigs, modelOwner, modelType, debouncedSearch]); return ( @@ -129,7 +129,7 @@ export default function Price() { bg="white" boxShadow="0px 1px 2px 0px rgba(19, 51, 107, 0.05), 0px 0px 1px 0px rgba(19, 51, 107, 0.08)" onClick={() => { - refetch() + refetch(); }} > { - setModelOwner(modelOwner.name) + setModelOwner(modelOwner.name); }} handleDropdownItemFilter={(dropdownItems, inputValue) => { - const lowerCasedInput = inputValue.toLowerCase() + const lowerCasedInput = inputValue.toLowerCase(); return dropdownItems.filter( (item) => !inputValue || item.name.toLowerCase().includes(lowerCasedInput) - ) + ); }} handleDropdownItemDisplay={(dropdownItem) => { - const iconSrc = getModelIcon(dropdownItem.icon) + const iconSrc = getModelIcon(dropdownItem.icon); if (dropdownItem.name === t('price.all')) { return ( @@ -215,7 +215,7 @@ export default function Price() { {dropdownItem.name} - ) + ); } return ( @@ -232,7 +232,7 @@ export default function Price() { {dropdownItem.name} - ) + ); }} flexProps={{ w: '240px' }} initSelectedItem={{ icon: '', name: t('price.all') }} @@ -266,13 +266,13 @@ export default function Price() { ), ]} setSelectedItem={(modelType) => { - setModelType(modelType) + setModelType(modelType); }} handleDropdownItemFilter={(dropdownItems, inputValue) => { - const lowerCasedInput = inputValue.toLowerCase() + const lowerCasedInput = inputValue.toLowerCase(); return dropdownItems.filter( (item) => !inputValue || item.toLowerCase().includes(lowerCasedInput) - ) + ); }} handleDropdownItemDisplay={(dropdownItem) => { return ( @@ -287,7 +287,7 @@ export default function Price() { > {dropdownItem} - ) + ); }} flexProps={{ w: '240px' }} initSelectedItem={t('price.all')} @@ -320,7 +320,7 @@ export default function Price() { }} value={searchInput} onChange={(e) => { - setSearchInput(e.target.value) + setSearchInput(e.target.value); }} /> @@ -364,5 +364,5 @@ export default function Price() { - ) + ); } diff --git a/frontend/providers/aiproxy/app/[lng]/layout.tsx b/frontend/providers/aiproxy/app/[lng]/layout.tsx index 35fc9be95..8eb8307f8 100644 --- a/frontend/providers/aiproxy/app/[lng]/layout.tsx +++ b/frontend/providers/aiproxy/app/[lng]/layout.tsx @@ -1,52 +1,52 @@ -import { dir } from 'i18next' -import type { Metadata } from 'next' -import Script from 'next/script' +import { dir } from 'i18next'; +import type { Metadata } from 'next'; +import Script from 'next/script'; -import { useTranslationServerSide } from '@/app/i18n/server' -import { fallbackLng, languages } from '@/app/i18n/settings' -import InitializeApp from '@/components/InitializeApp' -import ChakraProviders from '@/providers/chakra/providers' -import { I18nProvider } from '@/providers/i18n/i18nContext' -import QueryProvider from '@/providers/tanstack-query/QueryProvider' +import { useTranslationServerSide } from '@/app/i18n/server'; +import { fallbackLng, languages } from '@/app/i18n/settings'; +import InitializeApp from '@/components/InitializeApp'; +import ChakraProviders from '@/providers/chakra/providers'; +import { I18nProvider } from '@/providers/i18n/i18nContext'; +import QueryProvider from '@/providers/tanstack-query/QueryProvider'; -import './globals.css' -import 'react-day-picker/dist/style.css' +import './globals.css'; +import 'react-day-picker/dist/style.css'; export async function generateStaticParams(): Promise<{ lng: string }[]> { - return languages.map((lng) => ({ lng })) + return languages.map((lng) => ({ lng })); } export async function generateMetadata({ params, }: { params: { - lng: string - } + lng: string; + }; }): Promise { - let { lng } = await params - if (languages.indexOf(lng) < 0) lng = fallbackLng + let { lng } = await params; + if (languages.indexOf(lng) < 0) lng = fallbackLng; // eslint-disable-next-line react-hooks/rules-of-hooks - const { t } = await useTranslationServerSide(lng, 'common') + const { t } = await useTranslationServerSide(lng, 'common'); return { icons: { icon: '/favicon.svg', }, title: t('title'), description: t('description'), - } + }; } export default async function RootLayout({ children, params, }: Readonly<{ - children: React.ReactNode - params: Promise<{ lng: string }> + children: React.ReactNode; + params: Promise<{ lng: string }>; }>): Promise { - const lng = (await params).lng + const lng = (await params).lng; const scripts: { [key: string]: string }[] = JSON.parse( process.env.NEXT_PUBLIC_CUSTOM_SCRIPTS ?? '[]' - ) + ); return ( @@ -63,5 +63,5 @@ export default async function RootLayout({ ))} - ) + ); } diff --git a/frontend/providers/aiproxy/app/api-docs/layout.tsx b/frontend/providers/aiproxy/app/api-docs/layout.tsx index 353c47080..de3e6a95a 100644 --- a/frontend/providers/aiproxy/app/api-docs/layout.tsx +++ b/frontend/providers/aiproxy/app/api-docs/layout.tsx @@ -1,4 +1,4 @@ -import { Metadata } from 'next' +import { Metadata } from 'next'; export const metadata: Metadata = { title: 'AI Proxy API Doc', @@ -7,12 +7,12 @@ export const metadata: Metadata = { index: process.env.NODE_ENV === 'development', follow: false, }, -} +}; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} - ) + ); } diff --git a/frontend/providers/aiproxy/app/api-docs/page.tsx b/frontend/providers/aiproxy/app/api-docs/page.tsx index 41bb34746..2edf8d98f 100644 --- a/frontend/providers/aiproxy/app/api-docs/page.tsx +++ b/frontend/providers/aiproxy/app/api-docs/page.tsx @@ -1,14 +1,14 @@ -import ReactSwagger from '@/components/SwaggerUi' -import { getApiDocs } from '@/lib/swagger' +import ReactSwagger from '@/components/SwaggerUi'; +import { getApiDocs } from '@/lib/swagger'; async function ApiDocsPage() { - const spec = await getApiDocs() + const spec = await getApiDocs(); return (
- ) + ); } -export default ApiDocsPage +export default ApiDocsPage; diff --git a/frontend/providers/aiproxy/components/InitializeApp.tsx b/frontend/providers/aiproxy/components/InitializeApp.tsx index 529abbbcd..3cee4acf0 100644 --- a/frontend/providers/aiproxy/components/InitializeApp.tsx +++ b/frontend/providers/aiproxy/components/InitializeApp.tsx @@ -1,135 +1,135 @@ -'use client' +'use client'; -import { useCallback, useEffect } from 'react' -import { usePathname } from 'next/navigation' -import { useRouter } from 'next/navigation' -import { EVENT_NAME } from 'sealos-desktop-sdk' -import { createSealosApp, sealosApp } from 'sealos-desktop-sdk/app' +import { useCallback, useEffect } from 'react'; +import { usePathname } from 'next/navigation'; +import { useRouter } from 'next/navigation'; +import { EVENT_NAME } from 'sealos-desktop-sdk'; +import { createSealosApp, sealosApp } from 'sealos-desktop-sdk/app'; -import { initAppConfig } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { useSessionStore } from '@/store/session' +import { initAppConfig } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { useSessionStore } from '@/store/session'; export default function InitializeApp() { - const router = useRouter() - const pathname = usePathname() - const { lng } = useI18n() - const { i18n } = useTranslationClientSide(lng) + const router = useRouter(); + const pathname = usePathname(); + const { lng } = useI18n(); + const { i18n } = useTranslationClientSide(lng); const { setAiproxyBackend, setCurrencySymbol, setDocUrl, setIsInvitationActive, setInvitationUrl, - } = useBackendStore() + } = useBackendStore(); const handleI18nChange = useCallback( (data: { currentLanguage: string }) => { - const currentLng = i18n.resolvedLanguage // get the latest resolvedLanguage - const newLng = data.currentLanguage + const currentLng = i18n.resolvedLanguage; // get the latest resolvedLanguage + const newLng = data.currentLanguage; if (currentLng !== newLng) { - const currentPath = window.location.pathname - const pathWithoutLang = currentPath.split('/').slice(2).join('/') - router.push(`/${newLng}/${pathWithoutLang}`) + const currentPath = window.location.pathname; + const pathWithoutLang = currentPath.split('/').slice(2).join('/'); + router.push(`/${newLng}/${pathWithoutLang}`); } }, [i18n.resolvedLanguage] - ) + ); useEffect(() => { - const cleanupApp = createSealosApp() - let cleanupEventListener: (() => void) | undefined + const cleanupApp = createSealosApp(); + let cleanupEventListener: (() => void) | undefined; const initApp = async () => { try { - await initLanguage() + await initLanguage(); - await initSession() + await initSession(); - await initConfig() + await initConfig(); cleanupEventListener = sealosApp?.addAppEventListen( EVENT_NAME.CHANGE_I18N, handleI18nChange - ) + ); } catch (error) { - console.error('aiproxy: init app error:', error) + console.error('aiproxy: init app error:', error); } - } + }; - initApp() + initApp(); return () => { if (cleanupEventListener && typeof cleanupEventListener === 'function') { - cleanupEventListener() + cleanupEventListener(); } if (cleanupApp && typeof cleanupApp === 'function') { - cleanupApp() + cleanupApp(); } - } + }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + }, []); // init language const initLanguage = async () => { - const pathLng = pathname.split('/')[1] + const pathLng = pathname.split('/')[1]; try { - const lang = await sealosApp.getLanguage() + const lang = await sealosApp.getLanguage(); if (pathLng !== lang.lng) { - const pathParts = pathname.split('/') - pathParts[1] = lang.lng - router.push(pathParts.join('/')) - router.refresh() + const pathParts = pathname.split('/'); + pathParts[1] = lang.lng; + router.push(pathParts.join('/')); + router.refresh(); } - console.info('aiproxy: init language success') + console.info('aiproxy: init language success'); } catch (error) { if (error instanceof Error) { - console.debug('aiproxy: init language error:', error.message) + console.debug('aiproxy: init language error:', error.message); } else { - console.debug('aiproxy: unknown init language error:', error) + console.debug('aiproxy: unknown init language error:', error); } } - } + }; // init session const initSession = async () => { - const { setSession } = useSessionStore.getState() + const { setSession } = useSessionStore.getState(); try { - const newSession = await sealosApp.getSession() - const currentSession = useSessionStore.getState().session + const newSession = await sealosApp.getSession(); + const currentSession = useSessionStore.getState().session; // Compare token from persisted session with new session token if (newSession?.token !== currentSession?.token) { - setSession(newSession) - window.location.reload() + setSession(newSession); + window.location.reload(); } - console.info('aiproxy: init session success') + console.info('aiproxy: init session success'); } catch (err) { - console.info('aiproxy: app is not running in desktop') + console.info('aiproxy: app is not running in desktop'); if (!process.env.NEXT_PUBLIC_MOCK_USER) { - setSession(null) + setSession(null); } } - } + }; // init config const initConfig = async () => { try { const { aiproxyBackend, currencySymbol, docUrl, isInvitationActive, invitationUrl } = - await initAppConfig() - setAiproxyBackend(aiproxyBackend) - setCurrencySymbol(currencySymbol) - setDocUrl(docUrl) - setIsInvitationActive(isInvitationActive) - setInvitationUrl(invitationUrl) - console.info('aiproxy: init config success') + await initAppConfig(); + setAiproxyBackend(aiproxyBackend); + setCurrencySymbol(currencySymbol); + setDocUrl(docUrl); + setIsInvitationActive(isInvitationActive); + setInvitationUrl(invitationUrl); + console.info('aiproxy: init config success'); } catch (error) { - console.error('aiproxy: init config error:', error) + console.error('aiproxy: init config error:', error); } - } + }; - return null + return null; } diff --git a/frontend/providers/aiproxy/components/SwaggerUi.tsx b/frontend/providers/aiproxy/components/SwaggerUi.tsx index d4ed6fa8d..ab53a8273 100644 --- a/frontend/providers/aiproxy/components/SwaggerUi.tsx +++ b/frontend/providers/aiproxy/components/SwaggerUi.tsx @@ -1,12 +1,12 @@ -'use client' +'use client'; -import { memo } from 'react' -import SwaggerUI from 'swagger-ui-react' +import { memo } from 'react'; +import SwaggerUI from 'swagger-ui-react'; -import 'swagger-ui-react/swagger-ui.css' +import 'swagger-ui-react/swagger-ui.css'; interface ReactSwaggerProps { - spec: object | string + spec: object | string; } const ReactSwagger = memo(({ spec }: ReactSwaggerProps) => { @@ -50,9 +50,9 @@ const ReactSwagger = memo(({ spec }: ReactSwaggerProps) => { }} /> - ) -}) + ); +}); -ReactSwagger.displayName = 'ReactSwagger' +ReactSwagger.displayName = 'ReactSwagger'; -export default ReactSwagger +export default ReactSwagger; diff --git a/frontend/providers/aiproxy/components/admin/Sidebar.tsx b/frontend/providers/aiproxy/components/admin/Sidebar.tsx index 901423479..13003dc30 100644 --- a/frontend/providers/aiproxy/components/admin/Sidebar.tsx +++ b/frontend/providers/aiproxy/components/admin/Sidebar.tsx @@ -1,33 +1,33 @@ -'use client' -import { Flex, Text } from '@chakra-ui/react' -import Image, { StaticImageData } from 'next/image' -import Link from 'next/link' -import { usePathname } from 'next/navigation' +'use client'; +import { Flex, Text } from '@chakra-ui/react'; +import Image, { StaticImageData } from 'next/image'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import configIcon from '@/ui/svg/icons/admin-sidebar/config.svg' -import configIcon_a from '@/ui/svg/icons/admin-sidebar/config_a.svg' -import homeIcon from '@/ui/svg/icons/admin-sidebar/home.svg' -import homeIcon_a from '@/ui/svg/icons/admin-sidebar/home_a.svg' -import logsIcon from '@/ui/svg/icons/admin-sidebar/logs.svg' -import logsIcon_a from '@/ui/svg/icons/admin-sidebar/logs_a.svg' -import nsManagerIcon from '@/ui/svg/icons/admin-sidebar/nsManager.svg' -import nsManagerIcon_a from '@/ui/svg/icons/admin-sidebar/nsManager_a.svg' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import configIcon from '@/ui/svg/icons/admin-sidebar/config.svg'; +import configIcon_a from '@/ui/svg/icons/admin-sidebar/config_a.svg'; +import homeIcon from '@/ui/svg/icons/admin-sidebar/home.svg'; +import homeIcon_a from '@/ui/svg/icons/admin-sidebar/home_a.svg'; +import logsIcon from '@/ui/svg/icons/admin-sidebar/logs.svg'; +import logsIcon_a from '@/ui/svg/icons/admin-sidebar/logs_a.svg'; +import nsManagerIcon from '@/ui/svg/icons/admin-sidebar/nsManager.svg'; +import nsManagerIcon_a from '@/ui/svg/icons/admin-sidebar/nsManager_a.svg'; type Menu = { - id: string - url: string - value: string - icon: StaticImageData - activeIcon: StaticImageData - display: boolean -} + id: string; + url: string; + value: string; + icon: StaticImageData; + activeIcon: StaticImageData; + display: boolean; +}; const SideBar = (): JSX.Element => { - const pathname = usePathname() - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const pathname = usePathname(); + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); const menus: Menu[] = [ { @@ -62,7 +62,7 @@ const SideBar = (): JSX.Element => { activeIcon: nsManagerIcon_a, display: true, }, - ] + ]; return ( { {menus .filter((menu) => menu.display) .map((menu) => { - const fullUrl = `/${lng}${menu.url}` - const isActive = pathname === fullUrl + const fullUrl = `/${lng}${menu.url}`; + const isActive = pathname === fullUrl; return ( @@ -115,10 +115,10 @@ const SideBar = (): JSX.Element => { - ) + ); })} - ) -} + ); +}; -export default SideBar +export default SideBar; diff --git a/frontend/providers/aiproxy/components/common/ConstructMappingComponent.tsx b/frontend/providers/aiproxy/components/common/ConstructMappingComponent.tsx index 0c5df5b71..6433a7e44 100644 --- a/frontend/providers/aiproxy/components/common/ConstructMappingComponent.tsx +++ b/frontend/providers/aiproxy/components/common/ConstructMappingComponent.tsx @@ -1,11 +1,11 @@ -import React, { useEffect, useMemo, useState } from 'react' -import { Box, Button, Flex, FormLabel, Input, Text, VStack } from '@chakra-ui/react' +import React, { useEffect, useMemo, useState } from 'react'; +import { Box, Button, Flex, FormLabel, Input, Text, VStack } from '@chakra-ui/react'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; -import { CustomSelect } from './Select' -type MapKeyValuePair = { key: string; value: string } +import { CustomSelect } from './Select'; +type MapKeyValuePair = { key: string; value: string }; // mapKeys determines the available selection options export const ConstructMappingComponent = function ({ @@ -13,30 +13,30 @@ export const ConstructMappingComponent = function ({ mapData, setMapData, }: { - mapKeys: string[] - mapData: Record - setMapData: (mapping: Record) => void + mapKeys: string[]; + mapData: Record; + setMapData: (mapping: Record) => void; }) { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); - const [mapKeyValuePairs, setMapkeyValuePairs] = useState>([]) + const [mapKeyValuePairs, setMapkeyValuePairs] = useState>([]); // 组件初步渲染时,isInternalUpdate 为 false,可以保证每次重新渲染时,mapData 变化时,mapKeyValuePairs 重新初始化 - const [isInternalUpdate, setIsInternalUpdate] = useState(false) + const [isInternalUpdate, setIsInternalUpdate] = useState(false); // 初始化并在 mapData 变化时更新 mapKeyValuePairs useEffect(() => { if (!isInternalUpdate) { - const entries = Object.entries(mapData) + const entries = Object.entries(mapData); setMapkeyValuePairs( entries.length > 0 ? entries.map(([key, value]) => ({ key, value })) : [{ key: '', value: '' }] - ) + ); } - setIsInternalUpdate(false) - }, [mapData]) + setIsInternalUpdate(false); + }, [mapData]); const handleDropdownItemDisplay = (dropdownItem: string) => { if (dropdownItem === t('channelsFormPlaceholder.modelMappingInput')) { @@ -52,7 +52,7 @@ export const ConstructMappingComponent = function ({ > {t('channelsFormPlaceholder.modelMappingInput')} - ) + ); } return ( {dropdownItem} - ) - } + ); + }; const handleSeletedItemDisplay = (selectedItem: string) => { if (selectedItem === t('channelsFormPlaceholder.modelMappingInput')) { @@ -83,7 +83,7 @@ export const ConstructMappingComponent = function ({ > {t('channelsFormPlaceholder.modelMappingInput')} - ) + ); } return ( - ) - } + ); + }; // Handling mapData and mapKeyValuePairs cleanup when map keys change. useEffect(() => { // 1. Handle mapData cleanup - const removedKeysFromMapData = Object.keys(mapData).filter((key) => !mapKeys.includes(key)) + const removedKeysFromMapData = Object.keys(mapData).filter((key) => !mapKeys.includes(key)); if (removedKeysFromMapData.length > 0) { - const newMapData = { ...mapData } + const newMapData = { ...mapData }; removedKeysFromMapData.forEach((key) => { - delete newMapData[key] - }) - setIsInternalUpdate(true) - setMapData(newMapData) + delete newMapData[key]; + }); + setIsInternalUpdate(true); + setMapData(newMapData); } // 2. Handle mapKeyValuePairs cleanup - const removedPairs = mapKeyValuePairs.filter((pair) => pair.key && !mapKeys.includes(pair.key)) + const removedPairs = mapKeyValuePairs.filter((pair) => pair.key && !mapKeys.includes(pair.key)); if (removedPairs.length > 0) { const newMapKeyValuePairs = mapKeyValuePairs.filter( (pair) => !pair.key || mapKeys.includes(pair.key) - ) - setMapkeyValuePairs(newMapKeyValuePairs) + ); + setMapkeyValuePairs(newMapKeyValuePairs); } - }, [mapKeys]) + }, [mapKeys]); // Get the keys that have been selected const getSelectedMapKeys = (currentIndex: number) => { - const selected = new Set() + const selected = new Set(); mapKeyValuePairs.forEach((mapKeyValuePair, idx) => { if (idx !== currentIndex && mapKeyValuePair.key) { - selected.add(mapKeyValuePair.key) + selected.add(mapKeyValuePair.key); } - }) - return selected - } + }); + return selected; + }; // Handling adding a new row const handleAddNewMapKeyPair = () => { - setMapkeyValuePairs([...mapKeyValuePairs, { key: '', value: '' }]) - } + setMapkeyValuePairs([...mapKeyValuePairs, { key: '', value: '' }]); + }; // Handling deleting a row const handleRemoveMapKeyPair = (index: number) => { - const mapKeyValuePair = mapKeyValuePairs[index] - const newMapData = { ...mapData } + const mapKeyValuePair = mapKeyValuePairs[index]; + const newMapData = { ...mapData }; if (mapKeyValuePair.key) { - delete newMapData[mapKeyValuePair.key] + delete newMapData[mapKeyValuePair.key]; } - setIsInternalUpdate(true) - setMapData(newMapData) + setIsInternalUpdate(true); + setMapData(newMapData); - const newMapKeyValuePairs = mapKeyValuePairs.filter((_, idx) => idx !== index) - setMapkeyValuePairs(newMapKeyValuePairs) - } + const newMapKeyValuePairs = mapKeyValuePairs.filter((_, idx) => idx !== index); + setMapkeyValuePairs(newMapKeyValuePairs); + }; // Handling selection/input changes const handleInputChange = (index: number, field: 'key' | 'value', value: string) => { - const newMapKeyValuePairs = [...mapKeyValuePairs] - const oldValue = newMapKeyValuePairs[index][field] - newMapKeyValuePairs[index][field] = value + const newMapKeyValuePairs = [...mapKeyValuePairs]; + const oldValue = newMapKeyValuePairs[index][field]; + newMapKeyValuePairs[index][field] = value; // Update the mapping relationship - const newMapData = { ...mapData } + const newMapData = { ...mapData }; if (field === 'key') { - if (oldValue) delete newMapData[oldValue] + if (oldValue) delete newMapData[oldValue]; if (!value) { - newMapKeyValuePairs[index].value = '' + newMapKeyValuePairs[index].value = ''; } if (value && newMapKeyValuePairs[index].value) { - newMapData[value] = newMapKeyValuePairs[index].value + newMapData[value] = newMapKeyValuePairs[index].value; } } else { if (newMapKeyValuePairs[index].key) { - newMapData[newMapKeyValuePairs[index].key] = value + newMapData[newMapKeyValuePairs[index].key] = value; } } - setMapkeyValuePairs(newMapKeyValuePairs) - setIsInternalUpdate(true) - setMapData(newMapData) - } + setMapkeyValuePairs(newMapKeyValuePairs); + setIsInternalUpdate(true); + setMapData(newMapData); + }; // Check if there are still keys that can be selected const hasAvailableKeys = useMemo(() => { const usedKeys = new Set( mapKeyValuePairs.map((mapKeyValuePair) => mapKeyValuePair.key).filter(Boolean) - ) + ); // Ensure mapKeyValuePairs length does not exceed mapKeys length return ( mapKeyValuePairs.length < mapKeys.length && mapKeys.some((mapKey) => !usedKeys.has(mapKey)) - ) - }, [mapKeys, mapKeyValuePairs]) + ); + }, [mapKeys, mapKeyValuePairs]); return ( @@ -339,6 +339,6 @@ export const ConstructMappingComponent = function ({ )} - ) -} -export default ConstructMappingComponent + ); +}; +export default ConstructMappingComponent; diff --git a/frontend/providers/aiproxy/components/common/ConstructModeMappingComponent.tsx b/frontend/providers/aiproxy/components/common/ConstructModeMappingComponent.tsx index 3834aa307..74af9f89b 100644 --- a/frontend/providers/aiproxy/components/common/ConstructModeMappingComponent.tsx +++ b/frontend/providers/aiproxy/components/common/ConstructModeMappingComponent.tsx @@ -1,16 +1,16 @@ -import React, { useEffect, useMemo, useState } from 'react' -import { Badge, Box, Button, Flex, FormLabel, Input, Text, VStack } from '@chakra-ui/react' +import React, { useEffect, useMemo, useState } from 'react'; +import { Badge, Box, Button, Flex, FormLabel, Input, Text, VStack } from '@chakra-ui/react'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; -import { CustomSelect } from './Select' -type MapKeyValuePair = { key: string; value: string } +import { CustomSelect } from './Select'; +type MapKeyValuePair = { key: string; value: string }; type Model = { - name: string - isDefault: boolean -} + name: string; + isDefault: boolean; +}; // mapKeys determines the available selection options export const ConstructModeMappingComponent = function ({ @@ -18,30 +18,30 @@ export const ConstructModeMappingComponent = function ({ mapData, setMapData, }: { - mapKeys: Model[] - mapData: Record - setMapData: (mapping: Record) => void + mapKeys: Model[]; + mapData: Record; + setMapData: (mapping: Record) => void; }) { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); const [mapKeyValuePairs, setMapkeyValuePairs] = useState>([ { key: '', value: '' }, - ]) + ]); - const [isInternalUpdate, setIsInternalUpdate] = useState(false) + const [isInternalUpdate, setIsInternalUpdate] = useState(false); useEffect(() => { if (!isInternalUpdate) { - const entries = Object.entries(mapData) + const entries = Object.entries(mapData); setMapkeyValuePairs( entries.length > 0 ? entries.map(([key, value]) => ({ key, value })) : [{ key: '', value: '' }] - ) + ); } - setIsInternalUpdate(false) - }, [mapData]) + setIsInternalUpdate(false); + }, [mapData]); const handleDropdownItemDisplay = (dropdownItem: Model | string) => { if (dropdownItem === t('channelsFormPlaceholder.modelMappingInput')) { @@ -57,7 +57,7 @@ export const ConstructModeMappingComponent = function ({ > {t('channelsFormPlaceholder.modelMappingInput')} - ) + ); } if ((dropdownItem as Model).isDefault) { @@ -112,7 +112,7 @@ export const ConstructModeMappingComponent = function ({ - ) + ); } return ( {(dropdownItem as Model).name} - ) - } + ); + }; const handleSeletedItemDisplay = (selectedItem: Model | string) => { if (selectedItem === t('channelsFormPlaceholder.modelMappingInput')) { @@ -143,7 +143,7 @@ export const ConstructModeMappingComponent = function ({ > {t('channelsFormPlaceholder.modelMappingInput')} - ) + ); } if ((selectedItem as Model).isDefault) { @@ -215,7 +215,7 @@ export const ConstructModeMappingComponent = function ({ - ) + ); } return ( - ) - } + ); + }; // Handling mapData and mapKeyValuePairs cleanup when map keys change. useEffect(() => { // 1. Handle mapData cleanup const removedKeys = Object.keys(mapData).filter( (key) => !mapKeys.some((model) => model.name === key) - ) + ); if (removedKeys.length > 0) { // If there are mappings with removed keys, delete them - const newMapData = { ...mapData } + const newMapData = { ...mapData }; removedKeys.forEach((key) => { - delete newMapData[key] - }) - setIsInternalUpdate(true) - setMapData(newMapData) + delete newMapData[key]; + }); + setIsInternalUpdate(true); + setMapData(newMapData); } // 2. Handle mapKeyValuePairs cleanup const removedPairs = mapKeyValuePairs.filter( (pair) => pair.key && !mapKeys.some((model) => model.name === pair.key) - ) + ); if (removedPairs.length > 0) { const newMapKeyValuePairs = mapKeyValuePairs.filter( (pair) => !pair.key || mapKeys.some((model) => model.name === pair.key) - ) - setMapkeyValuePairs(newMapKeyValuePairs) + ); + setMapkeyValuePairs(newMapKeyValuePairs); } - }, [mapKeys]) + }, [mapKeys]); // Get the keys that have been selected const getSelectedMapKeys = (currentIndex: number) => { - const selected = new Set() + const selected = new Set(); mapKeyValuePairs.forEach((mapKeyValuePair, idx) => { if (idx !== currentIndex && mapKeyValuePair.key) { - selected.add(mapKeyValuePair.key) + selected.add(mapKeyValuePair.key); } - }) - return selected - } + }); + return selected; + }; // Handling adding a new row const handleAddNewMapKeyPair = () => { - setMapkeyValuePairs([...mapKeyValuePairs, { key: '', value: '' }]) - } + setMapkeyValuePairs([...mapKeyValuePairs, { key: '', value: '' }]); + }; // Handling deleting a row const handleRemoveMapKeyPair = (index: number) => { - const mapKeyValuePair = mapKeyValuePairs[index] - const newMapData = { ...mapData } + const mapKeyValuePair = mapKeyValuePairs[index]; + const newMapData = { ...mapData }; if (mapKeyValuePair.key) { - delete newMapData[mapKeyValuePair.key] + delete newMapData[mapKeyValuePair.key]; } - setIsInternalUpdate(true) - setMapData(newMapData) + setIsInternalUpdate(true); + setMapData(newMapData); - const newMapKeyValuePairs = mapKeyValuePairs.filter((_, idx) => idx !== index) - setMapkeyValuePairs(newMapKeyValuePairs) - } + const newMapKeyValuePairs = mapKeyValuePairs.filter((_, idx) => idx !== index); + setMapkeyValuePairs(newMapKeyValuePairs); + }; // Handling selection/input changes const handleInputChange = (index: number, field: 'key' | 'value', value: string) => { - const newMapKeyValuePairs = [...mapKeyValuePairs] - const oldValue = newMapKeyValuePairs[index][field] - newMapKeyValuePairs[index][field] = value + const newMapKeyValuePairs = [...mapKeyValuePairs]; + const oldValue = newMapKeyValuePairs[index][field]; + newMapKeyValuePairs[index][field] = value; // Update the mapping relationship - const newMapData = { ...mapData } + const newMapData = { ...mapData }; if (field === 'key') { - if (oldValue) delete newMapData[oldValue] + if (oldValue) delete newMapData[oldValue]; if (!value) { - newMapKeyValuePairs[index].value = '' + newMapKeyValuePairs[index].value = ''; } if (value && newMapKeyValuePairs[index].value) { - newMapData[value] = newMapKeyValuePairs[index].value + newMapData[value] = newMapKeyValuePairs[index].value; } } else { if (newMapKeyValuePairs[index].key) { - newMapData[newMapKeyValuePairs[index].key] = value + newMapData[newMapKeyValuePairs[index].key] = value; } } - setMapkeyValuePairs(newMapKeyValuePairs) - setIsInternalUpdate(true) - setMapData(newMapData) - } + setMapkeyValuePairs(newMapKeyValuePairs); + setIsInternalUpdate(true); + setMapData(newMapData); + }; // Check if there are still keys that can be selected const hasAvailableKeys = useMemo(() => { const usedKeys = new Set( mapKeyValuePairs.map((mapKeyValuePair) => mapKeyValuePair.key).filter(Boolean) - ) + ); return ( mapKeyValuePairs.length < mapKeys.length && mapKeys.some((mapKey) => !usedKeys.has(mapKey.name)) - ) - }, [mapKeys, mapKeyValuePairs]) + ); + }, [mapKeys, mapKeyValuePairs]); return ( @@ -370,9 +370,9 @@ export const ConstructModeMappingComponent = function ({ // when select placeholder, the newSelectedItem is null handleSelectedItemChange={(newSelectedItem) => { if (newSelectedItem) { - handleInputChange(index, 'key', newSelectedItem.name) + handleInputChange(index, 'key', newSelectedItem.name); } else { - handleInputChange(index, 'key', '') + handleInputChange(index, 'key', ''); } }} handleDropdownItemDisplay={handleDropdownItemDisplay} @@ -489,6 +489,6 @@ export const ConstructModeMappingComponent = function ({ )} - ) -} -export default ConstructModeMappingComponent + ); +}; +export default ConstructModeMappingComponent; diff --git a/frontend/providers/aiproxy/components/common/EditableTextNoLable.tsx b/frontend/providers/aiproxy/components/common/EditableTextNoLable.tsx index 2a09cd308..802056f87 100644 --- a/frontend/providers/aiproxy/components/common/EditableTextNoLable.tsx +++ b/frontend/providers/aiproxy/components/common/EditableTextNoLable.tsx @@ -1,6 +1,6 @@ -'use client' -import React, { useState } from 'react' -import { CheckIcon, CloseIcon } from '@chakra-ui/icons' +'use client'; +import React, { useState } from 'react'; +import { CheckIcon, CloseIcon } from '@chakra-ui/icons'; import { Box, Button, @@ -14,28 +14,28 @@ import { PopoverTrigger, Text, useDisclosure, -} from '@chakra-ui/react' +} from '@chakra-ui/react'; interface EditableTextProps { - value: string | number - onSubmit: (value: string) => void - flexProps?: FlexProps + value: string | number; + onSubmit: (value: string) => void; + flexProps?: FlexProps; } export const EditableTextNoLable = ({ value, onSubmit, flexProps }: EditableTextProps) => { - const [editValue, setEditValue] = useState(String(value)) - const { isOpen, onOpen, onClose } = useDisclosure() + const [editValue, setEditValue] = useState(String(value)); + const { isOpen, onOpen, onClose } = useDisclosure(); const handleSubmit = () => { - onSubmit(editValue) - onClose() - } + onSubmit(editValue); + onClose(); + }; const handleCancel = () => { // 关闭时 恢复到传递来的初始值 - setEditValue(String(value)) - onClose() - } + setEditValue(String(value)); + onClose(); + }; return ( @@ -173,5 +173,5 @@ export const EditableTextNoLable = ({ value, onSubmit, flexProps }: EditableText - ) -} + ); +}; diff --git a/frontend/providers/aiproxy/components/common/MultiSelectCombobox.tsx b/frontend/providers/aiproxy/components/common/MultiSelectCombobox.tsx index 4b6c911fa..dd5c90b24 100644 --- a/frontend/providers/aiproxy/components/common/MultiSelectCombobox.tsx +++ b/frontend/providers/aiproxy/components/common/MultiSelectCombobox.tsx @@ -1,5 +1,5 @@ -'use client' -import { Dispatch, ReactNode, SetStateAction, useMemo, useState } from 'react' +'use client'; +import { Dispatch, ReactNode, SetStateAction, useMemo, useState } from 'react'; import { Box, Button, @@ -11,11 +11,11 @@ import { ListItem, Text, VStack, -} from '@chakra-ui/react' -import { useCombobox, useMultipleSelection } from 'downshift' +} from '@chakra-ui/react'; +import { useCombobox, useMultipleSelection } from 'downshift'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; export const MultiSelectCombobox = function ({ dropdownItems, @@ -26,30 +26,30 @@ export const MultiSelectCombobox = function ({ handleSelectedItemDisplay, handleSetCustomSelectedItem, }: { - dropdownItems: T[] - selectedItems: T[] - setSelectedItems: Dispatch> - handleFilteredDropdownItems: (dropdownItems: T[], selectedItems: T[], inputValue: string) => T[] - handleDropdownItemDisplay: (dropdownItem: T) => ReactNode - handleSelectedItemDisplay: (selectedItem: T) => ReactNode + dropdownItems: T[]; + selectedItems: T[]; + setSelectedItems: Dispatch>; + handleFilteredDropdownItems: (dropdownItems: T[], selectedItems: T[], inputValue: string) => T[]; + handleDropdownItemDisplay: (dropdownItem: T) => ReactNode; + handleSelectedItemDisplay: (selectedItem: T) => ReactNode; handleSetCustomSelectedItem?: ( selectedItems: T[], setSelectedItems: Dispatch>, customSelectedItemName: string, setCustomSelectedItemName: Dispatch> - ) => void + ) => void; }): JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); - const [inputValue, setInputValue] = useState('') - const [customSelectedItemName, setCustomSelectedItemName] = useState('') + const [inputValue, setInputValue] = useState(''); + const [customSelectedItemName, setCustomSelectedItemName] = useState(''); // Dropdown list excludes already selected options and includes those matching the input. const items = useMemo( () => handleFilteredDropdownItems(dropdownItems, selectedItems, inputValue), [inputValue, selectedItems, dropdownItems, handleFilteredDropdownItems] - ) + ); // 对已经选中的项目 添加处理事件 const { getSelectedItemProps, getDropdownProps, removeSelectedItem } = useMultipleSelection({ @@ -61,14 +61,14 @@ export const MultiSelectCombobox = function ({ case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace: case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem: if (newSelectedItems) { - setSelectedItems(newSelectedItems) + setSelectedItems(newSelectedItems); } - break + break; default: - break + break; } }, - }) + }); const { isOpen, getToggleButtonProps, @@ -84,7 +84,7 @@ export const MultiSelectCombobox = function ({ selectedItem: null, inputValue, stateReducer(state, actionAndChanges) { - const { changes, type } = actionAndChanges + const { changes, type } = actionAndChanges; switch (type) { case useCombobox.stateChangeTypes.InputKeyDownEnter: @@ -93,9 +93,9 @@ export const MultiSelectCombobox = function ({ ...changes, isOpen: true, // keep the menu open after selection. highlightedIndex: 0, // with the first option highlighted. - } + }; default: - return changes + return changes; } }, onStateChange({ inputValue: newInputValue, type, selectedItem: newSelectedItem }) { @@ -104,20 +104,20 @@ export const MultiSelectCombobox = function ({ case useCombobox.stateChangeTypes.ItemClick: case useCombobox.stateChangeTypes.InputBlur: if (newSelectedItem) { - setSelectedItems([...selectedItems, newSelectedItem]) - setInputValue('') + setSelectedItems([...selectedItems, newSelectedItem]); + setInputValue(''); } - break + break; case useCombobox.stateChangeTypes.InputChange: - setInputValue(newInputValue ?? '') + setInputValue(newInputValue ?? ''); - break + break; default: - break + break; } }, - }) + }); return ( @@ -215,7 +215,7 @@ export const MultiSelectCombobox = function ({ setSelectedItems, customSelectedItemName, setCustomSelectedItemName - ) + ); }} _hover={{ transform: 'scale(1.05)', @@ -282,8 +282,8 @@ export const MultiSelectCombobox = function ({ justifyContent="center" cursor="pointer" onClick={(e) => { - e.stopPropagation() - removeSelectedItem(selectedItemForRender) + e.stopPropagation(); + removeSelectedItem(selectedItemForRender); }} > ({ ))} - ) -} + ); +}; diff --git a/frontend/providers/aiproxy/components/common/MyTooltip.tsx b/frontend/providers/aiproxy/components/common/MyTooltip.tsx index b7412bac3..4c3f7370b 100644 --- a/frontend/providers/aiproxy/components/common/MyTooltip.tsx +++ b/frontend/providers/aiproxy/components/common/MyTooltip.tsx @@ -1,6 +1,6 @@ -'use client' +'use client'; -import { Tooltip, TooltipProps } from '@chakra-ui/react' +import { Tooltip, TooltipProps } from '@chakra-ui/react'; export const MyTooltip = ({ children, ...tooltipProps }: TooltipProps) => { const tooltipStyles = { @@ -42,11 +42,11 @@ export const MyTooltip = ({ children, ...tooltipProps }: TooltipProps) => { }, }, }, - } + }; return ( {children} - ) -} + ); +}; diff --git a/frontend/providers/aiproxy/components/common/Select.tsx b/frontend/providers/aiproxy/components/common/Select.tsx index ece695317..51535cf03 100644 --- a/frontend/providers/aiproxy/components/common/Select.tsx +++ b/frontend/providers/aiproxy/components/common/Select.tsx @@ -1,10 +1,10 @@ -'use client' -import { ReactNode } from 'react' -import { Box, List, ListItem, Text } from '@chakra-ui/react' -import { useSelect } from 'downshift' +'use client'; +import { ReactNode } from 'react'; +import { Box, List, ListItem, Text } from '@chakra-ui/react'; +import { useSelect } from 'downshift'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; export const CustomSelect = function ({ listItems, @@ -14,16 +14,16 @@ export const CustomSelect = function ({ placeholder, initSelectedItem, }: { - listItems: T[] - handleSelectedItemChange: (selectedItem: T) => void - handleDropdownItemDisplay: (dropdownItem: T) => ReactNode - handleSelectedItemDisplay: (selectedItem: T) => ReactNode - placeholder?: string - initSelectedItem?: T + listItems: T[]; + handleSelectedItemChange: (selectedItem: T) => void; + handleDropdownItemDisplay: (dropdownItem: T) => ReactNode; + handleSelectedItemDisplay: (selectedItem: T) => ReactNode; + placeholder?: string; + initSelectedItem?: T; }) { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const items = [placeholder, ...listItems] + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const items = [placeholder, ...listItems]; const { isOpen, @@ -37,12 +37,12 @@ export const CustomSelect = function ({ initialSelectedItem: initSelectedItem, onSelectedItemChange: ({ selectedItem: newSelectedItem }) => { if (newSelectedItem === placeholder) { - handleSelectedItemChange(undefined as T) + handleSelectedItemChange(undefined as T); } else { - handleSelectedItemChange(newSelectedItem as T) + handleSelectedItemChange(newSelectedItem as T); } }, - }) + }); return ( @@ -143,5 +143,5 @@ export const CustomSelect = function ({ ))} - ) -} + ); +}; diff --git a/frontend/providers/aiproxy/components/common/SelectDateRange.tsx b/frontend/providers/aiproxy/components/common/SelectDateRange.tsx index 768a84327..dfbeff48a 100644 --- a/frontend/providers/aiproxy/components/common/SelectDateRange.tsx +++ b/frontend/providers/aiproxy/components/common/SelectDateRange.tsx @@ -1,5 +1,5 @@ -import { ChangeEventHandler, Dispatch, SetStateAction, useState } from 'react' -import { DateRange, DayPicker, SelectRangeEventHandler } from 'react-day-picker' +import { ChangeEventHandler, Dispatch, SetStateAction, useState } from 'react'; +import { DateRange, DayPicker, SelectRangeEventHandler } from 'react-day-picker'; import { Box, Button, @@ -10,16 +10,16 @@ import { Popover, PopoverContent, PopoverTrigger, -} from '@chakra-ui/react' -import { endOfDay, format, isAfter, isBefore, isValid, parse, startOfDay } from 'date-fns' +} from '@chakra-ui/react'; +import { endOfDay, format, isAfter, isBefore, isValid, parse, startOfDay } from 'date-fns'; type SelectDateRangeProps = { - isDisabled?: boolean - startTime: Date - setStartTime: Dispatch> - endTime: Date - setEndTime: Dispatch> -} + isDisabled?: boolean; + startTime: Date; + setStartTime: Dispatch>; + endTime: Date; + setEndTime: Dispatch>; +}; export default function SelectDateRange({ isDisabled, @@ -29,146 +29,146 @@ export default function SelectDateRange({ setEndTime, ...props }: SelectDateRangeProps & FlexProps): JSX.Element { - const initState = { from: startTime, to: endTime } + const initState = { from: startTime, to: endTime }; - const [selectedRange, setSelectedRange] = useState(initState) - const [fromValue, setFromValue] = useState(format(initState.from, 'y-MM-dd')) - const [toValue, setToValue] = useState(format(initState.to, 'y-MM-dd')) - const [inputState, setInputState] = useState<0 | 1>(0) + const [selectedRange, setSelectedRange] = useState(initState); + const [fromValue, setFromValue] = useState(format(initState.from, 'y-MM-dd')); + const [toValue, setToValue] = useState(format(initState.to, 'y-MM-dd')); + const [inputState, setInputState] = useState<0 | 1>(0); const onClose = () => { - selectedRange?.from && setStartTime(startOfDay(selectedRange.from)) - selectedRange?.to && setEndTime(endOfDay(selectedRange.to)) - } + selectedRange?.from && setStartTime(startOfDay(selectedRange.from)); + selectedRange?.to && setEndTime(endOfDay(selectedRange.to)); + }; const handleFromChange: ChangeEventHandler = (e) => { - setFromValue(e.target.value) - const date = parse(e.target.value, 'y-MM-dd', new Date()) + setFromValue(e.target.value); + const date = parse(e.target.value, 'y-MM-dd', new Date()); if (!isValid(date)) { - return setSelectedRange({ from: undefined, to: selectedRange?.to }) + return setSelectedRange({ from: undefined, to: selectedRange?.to }); } if (selectedRange?.to) { if (isAfter(date, selectedRange.to)) { - setSelectedRange({ from: selectedRange.to, to: date }) + setSelectedRange({ from: selectedRange.to, to: date }); } else { - setSelectedRange({ from: date, to: selectedRange?.to }) + setSelectedRange({ from: date, to: selectedRange?.to }); } } else { - setSelectedRange({ from: date, to: date }) + setSelectedRange({ from: date, to: date }); } - } + }; const handleToChange: ChangeEventHandler = (e) => { - setToValue(e.target.value) - const date = parse(e.target.value, 'y-MM-dd', new Date()) + setToValue(e.target.value); + const date = parse(e.target.value, 'y-MM-dd', new Date()); if (!isValid(date)) { - return setSelectedRange({ from: selectedRange?.from, to: undefined }) + return setSelectedRange({ from: selectedRange?.from, to: undefined }); } if (selectedRange?.from) { if (isBefore(date, selectedRange.from)) { - setSelectedRange({ from: date, to: selectedRange.from }) + setSelectedRange({ from: date, to: selectedRange.from }); } else { - setSelectedRange({ from: selectedRange?.from, to: date }) + setSelectedRange({ from: selectedRange?.from, to: date }); } } else { - setSelectedRange({ from: date, to: date }) + setSelectedRange({ from: date, to: date }); } - } + }; const handleRangeSelect: SelectRangeEventHandler = (range: DateRange | undefined) => { if (range) { - let { from, to } = range + let { from, to } = range; if (inputState === 0) { if (from === selectedRange?.from) { - from = to + from = to; } else { - to = from + to = from; } - setInputState(1) + setInputState(1); } else { - setInputState(0) + setInputState(0); } setSelectedRange({ from, to, - }) + }); if (from) { - setFromValue(format(from, 'y-MM-dd')) + setFromValue(format(from, 'y-MM-dd')); } else { - setFromValue('') + setFromValue(''); } if (to) { - setToValue(format(to, 'y-MM-dd')) + setToValue(format(to, 'y-MM-dd')); } else { - setToValue(from ? format(from, 'y-MM-dd') : '') + setToValue(from ? format(from, 'y-MM-dd') : ''); } } else { if (fromValue && selectedRange?.from) { - setToValue(fromValue) + setToValue(fromValue); setSelectedRange({ ...selectedRange, to: selectedRange.from, - }) - setInputState(1) + }); + setInputState(1); } } - } + }; const handleRangeSelectFrom: SelectRangeEventHandler = (range: DateRange | undefined) => { if (range) { - let { from, to } = range + let { from, to } = range; if (selectedRange?.to) { if (from) { if (!to) { - to = from + to = from; } else if (from === selectedRange?.from) { - from = to - to = selectedRange.to + from = to; + to = selectedRange.to; } if (isBefore(from, selectedRange.to)) { setSelectedRange({ ...selectedRange, from, - }) - setFromValue(format(from, 'y-MM-dd')) + }); + setFromValue(format(from, 'y-MM-dd')); } } } } - } + }; const handleRangeSelectTo: SelectRangeEventHandler = (range: DateRange | undefined) => { if (range) { - let { from, to } = range + let { from, to } = range; if (selectedRange?.from) { if (to) { if (!from) { - from = to + from = to; } else if (to === selectedRange?.to) { - to = from - from = selectedRange.from + to = from; + from = selectedRange.from; } if (isAfter(to, selectedRange.from)) { setSelectedRange({ ...selectedRange, to, - }) - setToValue(format(to, 'y-MM-dd')) + }); + setToValue(format(to, 'y-MM-dd')); } } } } else { if (fromValue && selectedRange?.from) { - setToValue(fromValue) + setToValue(fromValue); setSelectedRange({ ...selectedRange, to: selectedRange.from, - }) - setInputState(1) + }); + setInputState(1); } } - } + }; return ( { - selectedRange?.from && setStartTime(startOfDay(selectedRange.from)) + selectedRange?.from && setStartTime(startOfDay(selectedRange.from)); }} /> @@ -229,7 +229,7 @@ export default function SelectDateRange({ minW="90px" onChange={handleToChange} onBlur={() => { - selectedRange?.to && setEndTime(endOfDay(selectedRange.to)) + selectedRange?.to && setEndTime(endOfDay(selectedRange.to)); }} /> @@ -251,8 +251,8 @@ export default function SelectDateRange({ { - setInputState(0) - onClose() + setInputState(0); + onClose(); }} > @@ -286,5 +286,5 @@ export default function SelectDateRange({ - ) + ); } diff --git a/frontend/providers/aiproxy/components/common/SingleSelectCombobox.tsx b/frontend/providers/aiproxy/components/common/SingleSelectCombobox.tsx index f854d4481..8968874e3 100644 --- a/frontend/providers/aiproxy/components/common/SingleSelectCombobox.tsx +++ b/frontend/providers/aiproxy/components/common/SingleSelectCombobox.tsx @@ -1,17 +1,26 @@ -'use client' -import { ReactNode, useEffect, useState } from 'react' -import { Box, Button, FormLabel, Input, InputGroup, List, ListItem, VStack } from '@chakra-ui/react' -import { useCombobox, UseComboboxReturnValue } from 'downshift' +'use client'; +import { ReactNode, useEffect, useState } from 'react'; +import { + Box, + Button, + FormLabel, + Input, + InputGroup, + List, + ListItem, + VStack, +} from '@chakra-ui/react'; +import { useCombobox, UseComboboxReturnValue } from 'downshift'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; export const SingleSelectCombobox: (props: { - dropdownItems: T[] - setSelectedItem: (value: T) => void - handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[] - handleDropdownItemDisplay: (dropdownItem: T) => ReactNode - initSelectedItem?: T + dropdownItems: T[]; + setSelectedItem: (value: T) => void; + handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[]; + handleDropdownItemDisplay: (dropdownItem: T) => ReactNode; + initSelectedItem?: T; }) => JSX.Element = function ({ dropdownItems, setSelectedItem, @@ -19,18 +28,18 @@ export const SingleSelectCombobox: (props: { handleDropdownItemDisplay, initSelectedItem, }: { - dropdownItems: T[] - setSelectedItem: (value: T) => void - handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[] - handleDropdownItemDisplay: (dropdownItem: T) => ReactNode - initSelectedItem?: T + dropdownItems: T[]; + setSelectedItem: (value: T) => void; + handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[]; + handleDropdownItemDisplay: (dropdownItem: T) => ReactNode; + initSelectedItem?: T; }) { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const [getFilteredDropdownItems, setGetFilteredDropdownItems] = useState(dropdownItems) + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const [getFilteredDropdownItems, setGetFilteredDropdownItems] = useState(dropdownItems); useEffect(() => { - setGetFilteredDropdownItems(dropdownItems) - }, [dropdownItems]) + setGetFilteredDropdownItems(dropdownItems); + }, [dropdownItems]); const { isOpen: isComboboxOpen, @@ -44,18 +53,18 @@ export const SingleSelectCombobox: (props: { }: UseComboboxReturnValue = useCombobox({ items: getFilteredDropdownItems, onInputValueChange: ({ inputValue }) => { - setGetFilteredDropdownItems(handleDropdownItemFilter(dropdownItems, inputValue)) + setGetFilteredDropdownItems(handleDropdownItemFilter(dropdownItems, inputValue)); }, initialSelectedItem: initSelectedItem || undefined, onSelectedItemChange: ({ selectedItem }) => { - const selectedDropdownItem = dropdownItems.find((item) => item === selectedItem) + const selectedDropdownItem = dropdownItems.find((item) => item === selectedItem); if (selectedDropdownItem) { - setSelectedItem(selectedDropdownItem) + setSelectedItem(selectedDropdownItem); } }, - }) + }); return ( @@ -197,5 +206,5 @@ export const SingleSelectCombobox: (props: { ))} - ) -} + ); +}; diff --git a/frontend/providers/aiproxy/components/common/SingleSelectComboboxUnStyle.tsx b/frontend/providers/aiproxy/components/common/SingleSelectComboboxUnStyle.tsx index 3789de7ce..441853c0e 100644 --- a/frontend/providers/aiproxy/components/common/SingleSelectComboboxUnStyle.tsx +++ b/frontend/providers/aiproxy/components/common/SingleSelectComboboxUnStyle.tsx @@ -1,20 +1,20 @@ -'use client' -import { ReactNode, useEffect, useState } from 'react' -import { Box, Button, Flex, FlexProps, Input, InputGroup, List, ListItem } from '@chakra-ui/react' -import { useCombobox, UseComboboxReturnValue } from 'downshift' +'use client'; +import { ReactNode, useEffect, useState } from 'react'; +import { Box, Button, Flex, FlexProps, Input, InputGroup, List, ListItem } from '@chakra-ui/react'; +import { useCombobox, UseComboboxReturnValue } from 'downshift'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; export const SingleSelectComboboxUnstyle: (props: { - dropdownItems: T[] - setSelectedItem: (value: T) => void - handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[] - handleDropdownItemDisplay: (dropdownItem: T) => ReactNode - handleInputDisplay?: (item: T) => string - initSelectedItem?: T - flexProps?: FlexProps - placeholder?: string + dropdownItems: T[]; + setSelectedItem: (value: T) => void; + handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[]; + handleDropdownItemDisplay: (dropdownItem: T) => ReactNode; + handleInputDisplay?: (item: T) => string; + initSelectedItem?: T; + flexProps?: FlexProps; + placeholder?: string; }) => JSX.Element = function ({ dropdownItems, setSelectedItem, @@ -25,21 +25,21 @@ export const SingleSelectComboboxUnstyle: (props: { flexProps, placeholder, }: { - dropdownItems: T[] - setSelectedItem: (value: T) => void - handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[] - handleDropdownItemDisplay: (dropdownItem: T) => ReactNode - handleInputDisplay?: (item: T) => string - initSelectedItem?: T - flexProps?: FlexProps - placeholder?: string + dropdownItems: T[]; + setSelectedItem: (value: T) => void; + handleDropdownItemFilter: (dropdownItems: T[], inputValue: string) => T[]; + handleDropdownItemDisplay: (dropdownItem: T) => ReactNode; + handleInputDisplay?: (item: T) => string; + initSelectedItem?: T; + flexProps?: FlexProps; + placeholder?: string; }) { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const [getFilteredDropdownItems, setGetFilteredDropdownItems] = useState(dropdownItems) + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const [getFilteredDropdownItems, setGetFilteredDropdownItems] = useState(dropdownItems); useEffect(() => { - setGetFilteredDropdownItems(dropdownItems) - }, [dropdownItems]) + setGetFilteredDropdownItems(dropdownItems); + }, [dropdownItems]); const { isOpen: isComboboxOpen, @@ -52,22 +52,22 @@ export const SingleSelectComboboxUnstyle: (props: { }: UseComboboxReturnValue = useCombobox({ items: getFilteredDropdownItems, onInputValueChange: ({ inputValue }) => { - setGetFilteredDropdownItems(handleDropdownItemFilter(dropdownItems, inputValue)) + setGetFilteredDropdownItems(handleDropdownItemFilter(dropdownItems, inputValue)); }, initialSelectedItem: initSelectedItem || undefined, itemToString: (item) => { - if (!item) return '' - return handleInputDisplay ? handleInputDisplay(item) : String(item) + if (!item) return ''; + return handleInputDisplay ? handleInputDisplay(item) : String(item); }, onSelectedItemChange: ({ selectedItem }) => { - const selectedDropdownItem = dropdownItems.find((item) => item === selectedItem) + const selectedDropdownItem = dropdownItems.find((item) => item === selectedItem); if (selectedDropdownItem) { - setSelectedItem(selectedDropdownItem) + setSelectedItem(selectedDropdownItem); } }, - }) + }); return ( @@ -195,5 +195,5 @@ export const SingleSelectComboboxUnstyle: (props: { ))} - ) -} + ); +}; diff --git a/frontend/providers/aiproxy/components/common/SwitchPage.tsx b/frontend/providers/aiproxy/components/common/SwitchPage.tsx index 07972a569..e0acdde8e 100644 --- a/frontend/providers/aiproxy/components/common/SwitchPage.tsx +++ b/frontend/providers/aiproxy/components/common/SwitchPage.tsx @@ -1,7 +1,7 @@ -import { Button, ButtonProps, Flex, FlexProps, Text } from '@chakra-ui/react' -import { useTranslation } from 'next-i18next' +import { Button, ButtonProps, Flex, FlexProps, Text } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; -import { RightFirstIcon, ToLeftIcon } from '@/ui/icons' +import { RightFirstIcon, ToLeftIcon } from '@/ui/icons'; export default function SwitchPage({ totalPage, @@ -12,14 +12,14 @@ export default function SwitchPage({ isPreviousData, ...props }: { - currentPage: number - totalPage: number - totalItem: number - pageSize: number - isPreviousData?: boolean - setCurrentPage: (idx: number) => void + currentPage: number; + totalPage: number; + totalItem: number; + pageSize: number; + isPreviousData?: boolean; + setCurrentPage: (idx: number) => void; } & FlexProps) { - const { t } = useTranslation() + const { t } = useTranslation(); const switchStyle: ButtonProps = { width: '24px', height: '24px', @@ -38,7 +38,7 @@ export default function SwitchPage({ cursor: 'not-allowed', minW: '0', }, - } + }; return ( @@ -56,8 +56,8 @@ export default function SwitchPage({ minW="0" boxSize="24px" onClick={(e) => { - e.preventDefault() - setCurrentPage(1) + e.preventDefault(); + setCurrentPage(1); }} > @@ -70,8 +70,8 @@ export default function SwitchPage({ minW="0" boxSize="24px" onClick={(e) => { - e.preventDefault() - setCurrentPage(currentPage - 1) + e.preventDefault(); + setCurrentPage(currentPage - 1); }} > @@ -88,8 +88,8 @@ export default function SwitchPage({ minW="0" borderRadius={'50%'} onClick={(e) => { - e.preventDefault() - setCurrentPage(currentPage + 1) + e.preventDefault(); + setCurrentPage(currentPage + 1); }} > @@ -104,8 +104,8 @@ export default function SwitchPage({ borderRadius={'50%'} mr={'10px'} onClick={(e) => { - e.preventDefault() - setCurrentPage(totalPage) + e.preventDefault(); + setCurrentPage(totalPage); }} > @@ -118,5 +118,5 @@ export default function SwitchPage({ /{t('Page')} - ) + ); } diff --git a/frontend/providers/aiproxy/components/table/BaseTable.tsx b/frontend/providers/aiproxy/components/table/BaseTable.tsx index c80198e85..e9ae614e8 100644 --- a/frontend/providers/aiproxy/components/table/BaseTable.tsx +++ b/frontend/providers/aiproxy/components/table/BaseTable.tsx @@ -8,8 +8,8 @@ import { Th, Thead, Tr, -} from '@chakra-ui/react' -import { flexRender, Table as ReactTable } from '@tanstack/react-table' +} from '@chakra-ui/react'; +import { flexRender, Table as ReactTable } from '@tanstack/react-table'; export function BaseTable({ table, @@ -38,10 +38,10 @@ export function BaseTable({ > {flexRender(header.column.columnDef.header, header.getContext())} - ) + ); })} - ) + ); })} @@ -72,14 +72,14 @@ export function BaseTable({ {flexRender(cell.column.columnDef.cell, cell.getContext())} - ) + ); })} - ) + ); }) )} - ) + ); } diff --git a/frontend/providers/aiproxy/components/user/KeyList.tsx b/frontend/providers/aiproxy/components/user/KeyList.tsx index e76f634b4..15c4e7eab 100644 --- a/frontend/providers/aiproxy/components/user/KeyList.tsx +++ b/frontend/providers/aiproxy/components/user/KeyList.tsx @@ -1,5 +1,5 @@ -'use client' -import React, { useMemo, useState } from 'react' +'use client'; +import React, { useMemo, useState } from 'react'; import { Box, Button, @@ -28,33 +28,33 @@ import { Thead, Tr, useDisclosure, -} from '@chakra-ui/react' -import { CurrencySymbol } from '@sealos/ui' -import { useMessage } from '@sealos/ui' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +} from '@chakra-ui/react'; +import { CurrencySymbol } from '@sealos/ui'; +import { useMessage } from '@sealos/ui'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Column, createColumnHelper, flexRender, getCoreRowModel, useReactTable, -} from '@tanstack/react-table' -import { TFunction } from 'i18next' +} from '@tanstack/react-table'; +import { TFunction } from 'i18next'; -import { createToken, deleteToken, getTokens, updateToken } from '@/api/platform' -import { useTranslationClientSide } from '@/app/i18n/client' -import { MyTooltip } from '@/components/common/MyTooltip' -import SwitchPage from '@/components/common/SwitchPage' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import { QueryKey } from '@/types/query-key' -import { TokenInfo } from '@/types/user/token' -import { ChainIcon } from '@/ui/icons/index' +import { createToken, deleteToken, getTokens, updateToken } from '@/api/platform'; +import { useTranslationClientSide } from '@/app/i18n/client'; +import { MyTooltip } from '@/components/common/MyTooltip'; +import SwitchPage from '@/components/common/SwitchPage'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import { QueryKey } from '@/types/query-key'; +import { TokenInfo } from '@/types/user/token'; +import { ChainIcon } from '@/ui/icons/index'; export function KeyList(): JSX.Element { - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') - const { isOpen, onOpen, onClose } = useDisclosure() + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); + const { isOpen, onOpen, onClose } = useDisclosure(); return ( <> @@ -78,7 +78,7 @@ export function KeyList(): JSX.Element { {/* modal */} - ) + ); } export enum TableHeaderId { @@ -100,7 +100,7 @@ enum KeyStatus { } const CustomHeader = ({ column, t }: { column: Column; t: TFunction }) => { - const { currencySymbol } = useBackendStore() + const { currencySymbol } = useBackendStore(); if (column.id === TableHeaderId.USED_AMOUNT) { return ( @@ -116,7 +116,7 @@ const CustomHeader = ({ column, t }: { column: Column; t: TFunction } - ) + ); } return ( ; t: TFunction } > {t(column.id as TableHeaderId)} - ) -} + ); +}; const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { - const aiproxyBackend = useBackendStore((state) => state.aiproxyBackend) - const [total, setTotal] = useState(0) - const [page, setPage] = useState(1) - const [pageSize, setPageSize] = useState(10) - const [openPopoverId, setOpenPopoverId] = useState(null) + const aiproxyBackend = useBackendStore((state) => state.aiproxyBackend); + const [total, setTotal] = useState(0); + const [page, setPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [openPopoverId, setOpenPopoverId] = useState(null); const { message } = useMessage({ warningBoxBg: '#FFFAEB', @@ -146,28 +146,28 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) - const queryClient = useQueryClient() + }); + const queryClient = useQueryClient(); const { data, isLoading } = useQuery({ queryKey: [QueryKey.GetTokens, page, pageSize], queryFn: () => getTokens({ page, perPage: pageSize }), refetchOnReconnect: true, onSuccess(data) { - setTotal(data?.total || 0) + setTotal(data?.total || 0); }, - }) + }); const deleteKeyMutation = useMutation((id: number) => deleteToken(id), { onSuccess() { - queryClient.invalidateQueries([QueryKey.GetTokens]) + queryClient.invalidateQueries([QueryKey.GetTokens]); message({ status: 'success', title: t('key.deleteSuccess'), isClosable: true, duration: 2000, position: 'top', - }) + }); }, onError(err: any) { message({ @@ -176,22 +176,22 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { description: err?.message || t('key.deleteFailed'), isClosable: true, position: 'top', - }) + }); }, - }) + }); const updateKeyMutation = useMutation( ({ id, status }: { id: number; status: number }) => updateToken(id, status), { onSuccess() { - queryClient.invalidateQueries([QueryKey.GetTokens]) + queryClient.invalidateQueries([QueryKey.GetTokens]); message({ status: 'success', title: t('key.updateSuccess'), isClosable: true, duration: 2000, position: 'top', - }) + }); }, onError(err: any) { message({ @@ -200,22 +200,22 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { description: err?.message ? t(err.message) : t('key.updateFailed'), isClosable: true, position: 'top', - }) + }); }, } - ) + ); // Update the button click handlers in the table actions column: const handleStatusUpdate = (id: number, currentStatus: number) => { - const newStatus = currentStatus === KeyStatus.DISABLED ? KeyStatus.ENABLED : KeyStatus.DISABLED - updateKeyMutation.mutate({ id, status: newStatus }) - } + const newStatus = currentStatus === KeyStatus.DISABLED ? KeyStatus.ENABLED : KeyStatus.DISABLED; + updateKeyMutation.mutate({ id, status: newStatus }); + }; const handleDelete = (id: number) => { - deleteKeyMutation.mutate(id) - } + deleteKeyMutation.mutate(id); + }; - const columnHelper = createColumnHelper() + const columnHelper = createColumnHelper(); const columns = [ columnHelper.accessor((row) => row.name, { @@ -247,7 +247,7 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { letterSpacing="0.5px" cursor="pointer" onClick={() => { - const key = 'sk-' + info.getValue() + const key = 'sk-' + info.getValue(); navigator.clipboard.writeText(key).then( () => { message({ @@ -256,7 +256,7 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { isClosable: true, duration: 2000, position: 'top', - }) + }); }, (err) => { message({ @@ -265,9 +265,9 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { description: err?.message || t('copyFailed'), isClosable: true, position: 'top', - }) + }); } - ) + ); }} > @@ -296,7 +296,7 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { id: TableHeaderId.LAST_USED_AT, header: (props) => , cell: (info) => { - const timestamp = info.getValue() + const timestamp = info.getValue(); if (timestamp && timestamp < 0) { return ( @@ -310,7 +310,7 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { > {t('key.unused')} - ) + ); } return ( @@ -324,37 +324,37 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { > {new Date(timestamp).toLocaleString()} - ) + ); }, }), columnHelper.accessor((row) => row.status, { id: TableHeaderId.STATUS, header: (props) => , cell: (info) => { - const status = info.getValue() - let statusText = '' - let statusColor = '' + const status = info.getValue(); + let statusText = ''; + let statusColor = ''; switch (status) { case KeyStatus.ENABLED: - statusText = t('keystatus.enabled') - statusColor = 'green.600' - break + statusText = t('keystatus.enabled'); + statusColor = 'green.600'; + break; case KeyStatus.DISABLED: - statusText = t('keystatus.disabled') - statusColor = 'red.600' - break + statusText = t('keystatus.disabled'); + statusColor = 'red.600'; + break; case KeyStatus.EXPIRED: - statusText = t('keystatus.expired') - statusColor = 'orange.500' - break + statusText = t('keystatus.expired'); + statusColor = 'orange.500'; + break; case KeyStatus.EXHAUSTED: - statusText = t('keystatus.exhausted') - statusColor = 'gray.500' - break + statusText = t('keystatus.exhausted'); + statusColor = 'gray.500'; + break; default: - statusText = t('keystatus.unknown') - statusColor = 'gray.500' + statusText = t('keystatus.unknown'); + statusColor = 'gray.500'; } return ( @@ -368,7 +368,7 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { > {statusText} - ) + ); }, }), @@ -393,11 +393,11 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { id: TableHeaderId.USED_AMOUNT, header: (props) => , cell: (info) => { - const value = Number(info.getValue()) + const value = Number(info.getValue()); // 获取小数部分的长度 - const decimalLength = value.toString().split('.')[1]?.length || 0 + const decimalLength = value.toString().split('.')[1]?.length || 0; // 如果小数位超过6位则保留6位,否则保持原样 - const formattedValue = decimalLength > 6 ? value.toFixed(6) : value + const formattedValue = decimalLength > 6 ? value.toFixed(6) : value; return ( void }) => { > {formattedValue} - ) + ); }, }), @@ -485,8 +485,8 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { color: 'brightBlue.600', }} onClick={() => { - handleStatusUpdate(info.row.original.id, info.row.original.status) - setOpenPopoverId(null) + handleStatusUpdate(info.row.original.id, info.row.original.status); + setOpenPopoverId(null); }} > void }) => { color: 'brightBlue.600', }} onClick={() => { - setOpenPopoverId(null) - handleStatusUpdate(info.row.original.id, info.row.original.status) + setOpenPopoverId(null); + handleStatusUpdate(info.row.original.id, info.row.original.status); }} > void }) => { color: '#D92D20', }} onClick={() => { - handleDelete(info.row.original.id) - setOpenPopoverId(null) + handleDelete(info.row.original.id); + setOpenPopoverId(null); }} > void }) => { ), }), - ] + ]; const sortTableData = (data: TokenInfo[]) => { - return data.sort((a, b) => b.created_at - a.created_at) - } + return data.sort((a, b) => b.created_at - a.created_at); + }; - const sortedData = useMemo(() => sortTableData(data?.tokens || []), [data]) + const sortedData = useMemo(() => sortTableData(data?.tokens || []), [data]); const table = useReactTable({ data: sortedData, columns, getCoreRowModel: getCoreRowModel(), - }) + }); return ( <> @@ -767,7 +767,7 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { cursor="pointer" whiteSpace="nowrap" onClick={() => { - const endpoint = aiproxyBackend + const endpoint = aiproxyBackend; navigator.clipboard.writeText(endpoint).then( () => { message({ @@ -776,7 +776,7 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { isClosable: true, duration: 2000, position: 'top', - }) + }); }, (err) => { message({ @@ -785,9 +785,9 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { description: err?.message || t('copyFailed'), isClosable: true, position: 'top', - }) + }); } - ) + ); }} > {aiproxyBackend} @@ -857,10 +857,10 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { > {flexRender(header.column.columnDef.header, header.getContext())} - ) + ); })} - ) + ); })} @@ -878,10 +878,10 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { {flexRender(cell.column.columnDef.cell, cell.getContext())} - ) + ); })} - ) + ); })} @@ -900,22 +900,22 @@ const ModelKeyTable = ({ t, onOpen }: { t: TFunction; onOpen: () => void }) => { )} - ) -} + ); +}; function CreateKeyModal({ isOpen, onClose, t, }: { - isOpen: boolean - onClose: () => void - t: TFunction + isOpen: boolean; + onClose: () => void; + t: TFunction; }) { - const initialRef = React.useRef(null) - const [name, setName] = useState('') - const [error, setError] = useState('') - const queryClient = useQueryClient() + const initialRef = React.useRef(null); + const [name, setName] = useState(''); + const [error, setError] = useState(''); + const queryClient = useQueryClient(); const { message } = useMessage({ warningBoxBg: '#FFFAEB', warningIconBg: '#F79009', @@ -924,58 +924,58 @@ function CreateKeyModal({ successBoxBg: '#EDFBF3', successIconBg: '#039855', successIconFill: 'white', - }) + }); const createKeyMutation = useMutation((name: string) => createToken(name), { onSuccess(data) { - createKeyMutation.reset() - setName('') - queryClient.invalidateQueries([QueryKey.GetTokens]) + createKeyMutation.reset(); + setName(''); + queryClient.invalidateQueries([QueryKey.GetTokens]); message({ status: 'success', title: t('key.createSuccess'), isClosable: true, duration: 2000, position: 'top', - }) - onClose() + }); + onClose(); }, onError(err) { - console.error(err) + console.error(err); message({ status: 'warning', title: t('key.createFailed'), description: err instanceof Error ? t(err.message as any) : t('key.createFailed'), isClosable: true, position: 'top', - }) + }); }, - }) + }); const validateName = (value: string) => { if (!value) { - setError(t('key.nameRequired')) + setError(t('key.nameRequired')); } else if (value.length >= 32) { - setError(t('key.nameMaxLength')) + setError(t('key.nameMaxLength')); } else if (!/^[A-Za-z0-9-]+$/.test(value)) { - setError(t('key.nameOnlyLettersAndNumbers')) + setError(t('key.nameOnlyLettersAndNumbers')); } else { - setError('') + setError(''); } - } + }; const handleNameChange = (e: React.ChangeEvent) => { - const newName = e.target.value - validateName(newName) - setName(newName) - } + const newName = e.target.value; + validateName(newName); + setName(newName); + }; const handleConfirm = () => { if (error === '' && name !== '') { - createKeyMutation.mutate(name) - return + createKeyMutation.mutate(name); + return; } - } + }; return ( @@ -1098,7 +1098,7 @@ function CreateKeyModal({ - ) + ); } -export default KeyList +export default KeyList; diff --git a/frontend/providers/aiproxy/components/user/Sidebar.tsx b/frontend/providers/aiproxy/components/user/Sidebar.tsx index 50ed545fe..57f0e8269 100644 --- a/frontend/providers/aiproxy/components/user/Sidebar.tsx +++ b/frontend/providers/aiproxy/components/user/Sidebar.tsx @@ -1,38 +1,38 @@ -'use client' -import { Button, Flex, Text } from '@chakra-ui/react' -import Image, { StaticImageData } from 'next/image' -import Link from 'next/link' -import { usePathname } from 'next/navigation' +'use client'; +import { Button, Flex, Text } from '@chakra-ui/react'; +import Image, { StaticImageData } from 'next/image'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; -import { useTranslationClientSide } from '@/app/i18n/client' -import { useI18n } from '@/providers/i18n/i18nContext' -import { useBackendStore } from '@/store/backend' -import homeIcon from '@/ui/svg/icons/sidebar/home.svg' -import homeIcon_a from '@/ui/svg/icons/sidebar/home_a.svg' -import keysIcon from '@/ui/svg/icons/sidebar/key.svg' -import keysIcon_a from '@/ui/svg/icons/sidebar/key_a.svg' -import logsIcon from '@/ui/svg/icons/sidebar/logs.svg' -import logsIcon_a from '@/ui/svg/icons/sidebar/logs_a.svg' -import mcpIcon from '@/ui/svg/icons/sidebar/mcp.svg' -import mcpIcon_a from '@/ui/svg/icons/sidebar/mcp_a.svg' -import priceIcon from '@/ui/svg/icons/sidebar/price.svg' -import priceIcon_a from '@/ui/svg/icons/sidebar/price_a.svg' +import { useTranslationClientSide } from '@/app/i18n/client'; +import { useI18n } from '@/providers/i18n/i18nContext'; +import { useBackendStore } from '@/store/backend'; +import homeIcon from '@/ui/svg/icons/sidebar/home.svg'; +import homeIcon_a from '@/ui/svg/icons/sidebar/home_a.svg'; +import keysIcon from '@/ui/svg/icons/sidebar/key.svg'; +import keysIcon_a from '@/ui/svg/icons/sidebar/key_a.svg'; +import logsIcon from '@/ui/svg/icons/sidebar/logs.svg'; +import logsIcon_a from '@/ui/svg/icons/sidebar/logs_a.svg'; +import mcpIcon from '@/ui/svg/icons/sidebar/mcp.svg'; +import mcpIcon_a from '@/ui/svg/icons/sidebar/mcp_a.svg'; +import priceIcon from '@/ui/svg/icons/sidebar/price.svg'; +import priceIcon_a from '@/ui/svg/icons/sidebar/price_a.svg'; type Menu = { - id: string - url: string - value: string - icon: StaticImageData - activeIcon: StaticImageData - display: boolean -} + id: string; + url: string; + value: string; + icon: StaticImageData; + activeIcon: StaticImageData; + display: boolean; +}; const SideBar = (): JSX.Element => { - const pathname = usePathname() - const { lng } = useI18n() - const { t } = useTranslationClientSide(lng, 'common') + const pathname = usePathname(); + const { lng } = useI18n(); + const { t } = useTranslationClientSide(lng, 'common'); - const { invitationUrl, docUrl } = useBackendStore() + const { invitationUrl, docUrl } = useBackendStore(); const menus: Menu[] = [ { @@ -75,7 +75,7 @@ const SideBar = (): JSX.Element => { activeIcon: mcpIcon_a, display: true, }, - ] + ]; return ( { {menus .filter((menu) => menu.display) .map((menu) => { - const fullUrl = `/${lng}${menu.url}` - const isActive = pathname === fullUrl || pathname.startsWith(fullUrl + '/') + const fullUrl = `/${lng}${menu.url}`; + const isActive = pathname === fullUrl || pathname.startsWith(fullUrl + '/'); return ( @@ -132,7 +132,7 @@ const SideBar = (): JSX.Element => { - ) + ); })} {/* doc */} @@ -154,7 +154,7 @@ const SideBar = (): JSX.Element => { bg="rgba(150, 153, 180, 0.10)" _hover={{ bg: 'rgba(150, 153, 180, 0.15)' }} onClick={() => { - window.open(docUrl, '_blank') + window.open(docUrl, '_blank'); }} > { bg="rgba(150, 153, 180, 0.10)" _hover={{ bg: 'rgba(150, 153, 180, 0.15)' }} onClick={() => { - window.open(invitationUrl, '_blank') + window.open(invitationUrl, '_blank'); }} > { )} - ) -} + ); +}; -export default SideBar +export default SideBar; diff --git a/frontend/providers/aiproxy/providers/chakra/providers.tsx b/frontend/providers/aiproxy/providers/chakra/providers.tsx index 55e764aff..87b54a622 100644 --- a/frontend/providers/aiproxy/providers/chakra/providers.tsx +++ b/frontend/providers/aiproxy/providers/chakra/providers.tsx @@ -1,12 +1,12 @@ -'use client' +'use client'; -import { ReactNode } from 'react' -import { ChakraProvider } from '@chakra-ui/react' +import { ReactNode } from 'react'; +import { ChakraProvider } from '@chakra-ui/react'; -import { theme } from '@/ui/chakraTheme' +import { theme } from '@/ui/chakraTheme'; export function ChakraProviders({ children }: { children: ReactNode }): JSX.Element { - return {children} + return {children}; } -export default ChakraProviders +export default ChakraProviders; diff --git a/frontend/providers/aiproxy/providers/i18n/i18nContext.tsx b/frontend/providers/aiproxy/providers/i18n/i18nContext.tsx index 4e9fc28b4..a7ae5ba88 100644 --- a/frontend/providers/aiproxy/providers/i18n/i18nContext.tsx +++ b/frontend/providers/aiproxy/providers/i18n/i18nContext.tsx @@ -1,16 +1,16 @@ -'use client' -import { createContext, useContext } from 'react' +'use client'; +import { createContext, useContext } from 'react'; -const I18nContext = createContext<{ lng: string }>({ lng: 'en' }) +const I18nContext = createContext<{ lng: string }>({ lng: 'en' }); -export const useI18n = (): { lng: string } => useContext(I18nContext) +export const useI18n = (): { lng: string } => useContext(I18nContext); export function I18nProvider({ children, lng, }: { - children: React.ReactNode - lng: string + children: React.ReactNode; + lng: string; }): JSX.Element { - return {children} + return {children}; } diff --git a/frontend/providers/aiproxy/providers/tanstack-query/QueryProvider.tsx b/frontend/providers/aiproxy/providers/tanstack-query/QueryProvider.tsx index fd482b93c..1d4ce0c86 100644 --- a/frontend/providers/aiproxy/providers/tanstack-query/QueryProvider.tsx +++ b/frontend/providers/aiproxy/providers/tanstack-query/QueryProvider.tsx @@ -1,7 +1,7 @@ -'use client' +'use client'; -import { useState } from 'react' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { useState } from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const QueryProvider = ({ children }: { children: React.ReactNode }) => { const [queryClient] = useState( @@ -15,9 +15,9 @@ const QueryProvider = ({ children }: { children: React.ReactNode }) => { }, }, }) - ) + ); - return {children} -} + return {children}; +}; -export default QueryProvider +export default QueryProvider; diff --git a/frontend/providers/aiproxy/ui/icons/index.tsx b/frontend/providers/aiproxy/ui/icons/index.tsx index 2aec6d88c..16144a1c1 100644 --- a/frontend/providers/aiproxy/ui/icons/index.tsx +++ b/frontend/providers/aiproxy/ui/icons/index.tsx @@ -1,4 +1,4 @@ -import { Icon, IconProps } from '@chakra-ui/react' +import { Icon, IconProps } from '@chakra-ui/react'; export function ToLeftIcon(props: IconProps) { return ( @@ -15,7 +15,7 @@ export function ToLeftIcon(props: IconProps) { fill="#111824" /> - ) + ); } export function RightFirstIcon(props: IconProps) { @@ -33,7 +33,7 @@ export function RightFirstIcon(props: IconProps) { fill="#111824" /> - ) + ); } export const ChainIcon = (props: IconProps) => ( @@ -45,7 +45,7 @@ export const ChainIcon = (props: IconProps) => ( fill="currentColor" /> -) +); export const ListIcon = (props: IconProps) => ( @@ -74,4 +74,4 @@ export const ListIcon = (props: IconProps) => ( fill="currentColor" /> -) +); diff --git a/frontend/providers/aiproxy/ui/icons/mcp-icons/index.tsx b/frontend/providers/aiproxy/ui/icons/mcp-icons/index.tsx index e62cd9dd2..763f70a1d 100644 --- a/frontend/providers/aiproxy/ui/icons/mcp-icons/index.tsx +++ b/frontend/providers/aiproxy/ui/icons/mcp-icons/index.tsx @@ -1,53 +1,54 @@ -import { StaticImageData } from 'next/image' -import DefaultIcon from '@/ui/svg/icons/mcp/default.svg' -import alipayIcon from '@/ui/svg/icons/mcp/alipay.svg' -import baiduMapIcon from '@/ui/svg/icons/mcp/baiduMap.svg' -import one2306Icon from '@/ui/svg/icons/mcp/12306.svg' -import bingIcon from '@/ui/svg/icons/mcp/bing.svg' -import amapIcon from '@/ui/svg/icons/mcp/amap.svg' -import searchIcon from '@/ui/svg/icons/mcp/search.svg' -import firecrawlIcon from '@/ui/svg/icons/mcp/firecrawl.svg' -import flomoIcon from '@/ui/svg/icons/mcp/flomo.svg' -import vis from '@/ui/svg/icons/mcp/vis.svg' -import hefengIcon from '@/ui/svg/icons/mcp/hefeng.svg' -import gezheIcon from '@/ui/svg/icons/mcp/gezhe.svg' -import tavilyIcon from '@/ui/svg/icons/mcp/tavily.svg' -import jinaIcon from '@/ui/svg/icons/mcp/jina.svg' -import howtocookIcon from '@/ui/svg/icons/mcp/howtocook.svg' -import timeIcon from '@/ui/svg/icons/mcp/time.svg' -import blenderIcon from '@/ui/svg/icons/mcp/blender.svg' -import browserbaseIcon from '@/ui/svg/icons/mcp/browserbase.svg' -import excelIcon from '@/ui/svg/icons/mcp/excel.svg' -import magicIcon from '@/ui/svg/icons/mcp/21dev.png' -import alibabacloudDmsIcon from '@/ui/svg/icons/mcp/alibabacloud-dms.svg' -import academicSearchIcon from '@/ui/svg/icons/mcp/academic-search.svg' -import allvoicelabIcon from '@/ui/svg/icons/mcp/allvoicelab.png' -import cfworkerIcon from '@/ui/svg/icons/mcp/cfworker.svg' -import elasticsearchIcon from '@/ui/svg/icons/mcp/elasticsearch.svg' -import everythingSearchIcon from '@/ui/svg/icons/mcp/everything-search.svg' -import filesystemIcon from '@/ui/svg/icons/mcp/filesystem.svg' -import gitIcon from '@/ui/svg/icons/mcp/git.svg' -import knowledgeGraphMemoryIcon from '@/ui/svg/icons/mcp/knowledge-graph-memory.svg' -import mysqlIcon from '@/ui/svg/icons/mcp/mysql.svg' -import oceanbaseIcon from '@/ui/svg/icons/mcp/oceanbase.svg' -import openmemoryIcon from '@/ui/svg/icons/mcp/openmemory.svg' -import officeWordIcon from '@/ui/svg/icons/mcp/office-word.svg' -import redbookSearchCommentIcon from '@/ui/svg/icons/mcp/redbook-search-comment.svg' -import puppeteerIcon from '@/ui/svg/icons/mcp/puppeteer.svg' -import playwrightIcon from '@/ui/svg/icons/mcp/playwright.svg' -import markdownifyIcon from '@/ui/svg/icons/mcp/markdownify.svg' -import redisIcon from '@/ui/svg/icons/mcp/redis.svg' -import sqliteIcon from '@/ui/svg/icons/mcp/sqlite.svg' -import stockAnalysisIcon from '@/ui/svg/icons/mcp/stock-analysis.svg' -import queryTableIcon from '@/ui/svg/icons/mcp/query-table.svg' -import unionpayIcon from '@/ui/svg/icons/mcp/unionpay.svg' -import wecomBotIcon from '@/ui/svg/icons/mcp/wecom-bot.svg' -import chatIcon from '@/ui/svg/icons/mcp/chat.svg' -import webcontentIcon from '@/ui/svg/icons/mcp/webcontent.svg' -import githubIcon from '@/ui/svg/icons/mcp/github.svg' -import notionIcon from '@/ui/svg/icons/mcp/notion.svg' -import zhipuaiIcon from '@/ui/svg/icons/mcp/zhipuai.svg' -import weatherIcon from '@/ui/svg/icons/mcp/weather.svg' +import { StaticImageData } from 'next/image'; + +import magicIcon from '@/ui/svg/icons/mcp/21dev.png'; +import one2306Icon from '@/ui/svg/icons/mcp/12306.svg'; +import academicSearchIcon from '@/ui/svg/icons/mcp/academic-search.svg'; +import alibabacloudDmsIcon from '@/ui/svg/icons/mcp/alibabacloud-dms.svg'; +import alipayIcon from '@/ui/svg/icons/mcp/alipay.svg'; +import allvoicelabIcon from '@/ui/svg/icons/mcp/allvoicelab.png'; +import amapIcon from '@/ui/svg/icons/mcp/amap.svg'; +import baiduMapIcon from '@/ui/svg/icons/mcp/baiduMap.svg'; +import bingIcon from '@/ui/svg/icons/mcp/bing.svg'; +import blenderIcon from '@/ui/svg/icons/mcp/blender.svg'; +import browserbaseIcon from '@/ui/svg/icons/mcp/browserbase.svg'; +import cfworkerIcon from '@/ui/svg/icons/mcp/cfworker.svg'; +import chatIcon from '@/ui/svg/icons/mcp/chat.svg'; +import DefaultIcon from '@/ui/svg/icons/mcp/default.svg'; +import elasticsearchIcon from '@/ui/svg/icons/mcp/elasticsearch.svg'; +import everythingSearchIcon from '@/ui/svg/icons/mcp/everything-search.svg'; +import excelIcon from '@/ui/svg/icons/mcp/excel.svg'; +import filesystemIcon from '@/ui/svg/icons/mcp/filesystem.svg'; +import firecrawlIcon from '@/ui/svg/icons/mcp/firecrawl.svg'; +import flomoIcon from '@/ui/svg/icons/mcp/flomo.svg'; +import gezheIcon from '@/ui/svg/icons/mcp/gezhe.svg'; +import gitIcon from '@/ui/svg/icons/mcp/git.svg'; +import githubIcon from '@/ui/svg/icons/mcp/github.svg'; +import hefengIcon from '@/ui/svg/icons/mcp/hefeng.svg'; +import howtocookIcon from '@/ui/svg/icons/mcp/howtocook.svg'; +import jinaIcon from '@/ui/svg/icons/mcp/jina.svg'; +import knowledgeGraphMemoryIcon from '@/ui/svg/icons/mcp/knowledge-graph-memory.svg'; +import markdownifyIcon from '@/ui/svg/icons/mcp/markdownify.svg'; +import mysqlIcon from '@/ui/svg/icons/mcp/mysql.svg'; +import notionIcon from '@/ui/svg/icons/mcp/notion.svg'; +import oceanbaseIcon from '@/ui/svg/icons/mcp/oceanbase.svg'; +import officeWordIcon from '@/ui/svg/icons/mcp/office-word.svg'; +import openmemoryIcon from '@/ui/svg/icons/mcp/openmemory.svg'; +import playwrightIcon from '@/ui/svg/icons/mcp/playwright.svg'; +import puppeteerIcon from '@/ui/svg/icons/mcp/puppeteer.svg'; +import queryTableIcon from '@/ui/svg/icons/mcp/query-table.svg'; +import redbookSearchCommentIcon from '@/ui/svg/icons/mcp/redbook-search-comment.svg'; +import redisIcon from '@/ui/svg/icons/mcp/redis.svg'; +import searchIcon from '@/ui/svg/icons/mcp/search.svg'; +import sqliteIcon from '@/ui/svg/icons/mcp/sqlite.svg'; +import stockAnalysisIcon from '@/ui/svg/icons/mcp/stock-analysis.svg'; +import tavilyIcon from '@/ui/svg/icons/mcp/tavily.svg'; +import timeIcon from '@/ui/svg/icons/mcp/time.svg'; +import unionpayIcon from '@/ui/svg/icons/mcp/unionpay.svg'; +import vis from '@/ui/svg/icons/mcp/vis.svg'; +import weatherIcon from '@/ui/svg/icons/mcp/weather.svg'; +import webcontentIcon from '@/ui/svg/icons/mcp/webcontent.svg'; +import wecomBotIcon from '@/ui/svg/icons/mcp/wecom-bot.svg'; +import zhipuaiIcon from '@/ui/svg/icons/mcp/zhipuai.svg'; export const mcpIcons = { default: DefaultIcon, @@ -100,9 +101,9 @@ export const mcpIcons = { notion: notionIcon, zhipuai: zhipuaiIcon, weather: weatherIcon, -} +}; export const getMcpIcon = (mcpId: string): StaticImageData => { - const icon = mcpIcons[mcpId as keyof typeof mcpIcons] || mcpIcons['default'] - return icon -} + const icon = mcpIcons[mcpId as keyof typeof mcpIcons] || mcpIcons['default']; + return icon; +}; diff --git a/frontend/providers/aiproxy/ui/icons/mode-icons/index.tsx b/frontend/providers/aiproxy/ui/icons/mode-icons/index.tsx index efdc18076..73dcc539e 100644 --- a/frontend/providers/aiproxy/ui/icons/mode-icons/index.tsx +++ b/frontend/providers/aiproxy/ui/icons/mode-icons/index.tsx @@ -1,36 +1,35 @@ -import OpenAIIcon from '@/ui/svg/icons/modelist/openai.svg' -import QwenIcon from '@/ui/svg/icons/modelist/qianwen.svg' -import ChatglmIcon from '@/ui/svg/icons/modelist/chatglm.svg' -import DeepseekIcon from '@/ui/svg/icons/modelist/deepseek.svg' -import MoonshotIcon from '@/ui/svg/icons/modelist/moonshot.svg' -import SparkdeskIcon from '@/ui/svg/icons/modelist/sparkdesk.svg' -import DoubaoIcon from '@/ui/svg/icons/modelist/doubao.svg' -import BaaiIcon from '@/ui/svg/icons/modelist/baai.svg' -import HunyuanIcon from '@/ui/svg/icons/modelist/hunyuan.svg' -import MiniMaxIcon from '@/ui/svg/icons/modelist/minimax.svg' -import BaiduIcon from '@/ui/svg/icons/modelist/baidu.svg' -import GoogleGeminiIcon from '@/ui/svg/icons/modelist/google.svg' -import AlibabaIcon from '@/ui/svg/icons/modelist/alibaba.svg' -import FishAudioIcon from '@/ui/svg/icons/modelist/fishaudio.svg' -import StabilityAIIcon from '@/ui/svg/icons/modelist/stabilityai.svg' -import NeteaseIcon from '@/ui/svg/icons/modelist/netease.svg' -import AI360Icon from '@/ui/svg/icons/modelist/ai360.svg' -import AnthropicIcon from '@/ui/svg/icons/modelist/anthropic.svg' -import BaichuanIcon from '@/ui/svg/icons/modelist/baichuan.svg' -import MetaIcon from '@/ui/svg/icons/modelist/meta.svg' -import MistralIcon from '@/ui/svg/icons/modelist/mistral.svg' -import OpenChatIcon from '@/ui/svg/icons/modelist/openchat.svg' -import DefogIcon from '@/ui/svg/icons/modelist/defog.svg' -import NexusFlowIcon from '@/ui/svg/icons/modelist/nexusflow.svg' -import MicrosoftIcon from '@/ui/svg/icons/modelist/microsoft.svg' -import CohereIcon from '@/ui/svg/icons/modelist/cohere.svg' -import HuggingFaceIcon from '@/ui/svg/icons/modelist/huggingface.svg' -import LingyiWanwuIcon from '@/ui/svg/icons/modelist/lingyiwanwu.svg' -import StepFunIcon from '@/ui/svg/icons/modelist/stepfun.svg' -import DefaultIcon from '@/ui/svg/icons/modelist/default.svg' - -import AbabIcon from '@/ui/svg/icons/modelist/minimax.svg' -import ErnieIcon from '@/ui/svg/icons/modelist/ernie.svg' +import AI360Icon from '@/ui/svg/icons/modelist/ai360.svg'; +import AlibabaIcon from '@/ui/svg/icons/modelist/alibaba.svg'; +import AnthropicIcon from '@/ui/svg/icons/modelist/anthropic.svg'; +import BaaiIcon from '@/ui/svg/icons/modelist/baai.svg'; +import BaichuanIcon from '@/ui/svg/icons/modelist/baichuan.svg'; +import BaiduIcon from '@/ui/svg/icons/modelist/baidu.svg'; +import ChatglmIcon from '@/ui/svg/icons/modelist/chatglm.svg'; +import CohereIcon from '@/ui/svg/icons/modelist/cohere.svg'; +import DeepseekIcon from '@/ui/svg/icons/modelist/deepseek.svg'; +import DefaultIcon from '@/ui/svg/icons/modelist/default.svg'; +import DefogIcon from '@/ui/svg/icons/modelist/defog.svg'; +import DoubaoIcon from '@/ui/svg/icons/modelist/doubao.svg'; +import ErnieIcon from '@/ui/svg/icons/modelist/ernie.svg'; +import FishAudioIcon from '@/ui/svg/icons/modelist/fishaudio.svg'; +import GoogleGeminiIcon from '@/ui/svg/icons/modelist/google.svg'; +import HuggingFaceIcon from '@/ui/svg/icons/modelist/huggingface.svg'; +import HunyuanIcon from '@/ui/svg/icons/modelist/hunyuan.svg'; +import LingyiWanwuIcon from '@/ui/svg/icons/modelist/lingyiwanwu.svg'; +import MetaIcon from '@/ui/svg/icons/modelist/meta.svg'; +import MicrosoftIcon from '@/ui/svg/icons/modelist/microsoft.svg'; +import MiniMaxIcon from '@/ui/svg/icons/modelist/minimax.svg'; +import AbabIcon from '@/ui/svg/icons/modelist/minimax.svg'; +import MistralIcon from '@/ui/svg/icons/modelist/mistral.svg'; +import MoonshotIcon from '@/ui/svg/icons/modelist/moonshot.svg'; +import NeteaseIcon from '@/ui/svg/icons/modelist/netease.svg'; +import NexusFlowIcon from '@/ui/svg/icons/modelist/nexusflow.svg'; +import OpenAIIcon from '@/ui/svg/icons/modelist/openai.svg'; +import OpenChatIcon from '@/ui/svg/icons/modelist/openchat.svg'; +import QwenIcon from '@/ui/svg/icons/modelist/qianwen.svg'; +import SparkdeskIcon from '@/ui/svg/icons/modelist/sparkdesk.svg'; +import StabilityAIIcon from '@/ui/svg/icons/modelist/stabilityai.svg'; +import StepFunIcon from '@/ui/svg/icons/modelist/stepfun.svg'; export const modelIcons = { openai: OpenAIIcon, @@ -63,4 +62,4 @@ export const modelIcons = { lingyiwanwu: LingyiWanwuIcon, stepfun: StepFunIcon, default: DefaultIcon, -} +};