mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
feat: Add Discord-like project switcher sidebar with icon support
- Add project icon field to ProjectRef and Project types - Create vertical project switcher sidebar component - Project icons with hover tooltips - Active project highlighting - Plus button to create new projects - Right-click context menu for edit/delete - Add IconPicker component with 35+ Lucide icons - Add EditProjectDialog for inline project editing - Update settings appearance section with project details editor - Add setProjectIcon and setProjectName actions to app store - Integrate ProjectSwitcher in root layout (shows on app pages only) Implements #469 Co-authored-by: Web Dev Cody <webdevcody@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Palette, Moon, Sun } from 'lucide-react';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Palette, Moon, Sun, Edit2 } from 'lucide-react';
|
||||
import { darkThemes, lightThemes } from '@/config/theme-options';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { IconPicker } from '@/components/layout/project-switcher/components/icon-picker';
|
||||
import type { Theme, Project } from '../shared/types';
|
||||
|
||||
interface AppearanceSectionProps {
|
||||
@@ -16,10 +20,33 @@ export function AppearanceSection({
|
||||
currentProject,
|
||||
onThemeChange,
|
||||
}: AppearanceSectionProps) {
|
||||
const { setProjectIcon, setProjectName } = useAppStore();
|
||||
const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark');
|
||||
const [editingProject, setEditingProject] = useState(false);
|
||||
const [projectName, setProjectNameLocal] = useState(currentProject?.name || '');
|
||||
const [projectIcon, setProjectIconLocal] = useState<string | null>(
|
||||
(currentProject as any)?.icon || null
|
||||
);
|
||||
|
||||
const themesToShow = activeTab === 'dark' ? darkThemes : lightThemes;
|
||||
|
||||
const handleSaveProjectDetails = () => {
|
||||
if (!currentProject) return;
|
||||
if (projectName.trim() !== currentProject.name) {
|
||||
setProjectName(currentProject.id, projectName.trim());
|
||||
}
|
||||
if (projectIcon !== (currentProject as any)?.icon) {
|
||||
setProjectIcon(currentProject.id, projectIcon);
|
||||
}
|
||||
setEditingProject(false);
|
||||
};
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
setProjectNameLocal(currentProject?.name || '');
|
||||
setProjectIconLocal((currentProject as any)?.icon || null);
|
||||
setEditingProject(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -40,7 +67,68 @@ export function AppearanceSection({
|
||||
Customize the look and feel of your application.
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="p-6 space-y-6">
|
||||
{/* Project Details Section */}
|
||||
{currentProject && (
|
||||
<div className="space-y-4 pb-6 border-b border-border/50">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-foreground font-medium">Project Details</Label>
|
||||
{!editingProject && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setEditingProject(true)}
|
||||
className="h-8"
|
||||
>
|
||||
<Edit2 className="w-3.5 h-3.5 mr-1.5" />
|
||||
Edit
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{editingProject ? (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="project-name-settings">Project Name</Label>
|
||||
<Input
|
||||
id="project-name-settings"
|
||||
value={projectName}
|
||||
onChange={(e) => setProjectNameLocal(e.target.value)}
|
||||
placeholder="Enter project name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Project Icon</Label>
|
||||
<IconPicker
|
||||
selectedIcon={projectIcon}
|
||||
onSelectIcon={setProjectIconLocal}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<Button variant="outline" size="sm" onClick={handleCancelEdit}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="sm" onClick={handleSaveProjectDetails} disabled={!projectName.trim()}>
|
||||
Save Changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-accent/30 border border-border/50">
|
||||
<div className="text-sm">
|
||||
<div className="font-medium text-foreground">{currentProject.name}</div>
|
||||
<div className="text-muted-foreground text-xs mt-0.5">{currentProject.path}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Theme Section */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-foreground font-medium">
|
||||
|
||||
Reference in New Issue
Block a user