mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(ui): consolidate buttons — 3 variants (primary/secondary/tertiary) + danger tone (#4057)
This commit is contained in:
@@ -10,6 +10,8 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useT } from '../../../lib/i18n/I18nContext';
|
||||
import { mcpClientsApi } from '../../../services/api/mcpClientsApi';
|
||||
import ChipTabs from '../../layout/ChipTabs';
|
||||
import Button from '../../ui/Button';
|
||||
import InstallDialog from './InstallDialog';
|
||||
import InstalledServerDetail from './InstalledServerDetail';
|
||||
import McpInventoryPanel from './McpInventoryPanel';
|
||||
@@ -225,20 +227,22 @@ const McpServersTab = () => {
|
||||
if (view.mode === 'detail' && selectedServer) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
variant="tertiary"
|
||||
size="xs"
|
||||
onClick={() => setView({ mode: 'home' })}
|
||||
className="inline-flex items-center gap-1.5 text-xs font-medium text-stone-500 dark:text-neutral-400 hover:text-stone-700 dark:hover:text-neutral-200 transition-colors">
|
||||
<svg
|
||||
className="w-3.5 h-3.5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
leadingIcon={
|
||||
<svg
|
||||
className="w-3.5 h-3.5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
}>
|
||||
{t('mcp.install.back')}
|
||||
</button>
|
||||
</Button>
|
||||
<InstalledServerDetail
|
||||
server={selectedServer}
|
||||
connStatus={selectedConnStatus}
|
||||
@@ -253,20 +257,22 @@ const McpServersTab = () => {
|
||||
if (view.mode === 'install') {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
variant="tertiary"
|
||||
size="xs"
|
||||
onClick={() => setView({ mode: 'home' })}
|
||||
className="inline-flex items-center gap-1.5 text-xs font-medium text-stone-500 dark:text-neutral-400 hover:text-stone-700 dark:hover:text-neutral-200 transition-colors">
|
||||
<svg
|
||||
className="w-3.5 h-3.5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
leadingIcon={
|
||||
<svg
|
||||
className="w-3.5 h-3.5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
}>
|
||||
{t('mcp.install.back')}
|
||||
</button>
|
||||
</Button>
|
||||
<InstallDialog
|
||||
qualifiedName={view.qualifiedName}
|
||||
prefillEnv={view.prefillEnv}
|
||||
@@ -290,34 +296,33 @@ const McpServersTab = () => {
|
||||
aria-label={t('mcp.catalog.searchAria')}
|
||||
className="flex-1 rounded-lg border border-stone-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 px-3 py-2 text-sm text-stone-800 dark:text-neutral-100 placeholder:text-stone-400 dark:placeholder:text-neutral-500 focus:outline-none focus:ring-2 focus:ring-primary-500/40"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="md"
|
||||
onClick={() => setInventoryOpen(true)}
|
||||
aria-label={t('mcp.inventory.openAria')}
|
||||
className="shrink-0 rounded-lg border border-stone-200 dark:border-neutral-700 px-3 py-2 text-xs font-medium text-stone-600 dark:text-neutral-300 hover:bg-stone-50 dark:hover:bg-neutral-800">
|
||||
className="shrink-0">
|
||||
{t('mcp.inventory.openButton')}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Filter chips */}
|
||||
<div className="flex items-center gap-2">
|
||||
{(['all', 'installed', 'registry'] as FilterChip[]).map(chip => (
|
||||
<button
|
||||
key={chip}
|
||||
type="button"
|
||||
onClick={() => setActiveChip(chip)}
|
||||
className={`rounded-full px-3 py-1 text-xs font-medium transition-colors ${
|
||||
activeChip === chip
|
||||
? 'bg-primary-500 text-white'
|
||||
: 'bg-stone-100 dark:bg-neutral-800 text-stone-600 dark:text-neutral-300 hover:bg-stone-200 dark:hover:bg-neutral-700'
|
||||
}`}>
|
||||
{chip === 'all' && t('mcp.tab.filter.all')}
|
||||
{chip === 'installed' &&
|
||||
t('mcp.tab.filter.installed').replace('{count}', String(filteredInstalled.length))}
|
||||
{chip === 'registry' && t('mcp.tab.filter.registry')}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<ChipTabs<FilterChip>
|
||||
className="flex flex-wrap items-center gap-2"
|
||||
value={activeChip}
|
||||
onChange={setActiveChip}
|
||||
items={[
|
||||
{ id: 'all', label: t('mcp.tab.filter.all') },
|
||||
{
|
||||
id: 'installed',
|
||||
label: t('mcp.tab.filter.installed').replace(
|
||||
'{count}',
|
||||
String(filteredInstalled.length)
|
||||
),
|
||||
},
|
||||
{ id: 'registry', label: t('mcp.tab.filter.registry') },
|
||||
]}
|
||||
/>
|
||||
|
||||
{loadError && (
|
||||
<div className="rounded-lg border border-coral-200 dark:border-coral-500/30 bg-coral-50 dark:bg-coral-500/10 px-3 py-2 text-xs text-coral-700 dark:text-coral-300">
|
||||
@@ -495,12 +500,13 @@ const McpServersTab = () => {
|
||||
catalogPage < catalogTotalPages &&
|
||||
(activeChip === 'all' || activeChip === 'registry') && (
|
||||
<div className="py-3 text-center border-t border-stone-100 dark:border-neutral-800">
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
variant="tertiary"
|
||||
size="xs"
|
||||
onClick={handleLoadMore}
|
||||
className="text-xs font-medium text-primary-600 dark:text-primary-400 hover:underline">
|
||||
className="text-primary-600 dark:text-primary-400 hover:underline">
|
||||
{t('mcp.catalog.loadMore')}
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user