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 * as React from "react"
|
||||||
import {
|
import {
|
||||||
IconAdjustments,
|
IconAdjustments,
|
||||||
IconBrain,
|
|
||||||
IconPalette,
|
IconPalette,
|
||||||
IconPlug,
|
IconPlug,
|
||||||
IconPuzzle,
|
|
||||||
IconRobot,
|
IconRobot,
|
||||||
IconTerminal2,
|
|
||||||
IconUsers,
|
IconUsers,
|
||||||
} from "@tabler/icons-react"
|
} from "@tabler/icons-react"
|
||||||
|
|
||||||
import { useIsMobile } from "@/hooks/use-mobile"
|
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 {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@ -24,14 +17,13 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select"
|
} from "@/components/ui/select"
|
||||||
|
import { Separator } from "@/components/ui/separator"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
import { PreferencesTab } from "@/components/settings/preferences-tab"
|
import { PreferencesTab } from "@/components/settings/preferences-tab"
|
||||||
import { AppearanceTab } from "@/components/settings/appearance-tab"
|
import { AppearanceTab } from "@/components/settings/appearance-tab"
|
||||||
import { TeamTab } from "@/components/settings/team-tab"
|
import { TeamTab } from "@/components/settings/team-tab"
|
||||||
import { AIModelTab } from "@/components/settings/ai-model-tab"
|
import { AgentTab } from "@/components/settings/agent-tab"
|
||||||
import { SkillsTab } from "@/components/settings/skills-tab"
|
|
||||||
import { ClaudeCodeTab } from "@/components/settings/claude-code-tab"
|
|
||||||
import { NetSuiteConnectionStatus } from "@/components/netsuite/connection-status"
|
import { NetSuiteConnectionStatus } from "@/components/netsuite/connection-status"
|
||||||
import { SyncControls } from "@/components/netsuite/sync-controls"
|
import { SyncControls } from "@/components/netsuite/sync-controls"
|
||||||
import { GoogleDriveConnectionStatus } from "@/components/google/connection-status"
|
import { GoogleDriveConnectionStatus } from "@/components/google/connection-status"
|
||||||
@ -40,46 +32,17 @@ const SETTINGS_TABS = [
|
|||||||
{ value: "preferences", label: "Preferences", icon: IconAdjustments },
|
{ value: "preferences", label: "Preferences", icon: IconAdjustments },
|
||||||
{ value: "appearance", label: "Theme", icon: IconPalette },
|
{ value: "appearance", label: "Theme", icon: IconPalette },
|
||||||
{ value: "team", label: "Team", icon: IconUsers },
|
{ value: "team", label: "Team", icon: IconUsers },
|
||||||
{ value: "ai-model", label: "AI Model", icon: IconBrain },
|
|
||||||
{ value: "agent", label: "Agent", icon: IconRobot },
|
{ value: "agent", label: "Agent", icon: IconRobot },
|
||||||
{ value: "skills", label: "Skills", icon: IconPuzzle },
|
|
||||||
{ value: "integrations", label: "Integrations", icon: IconPlug },
|
{ value: "integrations", label: "Integrations", icon: IconPlug },
|
||||||
{ value: "claude-code", label: "Code Bridge", icon: IconTerminal2 },
|
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
type SectionValue = (typeof SETTINGS_TABS)[number]["value"]
|
type SectionValue = (typeof SETTINGS_TABS)[number]["value"]
|
||||||
|
|
||||||
// wide sections get unconstrained width for tables/complex layouts
|
// wide sections get unconstrained width for tables/complex layouts
|
||||||
const WIDE_SECTIONS = new Set<string>([
|
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() {
|
function IntegrationsSection() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@ -106,16 +69,10 @@ export default function SettingsPage() {
|
|||||||
return <AppearanceTab />
|
return <AppearanceTab />
|
||||||
case "team":
|
case "team":
|
||||||
return <TeamTab />
|
return <TeamTab />
|
||||||
case "ai-model":
|
|
||||||
return <AIModelTab />
|
|
||||||
case "agent":
|
case "agent":
|
||||||
return <AgentSection />
|
return <AgentTab />
|
||||||
case "skills":
|
|
||||||
return <SkillsTab />
|
|
||||||
case "integrations":
|
case "integrations":
|
||||||
return <IntegrationsSection />
|
return <IntegrationsSection />
|
||||||
case "claude-code":
|
|
||||||
return <ClaudeCodeTab />
|
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,10 +29,8 @@ import { Textarea } from "@/components/ui/textarea"
|
|||||||
import { NetSuiteConnectionStatus } from "@/components/netsuite/connection-status"
|
import { NetSuiteConnectionStatus } from "@/components/netsuite/connection-status"
|
||||||
import { SyncControls } from "@/components/netsuite/sync-controls"
|
import { SyncControls } from "@/components/netsuite/sync-controls"
|
||||||
import { GoogleDriveConnectionStatus } from "@/components/google/connection-status"
|
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 { 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 { TeamTab } from "@/components/settings/team-tab"
|
||||||
import { useNative } from "@/hooks/use-native"
|
import { useNative } from "@/hooks/use-native"
|
||||||
import { useBiometricAuth } from "@/hooks/use-biometric-auth"
|
import { useBiometricAuth } from "@/hooks/use-biometric-auth"
|
||||||
@ -44,9 +42,7 @@ const SETTINGS_TABS = [
|
|||||||
{ value: "notifications", label: "Notifications" },
|
{ value: "notifications", label: "Notifications" },
|
||||||
{ value: "appearance", label: "Theme" },
|
{ value: "appearance", label: "Theme" },
|
||||||
{ value: "integrations", label: "Integrations" },
|
{ value: "integrations", label: "Integrations" },
|
||||||
{ value: "ai-model", label: "AI Model" },
|
|
||||||
{ value: "agent", label: "Agent" },
|
{ value: "agent", label: "Agent" },
|
||||||
{ value: "skills", label: "Skills" },
|
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
const CREATE_SETTING_TAB = {
|
const CREATE_SETTING_TAB = {
|
||||||
@ -91,7 +87,6 @@ export function SettingsModal({
|
|||||||
const [pushNotifs, setPushNotifs] = React.useState(true)
|
const [pushNotifs, setPushNotifs] = React.useState(true)
|
||||||
const [weeklyDigest, setWeeklyDigest] = React.useState(false)
|
const [weeklyDigest, setWeeklyDigest] = React.useState(false)
|
||||||
const [timezone, setTimezone] = React.useState("America/New_York")
|
const [timezone, setTimezone] = React.useState("America/New_York")
|
||||||
const [signetId, setSignetId] = React.useState("")
|
|
||||||
const [customTabs, setCustomTabs] = React.useState<ReadonlyArray<CustomSettingTab>>([])
|
const [customTabs, setCustomTabs] = React.useState<ReadonlyArray<CustomSettingTab>>([])
|
||||||
const [activeTab, setActiveTab] = React.useState<string>("general")
|
const [activeTab, setActiveTab] = React.useState<string>("general")
|
||||||
const [newSettingName, setNewSettingName] = React.useState("")
|
const [newSettingName, setNewSettingName] = React.useState("")
|
||||||
@ -263,41 +258,11 @@ export function SettingsModal({
|
|||||||
<Separator />
|
<Separator />
|
||||||
<NetSuiteConnectionStatus />
|
<NetSuiteConnectionStatus />
|
||||||
<SyncControls />
|
<SyncControls />
|
||||||
<Separator />
|
|
||||||
<ClaudeCodeTab />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
case "ai-model":
|
|
||||||
return <div className="pt-2"><AIModelTab /></div>
|
|
||||||
|
|
||||||
case "agent":
|
case "agent":
|
||||||
return (
|
return <div className="flex min-h-0 flex-1 pt-2"><AgentTab /></div>
|
||||||
<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>
|
|
||||||
|
|
||||||
case CREATE_SETTING_TAB.value:
|
case CREATE_SETTING_TAB.value:
|
||||||
return (
|
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