Files
sam-sales/prt/ref/components/Sidebar.tsx
2025-12-22 21:35:17 +09:00

143 lines
6.1 KiB
TypeScript

import React, { useState } from 'react';
import { Category, Prompt } from '../types';
import { FolderIcon, FileIcon, PlusIcon, TrashIcon } from './Icons';
interface SidebarProps {
categories: Category[];
prompts: Prompt[];
onSelectPrompt: (id: string) => void;
onAddCategory: (parentId: string | null) => void;
onAddPrompt: (categoryId: string) => void;
onDeleteCategory: (id: string) => void;
selectedPromptId: string | null;
}
const Sidebar: React.FC<SidebarProps> = ({
categories,
prompts,
onSelectPrompt,
onAddCategory,
onAddPrompt,
onDeleteCategory,
selectedPromptId
}) => {
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
const toggleExpand = (id: string) => {
setExpanded(prev => ({ ...prev, [id]: !prev[id] }));
};
const renderCategory = (category: Category, depth: number = 0) => {
const categoryPrompts = prompts.filter(p => p.categoryId === category.id);
const isExpanded = expanded[category.id];
return (
<div key={category.id} className="mb-0.5">
<div
className={`group flex items-center justify-between px-3 py-2 rounded-lg cursor-pointer transition-all duration-200 ${isExpanded ? 'bg-slate-50' : 'hover:bg-slate-50'}`}
onClick={() => toggleExpand(category.id)}
>
<div className="flex items-center space-x-2.5" style={{ paddingLeft: `${depth * 12}px` }}>
<span className={`transform transition-transform duration-200 ${isExpanded ? 'rotate-90' : ''}`}>
<svg className="w-3 h-3 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M9 5l7 7-7 7" />
</svg>
</span>
<FolderIcon className={`w-4 h-4 ${isExpanded ? 'text-indigo-500' : 'text-slate-400'}`} />
<span className={`text-sm font-semibold truncate ${isExpanded ? 'text-slate-900' : 'text-slate-600'}`}>{category.name}</span>
</div>
<div className="flex items-center space-x-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={(e) => { e.stopPropagation(); onAddPrompt(category.id); }}
className="p-1 hover:bg-white hover:shadow-sm rounded text-slate-500 transition-all"
title="프롬프트 추가"
>
<PlusIcon className="w-3.5 h-3.5" />
</button>
<button
onClick={(e) => { e.stopPropagation(); onDeleteCategory(category.id); }}
className="p-1 hover:bg-red-50 hover:text-red-500 rounded text-slate-400 transition-all"
title="분야 삭제"
>
<TrashIcon className="w-3.5 h-3.5" />
</button>
</div>
</div>
{isExpanded && (
<div className="mt-0.5 border-l border-slate-100 ml-4.5 pl-0">
{categoryPrompts.map(prompt => (
<div
key={prompt.id}
onClick={() => onSelectPrompt(prompt.id)}
className={`flex items-center space-x-3 px-4 py-2 my-0.5 cursor-pointer rounded-lg text-sm transition-all duration-200
${selectedPromptId === prompt.id
? 'bg-indigo-600 text-white shadow-md shadow-indigo-100'
: 'hover:bg-indigo-50 text-slate-500 hover:text-indigo-600'}
`}
style={{ marginLeft: `${depth * 4}px` }}
>
<FileIcon className={`w-4 h-4 ${selectedPromptId === prompt.id ? 'text-white' : 'text-slate-300'}`} />
<span className="truncate font-medium">{prompt.name}</span>
</div>
))}
</div>
)}
</div>
);
};
return (
<aside className="w-80 sidebar-border bg-white flex flex-col h-screen overflow-hidden">
<div className="px-6 py-8">
<div className="flex items-center justify-between mb-2">
<div className="flex flex-col">
<h1 className="text-xl font-bold text-slate-900 leading-tight tracking-tight">SAM </h1>
<a href="https://codebridge-x.com" target="_blank" className="text-[10px] font-black text-indigo-500 uppercase tracking-widest hover:underline transition-all">
codebridge-x.com
</a>
</div>
</div>
<button
onClick={() => onAddCategory(null)}
className="mt-6 w-full flex items-center justify-center space-x-2 py-2.5 bg-slate-900 text-white rounded-xl hover:bg-slate-800 transition-all shadow-lg active:scale-95"
>
<PlusIcon className="w-4 h-4" />
<span className="text-sm font-bold"> </span>
</button>
</div>
<div className="flex-1 overflow-y-auto px-4 pb-10 space-y-1">
{categories.length === 0 ? (
<div className="text-center py-20 px-6">
<div className="w-12 h-12 bg-slate-50 rounded-2xl flex items-center justify-center mx-auto mb-4">
<FolderIcon className="w-6 h-6 text-slate-300" />
</div>
<p className="text-sm text-slate-400 font-medium"> .</p>
</div>
) : (
categories.map(cat => renderCategory(cat))
)}
</div>
<div className="mt-auto p-6 bg-slate-50/50 border-t border-slate-100">
<div className="flex items-center space-x-4">
<div className="relative">
<div className="w-10 h-10 rounded-2xl bg-indigo-600 flex items-center justify-center text-white font-black text-sm shadow-inner">
CB
</div>
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-emerald-500 border-2 border-white rounded-full"></div>
</div>
<div className="flex flex-col">
<p className="text-sm font-black text-slate-900 leading-none mb-1"> </p>
<p className="text-[11px] text-slate-500 font-bold">v2.1 </p>
</div>
</div>
</div>
</aside>
);
};
export default Sidebar;