refactor(settings): unify agent tabs into single Agent section
Consolidate AI Model, Skills, Code Bridge, and Identity into one Agent tab with pill-style sub-navigation. Reduces top-level settings from 8 tabs to 5 (page) / 6 (modal).
This commit is contained in:
parent
7f5efb84e2
commit
feb7dc2643
@ -3,20 +3,13 @@
|
||||
import * as React from "react"
|
||||
import {
|
||||
IconAdjustments,
|
||||
IconBrain,
|
||||
IconPalette,
|
||||
IconPlug,
|
||||
IconPuzzle,
|
||||
IconRobot,
|
||||
IconTerminal2,
|
||||
IconUsers,
|
||||
} from "@tabler/icons-react"
|
||||
|
||||
import { useIsMobile } from "@/hooks/use-mobile"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@ -24,14 +17,13 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
import { PreferencesTab } from "@/components/settings/preferences-tab"
|
||||
import { AppearanceTab } from "@/components/settings/appearance-tab"
|
||||
import { TeamTab } from "@/components/settings/team-tab"
|
||||
import { AIModelTab } from "@/components/settings/ai-model-tab"
|
||||
import { SkillsTab } from "@/components/settings/skills-tab"
|
||||
import { ClaudeCodeTab } from "@/components/settings/claude-code-tab"
|
||||
import { AgentTab } from "@/components/settings/agent-tab"
|
||||
import { NetSuiteConnectionStatus } from "@/components/netsuite/connection-status"
|
||||
import { SyncControls } from "@/components/netsuite/sync-controls"
|
||||
import { GoogleDriveConnectionStatus } from "@/components/google/connection-status"
|
||||
@ -40,46 +32,17 @@ const SETTINGS_TABS = [
|
||||
{ value: "preferences", label: "Preferences", icon: IconAdjustments },
|
||||
{ value: "appearance", label: "Theme", icon: IconPalette },
|
||||
{ value: "team", label: "Team", icon: IconUsers },
|
||||
{ value: "ai-model", label: "AI Model", icon: IconBrain },
|
||||
{ value: "agent", label: "Agent", icon: IconRobot },
|
||||
{ value: "skills", label: "Skills", icon: IconPuzzle },
|
||||
{ value: "integrations", label: "Integrations", icon: IconPlug },
|
||||
{ value: "claude-code", label: "Code Bridge", icon: IconTerminal2 },
|
||||
] as const
|
||||
|
||||
type SectionValue = (typeof SETTINGS_TABS)[number]["value"]
|
||||
|
||||
// wide sections get unconstrained width for tables/complex layouts
|
||||
const WIDE_SECTIONS = new Set<string>([
|
||||
"appearance", "team", "ai-model", "claude-code",
|
||||
"appearance", "team", "agent",
|
||||
])
|
||||
|
||||
function AgentSection() {
|
||||
const [signetId, setSignetId] = React.useState("")
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="signet-id" className="text-xs">
|
||||
Signet ID (ETH)
|
||||
</Label>
|
||||
<Input
|
||||
id="signet-id"
|
||||
value={signetId}
|
||||
onChange={(e) => setSignetId(e.target.value)}
|
||||
placeholder="0x..."
|
||||
className="h-9 max-w-sm font-mono"
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
<Separator />
|
||||
<Button className="w-full max-w-sm">
|
||||
Configure your agent
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function IntegrationsSection() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
@ -106,16 +69,10 @@ export default function SettingsPage() {
|
||||
return <AppearanceTab />
|
||||
case "team":
|
||||
return <TeamTab />
|
||||
case "ai-model":
|
||||
return <AIModelTab />
|
||||
case "agent":
|
||||
return <AgentSection />
|
||||
case "skills":
|
||||
return <SkillsTab />
|
||||
return <AgentTab />
|
||||
case "integrations":
|
||||
return <IntegrationsSection />
|
||||
case "claude-code":
|
||||
return <ClaudeCodeTab />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
@ -29,10 +29,8 @@ import { Textarea } from "@/components/ui/textarea"
|
||||
import { NetSuiteConnectionStatus } from "@/components/netsuite/connection-status"
|
||||
import { SyncControls } from "@/components/netsuite/sync-controls"
|
||||
import { GoogleDriveConnectionStatus } from "@/components/google/connection-status"
|
||||
import { SkillsTab } from "@/components/settings/skills-tab"
|
||||
import { AIModelTab } from "@/components/settings/ai-model-tab"
|
||||
import { AppearanceTab } from "@/components/settings/appearance-tab"
|
||||
import { ClaudeCodeTab } from "@/components/settings/claude-code-tab"
|
||||
import { AgentTab } from "@/components/settings/agent-tab"
|
||||
import { TeamTab } from "@/components/settings/team-tab"
|
||||
import { useNative } from "@/hooks/use-native"
|
||||
import { useBiometricAuth } from "@/hooks/use-biometric-auth"
|
||||
@ -44,9 +42,7 @@ const SETTINGS_TABS = [
|
||||
{ value: "notifications", label: "Notifications" },
|
||||
{ value: "appearance", label: "Theme" },
|
||||
{ value: "integrations", label: "Integrations" },
|
||||
{ value: "ai-model", label: "AI Model" },
|
||||
{ value: "agent", label: "Agent" },
|
||||
{ value: "skills", label: "Skills" },
|
||||
] as const
|
||||
|
||||
const CREATE_SETTING_TAB = {
|
||||
@ -91,7 +87,6 @@ export function SettingsModal({
|
||||
const [pushNotifs, setPushNotifs] = React.useState(true)
|
||||
const [weeklyDigest, setWeeklyDigest] = React.useState(false)
|
||||
const [timezone, setTimezone] = React.useState("America/New_York")
|
||||
const [signetId, setSignetId] = React.useState("")
|
||||
const [customTabs, setCustomTabs] = React.useState<ReadonlyArray<CustomSettingTab>>([])
|
||||
const [activeTab, setActiveTab] = React.useState<string>("general")
|
||||
const [newSettingName, setNewSettingName] = React.useState("")
|
||||
@ -263,41 +258,11 @@ export function SettingsModal({
|
||||
<Separator />
|
||||
<NetSuiteConnectionStatus />
|
||||
<SyncControls />
|
||||
<Separator />
|
||||
<ClaudeCodeTab />
|
||||
</div>
|
||||
)
|
||||
|
||||
case "ai-model":
|
||||
return <div className="pt-2"><AIModelTab /></div>
|
||||
|
||||
case "agent":
|
||||
return (
|
||||
<div className="space-y-4 pt-2">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="signet-id" className="text-xs">
|
||||
Signet ID (ETH)
|
||||
</Label>
|
||||
<Input
|
||||
id="signet-id"
|
||||
value={signetId}
|
||||
onChange={(e) => setSignetId(e.target.value)}
|
||||
placeholder="0x..."
|
||||
className="h-9 font-mono"
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<Button className="w-full">
|
||||
Configure your agent
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
case "skills":
|
||||
return <div className="pt-2"><SkillsTab /></div>
|
||||
return <div className="flex min-h-0 flex-1 pt-2"><AgentTab /></div>
|
||||
|
||||
case CREATE_SETTING_TAB.value:
|
||||
return (
|
||||
|
||||
96
src/components/settings/agent-tab.tsx
Normal file
96
src/components/settings/agent-tab.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import {
|
||||
BrainCircuit,
|
||||
Blocks,
|
||||
Cable,
|
||||
Fingerprint,
|
||||
} from "lucide-react"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { AIModelTab } from "@/components/settings/ai-model-tab"
|
||||
import { SkillsTab } from "@/components/settings/skills-tab"
|
||||
import { ClaudeCodeTab } from "@/components/settings/claude-code-tab"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const SUB_TABS = [
|
||||
{ value: "model", label: "Model", icon: BrainCircuit },
|
||||
{ value: "skills", label: "Skills", icon: Blocks },
|
||||
{ value: "bridge", label: "Bridge", icon: Cable },
|
||||
{ value: "identity", label: "Identity", icon: Fingerprint },
|
||||
] as const
|
||||
|
||||
type SubTab = (typeof SUB_TABS)[number]["value"]
|
||||
|
||||
export function AgentTab(): React.ReactElement {
|
||||
const [activeSubTab, setActiveSubTab] =
|
||||
React.useState<SubTab>("model")
|
||||
const [signetId, setSignetId] = React.useState("")
|
||||
|
||||
return (
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-4">
|
||||
<div
|
||||
className="flex gap-1.5"
|
||||
role="radiogroup"
|
||||
aria-label="Agent settings"
|
||||
>
|
||||
{SUB_TABS.map((tab) => {
|
||||
const isActive = activeSubTab === tab.value
|
||||
const Icon = tab.icon
|
||||
return (
|
||||
<button
|
||||
key={tab.value}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={isActive}
|
||||
onClick={() => setActiveSubTab(tab.value)}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 rounded-full px-3 py-1.5",
|
||||
"text-xs font-medium transition-all",
|
||||
isActive
|
||||
? "bg-primary/10 text-primary ring-1 ring-primary/20"
|
||||
: "text-muted-foreground hover:bg-muted/70"
|
||||
)}
|
||||
>
|
||||
<Icon className="size-3.5" />
|
||||
<span>{tab.label}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-y-auto">
|
||||
{activeSubTab === "model" && <AIModelTab />}
|
||||
{activeSubTab === "skills" && <SkillsTab />}
|
||||
{activeSubTab === "bridge" && <ClaudeCodeTab />}
|
||||
{activeSubTab === "identity" && (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="signet-id" className="text-xs">
|
||||
Signet ID (ETH)
|
||||
</Label>
|
||||
<Input
|
||||
id="signet-id"
|
||||
value={signetId}
|
||||
onChange={(e) => setSignetId(e.target.value)}
|
||||
placeholder="0x..."
|
||||
className="h-9 font-mono"
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<Button className="w-full">
|
||||
Configure your agent
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user