jan/web-app/src/containers/DropdownToolsAvailable.tsx
Sam Hoang Van c32dd092d0
Enhance i18n and add missing i18n for all component (#5314)
* Refactor translation imports and update text for localization across settings and system monitor routes

- Changed translation import from 'react-i18next' to '@/i18n/react-i18next-compat' in multiple files.
- Updated various text strings to use translation keys for better localization support in:
  - Local API Server settings
  - MCP Servers settings
  - Privacy settings
  - Provider settings
  - Shortcuts settings
  - System Monitor
  - Thread details
- Ensured consistent use of translation keys for all user-facing text.

Update web-app/src/routes/settings/appearance.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

Update web-app/src/routes/settings/appearance.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

Update web-app/src/locales/vn/settings.json

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

Update web-app/src/containers/dialogs/DeleteMCPServerConfirm.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

Update web-app/src/locales/id/common.json

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

* Add Chinese (Simplified and Traditional) localization files for various components

- Created `tools.json`, `updater.json`, `assistants.json`, `chat.json`, `common.json`, `hub.json`, `logs.json`, `mcp-servers.json`, `provider.json`, `providers.json`, `settings.json`, `setup.json`, `system-monitor.json`, `tool-approval.json` in both `zh-CN` and `zh-TW` locales.
- Added translations for tool approval, updater notifications, assistant management, chat interface, common UI elements, hub interactions, logging messages, MCP server configurations, provider management, settings options, setup instructions, and system monitoring.

* Refactor localization strings for improved clarity and consistency in English, Indonesian, and Vietnamese settings files

* Fix missing key and reword

* fix pr comment

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-06-20 15:33:54 +07:00

160 lines
5.2 KiB
TypeScript

import { useEffect, useState } from 'react'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Switch } from '@/components/ui/switch'
import { useThreads } from '@/hooks/useThreads'
import { useToolAvailable } from '@/hooks/useToolAvailable'
import React from 'react'
import { useAppState } from '@/hooks/useAppState'
import { useTranslation } from '@/i18n/react-i18next-compat'
interface DropdownToolsAvailableProps {
children: (isOpen: boolean, toolsCount: number) => React.ReactNode
initialMessage?: boolean
onOpenChange?: (isOpen: boolean) => void
}
export default function DropdownToolsAvailable({
children,
initialMessage = false,
onOpenChange,
}: DropdownToolsAvailableProps) {
const { tools } = useAppState()
const [isOpen, setIsOpen] = useState(false)
const { t } = useTranslation()
const handleOpenChange = (open: boolean) => {
setIsOpen(open)
onOpenChange?.(open)
}
const { getCurrentThread } = useThreads()
const {
isToolDisabled,
setToolDisabledForThread,
setDefaultDisabledTools,
initializeThreadTools,
getDisabledToolsForThread,
getDefaultDisabledTools,
} = useToolAvailable()
const currentThread = getCurrentThread()
// Separate effect for thread initialization - only when we have tools and a new thread
useEffect(() => {
if (tools.length > 0 && currentThread?.id) {
initializeThreadTools(currentThread.id, tools)
}
}, [currentThread?.id, tools, initializeThreadTools])
const handleToolToggle = (toolName: string, checked: boolean) => {
if (initialMessage) {
// Update default tools for new threads/index page
const currentDefaults = getDefaultDisabledTools()
if (checked) {
setDefaultDisabledTools(
currentDefaults.filter((name) => name !== toolName)
)
} else {
setDefaultDisabledTools([...currentDefaults, toolName])
}
} else if (currentThread?.id) {
// Update tools for specific thread
setToolDisabledForThread(currentThread.id, toolName, checked)
}
}
const isToolChecked = (toolName: string): boolean => {
if (initialMessage) {
// Use default tools for index page
return !getDefaultDisabledTools().includes(toolName)
} else if (currentThread?.id) {
// Use thread-specific tools
return !isToolDisabled(currentThread.id, toolName)
}
return false
}
const getEnabledToolsCount = (): number => {
const disabledTools = initialMessage
? getDefaultDisabledTools()
: currentThread?.id
? getDisabledToolsForThread(currentThread.id)
: []
return tools.filter((tool) => !disabledTools.includes(tool.name)).length
}
const renderTrigger = () => children(isOpen, getEnabledToolsCount())
if (tools.length === 0) {
return (
<DropdownMenu onOpenChange={handleOpenChange}>
<DropdownMenuTrigger asChild>{renderTrigger()}</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="max-w-64">
<DropdownMenuItem disabled>{t('common:noToolsAvailable')}</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
return (
<DropdownMenu onOpenChange={handleOpenChange}>
<DropdownMenuTrigger asChild>{renderTrigger()}</DropdownMenuTrigger>
<DropdownMenuContent
side="top"
align="start"
className="max-w-64 backdrop-blur-xl bg-main-view"
>
<DropdownMenuLabel className="flex items-center gap-2 sticky -top-1 z-10 px-4 pl-2 py-2 ">
Available Tools
</DropdownMenuLabel>
<DropdownMenuSeparator />
<div className="max-h-64 overflow-y-auto">
{tools.map((tool) => {
const isChecked = isToolChecked(tool.name)
return (
<div
key={tool.name}
className="py-2 hover:bg-main-view-fg/5 hover:backdrop-blur-2xl rounded-sm px-2 mx-auto w-full"
>
<div className="flex items-start justify-center gap-3">
<div className="flex items-start justify-between gap-4 w-full">
<div className="overflow-hidden w-full flex flex-col ">
<div className="truncate">
<span className="text-sm font-medium" title={tool.name}>
{tool.name}
</span>
</div>
{tool.description && (
<p className="text-xs text-main-view-fg/70 mt-1 line-clamp-2">
{tool.description}
</p>
)}
</div>
<div className="shrink-0 mx-auto">
<Switch
checked={isChecked}
onCheckedChange={(checked) =>
handleToolToggle(tool.name, checked)
}
/>
</div>
</div>
</div>
</div>
)
})}
</div>
</DropdownMenuContent>
</DropdownMenu>
)
}