mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
feat: Make input controls and settings responsive for mobile devices
This commit is contained in:
@@ -79,7 +79,7 @@ export function InputControls({
|
|||||||
{/* Text Input and Controls */}
|
{/* Text Input and Controls */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex gap-2 transition-all duration-200 rounded-xl p-1',
|
'flex flex-col gap-2 transition-all duration-200 rounded-xl p-1',
|
||||||
isDragOver && 'bg-primary/5 ring-2 ring-primary/30'
|
isDragOver && 'bg-primary/5 ring-2 ring-primary/30'
|
||||||
)}
|
)}
|
||||||
onDragEnter={onDragEnter}
|
onDragEnter={onDragEnter}
|
||||||
@@ -87,7 +87,8 @@ export function InputControls({
|
|||||||
onDragOver={onDragOver}
|
onDragOver={onDragOver}
|
||||||
onDrop={onDrop}
|
onDrop={onDrop}
|
||||||
>
|
>
|
||||||
<div className="flex-1 relative">
|
{/* Textarea - full width on mobile */}
|
||||||
|
<div className="relative w-full">
|
||||||
<Textarea
|
<Textarea
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={
|
placeholder={
|
||||||
@@ -105,14 +106,14 @@ export function InputControls({
|
|||||||
data-testid="agent-input"
|
data-testid="agent-input"
|
||||||
rows={1}
|
rows={1}
|
||||||
className={cn(
|
className={cn(
|
||||||
'min-h-11 bg-background border-border rounded-xl pl-4 pr-20 text-sm transition-all resize-none max-h-36 overflow-y-auto py-2.5',
|
'min-h-11 w-full bg-background border-border rounded-xl pl-4 pr-4 sm:pr-20 text-sm transition-all resize-none max-h-36 overflow-y-auto py-2.5',
|
||||||
'focus:ring-2 focus:ring-primary/20 focus:border-primary/50',
|
'focus:ring-2 focus:ring-primary/20 focus:border-primary/50',
|
||||||
hasFiles && 'border-primary/30',
|
hasFiles && 'border-primary/30',
|
||||||
isDragOver && 'border-primary bg-primary/5'
|
isDragOver && 'border-primary bg-primary/5'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{hasFiles && !isDragOver && (
|
{hasFiles && !isDragOver && (
|
||||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-xs bg-primary text-primary-foreground px-2 py-0.5 rounded-full font-medium">
|
<div className="hidden sm:block absolute right-3 top-1/2 -translate-y-1/2 text-xs bg-primary text-primary-foreground px-2 py-0.5 rounded-full font-medium">
|
||||||
files attached
|
files attached
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -124,58 +125,64 @@ export function InputControls({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Model Selector */}
|
{/* Controls row - responsive layout */}
|
||||||
<AgentModelSelector
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
value={modelSelection}
|
{/* Model Selector */}
|
||||||
onChange={onModelSelect}
|
<AgentModelSelector
|
||||||
disabled={!isConnected}
|
value={modelSelection}
|
||||||
/>
|
onChange={onModelSelect}
|
||||||
|
|
||||||
{/* File Attachment Button */}
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
|
||||||
onClick={onToggleImageDropZone}
|
|
||||||
disabled={!isConnected}
|
|
||||||
className={cn(
|
|
||||||
'h-11 w-11 rounded-xl border-border',
|
|
||||||
showImageDropZone && 'bg-primary/10 text-primary border-primary/30',
|
|
||||||
hasFiles && 'border-primary/30 text-primary'
|
|
||||||
)}
|
|
||||||
title="Attach files (images, .txt, .md)"
|
|
||||||
>
|
|
||||||
<Paperclip className="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
{/* Stop Button (only when processing) */}
|
|
||||||
{isProcessing && (
|
|
||||||
<Button
|
|
||||||
onClick={onStop}
|
|
||||||
disabled={!isConnected}
|
disabled={!isConnected}
|
||||||
className="h-11 px-4 rounded-xl"
|
/>
|
||||||
variant="destructive"
|
|
||||||
data-testid="stop-agent"
|
|
||||||
title="Stop generation"
|
|
||||||
>
|
|
||||||
<Square className="w-4 h-4 fill-current" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Send / Queue Button */}
|
{/* File Attachment Button */}
|
||||||
<Button
|
<Button
|
||||||
onClick={onSend}
|
variant="outline"
|
||||||
disabled={!canSend}
|
size="icon"
|
||||||
className="h-11 px-4 rounded-xl"
|
onClick={onToggleImageDropZone}
|
||||||
variant={isProcessing ? 'outline' : 'default'}
|
disabled={!isConnected}
|
||||||
data-testid="send-message"
|
className={cn(
|
||||||
title={isProcessing ? 'Add to queue' : 'Send message'}
|
'h-11 w-11 rounded-xl border-border shrink-0',
|
||||||
>
|
showImageDropZone && 'bg-primary/10 text-primary border-primary/30',
|
||||||
{isProcessing ? <ListOrdered className="w-4 h-4" /> : <Send className="w-4 h-4" />}
|
hasFiles && 'border-primary/30 text-primary'
|
||||||
</Button>
|
)}
|
||||||
|
title="Attach files (images, .txt, .md)"
|
||||||
|
>
|
||||||
|
<Paperclip className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Spacer to push action buttons to the right */}
|
||||||
|
<div className="flex-1" />
|
||||||
|
|
||||||
|
{/* Stop Button (only when processing) */}
|
||||||
|
{isProcessing && (
|
||||||
|
<Button
|
||||||
|
onClick={onStop}
|
||||||
|
disabled={!isConnected}
|
||||||
|
className="h-11 px-4 rounded-xl shrink-0"
|
||||||
|
variant="destructive"
|
||||||
|
data-testid="stop-agent"
|
||||||
|
title="Stop generation"
|
||||||
|
>
|
||||||
|
<Square className="w-4 h-4 fill-current" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Send / Queue Button */}
|
||||||
|
<Button
|
||||||
|
onClick={onSend}
|
||||||
|
disabled={!canSend}
|
||||||
|
className="h-11 px-4 rounded-xl shrink-0"
|
||||||
|
variant={isProcessing ? 'outline' : 'default'}
|
||||||
|
data-testid="send-message"
|
||||||
|
title={isProcessing ? 'Add to queue' : 'Send message'}
|
||||||
|
>
|
||||||
|
{isProcessing ? <ListOrdered className="w-4 h-4" /> : <Send className="w-4 h-4" />}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Keyboard hint */}
|
{/* Keyboard hint */}
|
||||||
<p className="text-[11px] text-muted-foreground mt-2 text-center">
|
<p className="text-[11px] text-muted-foreground mt-2 text-center hidden sm:block">
|
||||||
Press <kbd className="px-1.5 py-0.5 bg-muted rounded text-[10px] font-medium">Enter</kbd> to
|
Press <kbd className="px-1.5 py-0.5 bg-muted rounded text-[10px] font-medium">Enter</kbd> to
|
||||||
send,{' '}
|
send,{' '}
|
||||||
<kbd className="px-1.5 py-0.5 bg-muted rounded text-[10px] font-medium">Shift+Enter</kbd>{' '}
|
<kbd className="px-1.5 py-0.5 bg-muted rounded text-[10px] font-medium">Shift+Enter</kbd>{' '}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
|
|
||||||
@@ -30,6 +30,9 @@ import { PromptCustomizationSection } from './settings-view/prompts';
|
|||||||
import type { Project as SettingsProject, Theme } from './settings-view/shared/types';
|
import type { Project as SettingsProject, Theme } from './settings-view/shared/types';
|
||||||
import type { Project as ElectronProject } from '@/lib/electron';
|
import type { Project as ElectronProject } from '@/lib/electron';
|
||||||
|
|
||||||
|
// Breakpoint constant for mobile (matches Tailwind lg breakpoint)
|
||||||
|
const LG_BREAKPOINT = 1024;
|
||||||
|
|
||||||
export function SettingsView() {
|
export function SettingsView() {
|
||||||
const {
|
const {
|
||||||
theme,
|
theme,
|
||||||
@@ -101,6 +104,33 @@ export function SettingsView() {
|
|||||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
|
const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false);
|
||||||
|
|
||||||
|
// Mobile navigation state - default to showing on desktop, hidden on mobile
|
||||||
|
const [showNavigation, setShowNavigation] = useState(() => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
return window.innerWidth >= LG_BREAKPOINT;
|
||||||
|
}
|
||||||
|
return true; // Default to showing on SSR
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-close navigation on mobile when a section is selected
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== 'undefined' && window.innerWidth < LG_BREAKPOINT) {
|
||||||
|
setShowNavigation(false);
|
||||||
|
}
|
||||||
|
}, [activeView]);
|
||||||
|
|
||||||
|
// Handle window resize to show/hide navigation appropriately
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => {
|
||||||
|
if (window.innerWidth >= LG_BREAKPOINT) {
|
||||||
|
setShowNavigation(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('resize', handleResize);
|
||||||
|
return () => window.removeEventListener('resize', handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Render the active section based on current view
|
// Render the active section based on current view
|
||||||
const renderActiveSection = () => {
|
const renderActiveSection = () => {
|
||||||
switch (activeView) {
|
switch (activeView) {
|
||||||
@@ -187,20 +217,25 @@ export function SettingsView() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col overflow-hidden content-bg" data-testid="settings-view">
|
<div className="flex-1 flex flex-col overflow-hidden content-bg" data-testid="settings-view">
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<SettingsHeader />
|
<SettingsHeader
|
||||||
|
showNavigation={showNavigation}
|
||||||
|
onToggleNavigation={() => setShowNavigation(!showNavigation)}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Content Area with Sidebar */}
|
{/* Content Area with Sidebar */}
|
||||||
<div className="flex-1 flex overflow-hidden">
|
<div className="flex-1 flex overflow-hidden">
|
||||||
{/* Side Navigation - No longer scrolls, just switches views */}
|
{/* Side Navigation - Overlay on mobile, sidebar on desktop */}
|
||||||
<SettingsNavigation
|
<SettingsNavigation
|
||||||
navItems={NAV_ITEMS}
|
navItems={NAV_ITEMS}
|
||||||
activeSection={activeView}
|
activeSection={activeView}
|
||||||
currentProject={currentProject}
|
currentProject={currentProject}
|
||||||
onNavigate={handleNavigate}
|
onNavigate={handleNavigate}
|
||||||
|
isOpen={showNavigation}
|
||||||
|
onClose={() => setShowNavigation(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Content Panel - Shows only the active section */}
|
{/* Content Panel - Shows only the active section */}
|
||||||
<div className="flex-1 overflow-y-auto p-8">
|
<div className="flex-1 overflow-y-auto p-4 lg:p-8">
|
||||||
<div className="max-w-4xl mx-auto">{renderActiveSection()}</div>
|
<div className="max-w-4xl mx-auto">{renderActiveSection()}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import { Settings } from 'lucide-react';
|
import { Settings, PanelLeft, PanelLeftClose } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
interface SettingsHeaderProps {
|
interface SettingsHeaderProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
showNavigation?: boolean;
|
||||||
|
onToggleNavigation?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SettingsHeader({
|
export function SettingsHeader({
|
||||||
title = 'Settings',
|
title = 'Settings',
|
||||||
description = 'Configure your API keys and preferences',
|
description = 'Configure your API keys and preferences',
|
||||||
|
showNavigation,
|
||||||
|
onToggleNavigation,
|
||||||
}: SettingsHeaderProps) {
|
}: SettingsHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -18,21 +23,39 @@ export function SettingsHeader({
|
|||||||
'bg-gradient-to-r from-card/90 via-card/70 to-card/80 backdrop-blur-xl'
|
'bg-gradient-to-r from-card/90 via-card/70 to-card/80 backdrop-blur-xl'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="px-8 py-6">
|
<div className="px-4 py-4 lg:px-8 lg:py-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-3 lg:gap-4">
|
||||||
|
{/* Mobile menu toggle button - only visible on mobile */}
|
||||||
|
{onToggleNavigation && (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={onToggleNavigation}
|
||||||
|
className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground lg:hidden"
|
||||||
|
aria-label={showNavigation ? 'Close navigation menu' : 'Open navigation menu'}
|
||||||
|
>
|
||||||
|
{showNavigation ? (
|
||||||
|
<PanelLeftClose className="w-5 h-5" />
|
||||||
|
) : (
|
||||||
|
<PanelLeft className="w-5 h-5" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-12 h-12 rounded-2xl flex items-center justify-center',
|
'w-10 h-10 lg:w-12 lg:h-12 rounded-xl lg:rounded-2xl flex items-center justify-center',
|
||||||
'bg-gradient-to-br from-brand-500 to-brand-600',
|
'bg-gradient-to-br from-brand-500 to-brand-600',
|
||||||
'shadow-lg shadow-brand-500/25',
|
'shadow-lg shadow-brand-500/25',
|
||||||
'ring-1 ring-white/10'
|
'ring-1 ring-white/10'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Settings className="w-6 h-6 text-white" />
|
<Settings className="w-5 h-5 lg:w-6 lg:h-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-foreground tracking-tight">{title}</h1>
|
<h1 className="text-xl lg:text-2xl font-bold text-foreground tracking-tight">
|
||||||
<p className="text-sm text-muted-foreground/80 mt-0.5">{description}</p>
|
{title}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs lg:text-sm text-muted-foreground/80 mt-0.5">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { ChevronDown, ChevronRight } from 'lucide-react';
|
import { ChevronDown, ChevronRight, X } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import type { Project } from '@/lib/electron';
|
import type { Project } from '@/lib/electron';
|
||||||
import type { NavigationItem, NavigationGroup } from '../config/navigation';
|
import type { NavigationItem, NavigationGroup } from '../config/navigation';
|
||||||
import { GLOBAL_NAV_GROUPS, PROJECT_NAV_ITEMS } from '../config/navigation';
|
import { GLOBAL_NAV_GROUPS, PROJECT_NAV_ITEMS } from '../config/navigation';
|
||||||
@@ -13,6 +14,8 @@ interface SettingsNavigationProps {
|
|||||||
activeSection: SettingsViewId;
|
activeSection: SettingsViewId;
|
||||||
currentProject: Project | null;
|
currentProject: Project | null;
|
||||||
onNavigate: (sectionId: SettingsViewId) => void;
|
onNavigate: (sectionId: SettingsViewId) => void;
|
||||||
|
isOpen?: boolean;
|
||||||
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function NavButton({
|
function NavButton({
|
||||||
@@ -167,75 +170,115 @@ export function SettingsNavigation({
|
|||||||
activeSection,
|
activeSection,
|
||||||
currentProject,
|
currentProject,
|
||||||
onNavigate,
|
onNavigate,
|
||||||
|
isOpen = true,
|
||||||
|
onClose,
|
||||||
}: SettingsNavigationProps) {
|
}: SettingsNavigationProps) {
|
||||||
|
// On mobile, only show when isOpen is true
|
||||||
|
// On desktop (lg+), always show regardless of isOpen
|
||||||
|
const shouldShow = isOpen;
|
||||||
|
|
||||||
|
if (!shouldShow) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<>
|
||||||
className={cn(
|
{/* Mobile backdrop overlay */}
|
||||||
'hidden lg:block w-64 shrink-0 overflow-y-auto',
|
<div
|
||||||
'border-r border-border/50',
|
className="fixed inset-0 bg-black/50 z-20 lg:hidden"
|
||||||
'bg-gradient-to-b from-card/80 via-card/60 to-card/40 backdrop-blur-xl'
|
onClick={onClose}
|
||||||
)}
|
data-testid="settings-nav-backdrop"
|
||||||
>
|
/>
|
||||||
<div className="sticky top-0 p-4 space-y-1">
|
|
||||||
{/* Global Settings Groups */}
|
|
||||||
{GLOBAL_NAV_GROUPS.map((group, groupIndex) => (
|
|
||||||
<div key={group.label}>
|
|
||||||
{/* Group divider (except for first group) */}
|
|
||||||
{groupIndex > 0 && <div className="my-3 border-t border-border/50" />}
|
|
||||||
|
|
||||||
{/* Group Label */}
|
{/* Navigation sidebar */}
|
||||||
<div className="px-3 py-2 text-xs font-semibold text-muted-foreground/70 uppercase tracking-wider">
|
<nav
|
||||||
{group.label}
|
className={cn(
|
||||||
|
// Mobile: fixed position overlay
|
||||||
|
'fixed inset-y-0 left-0 w-72 z-30',
|
||||||
|
// Desktop: relative position in layout
|
||||||
|
'lg:relative lg:w-64 lg:z-auto',
|
||||||
|
'shrink-0 overflow-y-auto',
|
||||||
|
'border-r border-border/50',
|
||||||
|
'bg-gradient-to-b from-card/95 via-card/90 to-card/85 backdrop-blur-xl',
|
||||||
|
// Desktop background
|
||||||
|
'lg:from-card/80 lg:via-card/60 lg:to-card/40'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* Mobile close button */}
|
||||||
|
<div className="lg:hidden flex items-center justify-between px-4 py-3 border-b border-border/50">
|
||||||
|
<span className="text-sm font-semibold text-foreground">Navigation</span>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={onClose}
|
||||||
|
className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
|
||||||
|
aria-label="Close navigation menu"
|
||||||
|
>
|
||||||
|
<X className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="sticky top-0 p-4 space-y-1">
|
||||||
|
{/* Global Settings Groups */}
|
||||||
|
{GLOBAL_NAV_GROUPS.map((group, groupIndex) => (
|
||||||
|
<div key={group.label}>
|
||||||
|
{/* Group divider (except for first group) */}
|
||||||
|
{groupIndex > 0 && <div className="my-3 border-t border-border/50" />}
|
||||||
|
|
||||||
|
{/* Group Label */}
|
||||||
|
<div className="px-3 py-2 text-xs font-semibold text-muted-foreground/70 uppercase tracking-wider">
|
||||||
|
{group.label}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Group Items */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
{group.items.map((item) =>
|
||||||
|
item.subItems ? (
|
||||||
|
<NavItemWithSubItems
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
activeSection={activeSection}
|
||||||
|
onNavigate={onNavigate}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<NavButton
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
isActive={activeSection === item.id}
|
||||||
|
onNavigate={onNavigate}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
{/* Group Items */}
|
{/* Project Settings - only show when a project is selected */}
|
||||||
<div className="space-y-1">
|
{currentProject && (
|
||||||
{group.items.map((item) =>
|
<>
|
||||||
item.subItems ? (
|
{/* Divider */}
|
||||||
<NavItemWithSubItems
|
<div className="my-3 border-t border-border/50" />
|
||||||
key={item.id}
|
|
||||||
item={item}
|
{/* Project Settings Label */}
|
||||||
activeSection={activeSection}
|
<div className="px-3 py-2 text-xs font-semibold text-muted-foreground/70 uppercase tracking-wider">
|
||||||
onNavigate={onNavigate}
|
Project Settings
|
||||||
/>
|
</div>
|
||||||
) : (
|
|
||||||
|
{/* Project Settings Items */}
|
||||||
|
<div className="space-y-1">
|
||||||
|
{PROJECT_NAV_ITEMS.map((item) => (
|
||||||
<NavButton
|
<NavButton
|
||||||
key={item.id}
|
key={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
isActive={activeSection === item.id}
|
isActive={activeSection === item.id}
|
||||||
onNavigate={onNavigate}
|
onNavigate={onNavigate}
|
||||||
/>
|
/>
|
||||||
)
|
))}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</>
|
||||||
</div>
|
)}
|
||||||
))}
|
</div>
|
||||||
|
</nav>
|
||||||
{/* Project Settings - only show when a project is selected */}
|
</>
|
||||||
{currentProject && (
|
|
||||||
<>
|
|
||||||
{/* Divider */}
|
|
||||||
<div className="my-3 border-t border-border/50" />
|
|
||||||
|
|
||||||
{/* Project Settings Label */}
|
|
||||||
<div className="px-3 py-2 text-xs font-semibold text-muted-foreground/70 uppercase tracking-wider">
|
|
||||||
Project Settings
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Project Settings Items */}
|
|
||||||
<div className="space-y-1">
|
|
||||||
{PROJECT_NAV_ITEMS.map((item) => (
|
|
||||||
<NavButton
|
|
||||||
key={item.id}
|
|
||||||
item={item}
|
|
||||||
isActive={activeSection === item.id}
|
|
||||||
onNavigate={onNavigate}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user