refactor: sider bar

This commit is contained in:
SonPhung
2026-07-10 00:42:23 +07:00
parent f7fec6e510
commit 63d570801a

View File

@@ -178,7 +178,7 @@ function DraggableAttribute({ id, label }: { id: string; label: string }) {
export function Sidebar() { export function Sidebar() {
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [selectedGroupId, setSelectedGroupId] = useState(ATTRIBUTE_GROUPS[0].id); const [selectedGroupIds, setSelectedGroupIds] = useState<string[]>([ATTRIBUTE_GROUPS[0].id]);
const filteredGroups = useMemo(() => { const filteredGroups = useMemo(() => {
if (search.trim()) { if (search.trim()) {
@@ -191,37 +191,14 @@ export function Sidebar() {
} }
// Khi không search, chỉ hiển thị nhóm được chọn // Khi không search, chỉ hiển thị nhóm được chọn
return ATTRIBUTE_GROUPS.filter(g => g.id === selectedGroupId); return ATTRIBUTE_GROUPS.filter(g => selectedGroupIds.includes(g.id));
}, [search, selectedGroupId]); }, [search, selectedGroupIds]);
return ( return (
<div className="w-72 border-r bg-gray-50/50 flex flex-col h-full"> <div className="w-72 border-r bg-gray-50/50 flex flex-col h-full">
<div className="p-4 border-b bg-white flex flex-col gap-3"> <div className="p-4 border-b bg-white flex flex-col gap-3">
<h3 className="font-semibold">Trường dữ liệu</h3>
{/* Dropdown chọn nhóm */}
<div className="flex flex-col gap-1.5">
<label className="text-xs font-medium text-muted-foreground">Nhóm dữ liệu</label>
<select
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary"
value={selectedGroupId}
onChange={(e) => {
setSelectedGroupId(e.target.value);
setSearch(""); // Reset search khi chuyển nhóm
}}
>
{Array.from(new Set(ATTRIBUTE_GROUPS.map(g => g.category))).map(category => (
<optgroup key={category} label={category}>
{ATTRIBUTE_GROUPS.filter(g => g.category === category).map(g => (
<option key={g.id} value={g.id}>{g.name}</option>
))}
</optgroup>
))}
</select>
</div>
{/* Ô tìm kiếm */} {/* Ô tìm kiếm */}
<div className="relative mt-1"> <div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" /> <Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input <Input
type="search" type="search"
@@ -231,6 +208,52 @@ export function Sidebar() {
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
/> />
</div> </div>
{/* Chips chọn nhóm */}
<div className="flex flex-col gap-1.5">
{selectedGroupIds.length > 0 && (
<div className="flex justify-end">
<button
onClick={() => setSelectedGroupIds([])}
className="text-[10px] text-muted-foreground hover:text-primary transition-colors"
>
Bỏ chọn tất cả
</button>
</div>
)}
<div className="flex flex-col gap-3 max-h-[35vh] overflow-y-auto pb-1 pr-1 scrollbar-thin scrollbar-thumb-gray-200">
{Array.from(new Set(ATTRIBUTE_GROUPS.map(g => g.category))).map(category => (
<div key={category} className="flex flex-col gap-1.5">
<div className="text-[10px] font-semibold text-muted-foreground uppercase tracking-wider">{category}</div>
<div className="flex flex-wrap gap-1.5">
{ATTRIBUTE_GROUPS.filter(g => g.category === category).map(g => {
const isSelected = selectedGroupIds.includes(g.id);
return (
<button
key={g.id}
onClick={() => {
if (isSelected) {
setSelectedGroupIds(prev => prev.filter(id => id !== g.id));
} else {
setSelectedGroupIds(prev => [...prev, g.id]);
}
setSearch(""); // Reset search khi chuyển nhóm
}}
className={`inline-flex items-center px-2.5 py-0.5 text-[11px] font-medium rounded-md border transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ${
isSelected
? "border-transparent bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm"
: "border-input bg-background hover:bg-accent hover:text-accent-foreground"
}`}
>
{g.name}
</button>
)
})}
</div>
</div>
))}
</div>
</div>
</div> </div>
<div className="flex-1 overflow-y-auto p-4 flex flex-col gap-6"> <div className="flex-1 overflow-y-auto p-4 flex flex-col gap-6">