feat: purchase-plans global update
This commit is contained in:
148
components/column-selector-dialog.tsx
Normal file
148
components/column-selector-dialog.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
import { X, Check } from "lucide-react";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ALL_COLUMNS, COLUMN_GROUPS, DEFAULT_VISIBLE_COLUMNS } from "@/lib/config/placement-columns";
|
||||
|
||||
interface ColumnSelectorDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
visibleColumns: Set<string>;
|
||||
onVisibleColumnsChange: (columns: Set<string>) => void;
|
||||
}
|
||||
|
||||
export function ColumnSelectorDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
visibleColumns,
|
||||
onVisibleColumnsChange,
|
||||
}: ColumnSelectorDialogProps) {
|
||||
const handleToggleColumn = (columnId: string) => {
|
||||
const next = new Set(visibleColumns);
|
||||
if (next.has(columnId)) {
|
||||
// Don't allow hiding required columns
|
||||
const column = ALL_COLUMNS.find((c) => c.id === columnId);
|
||||
if (column?.required) return;
|
||||
next.delete(columnId);
|
||||
} else {
|
||||
next.add(columnId);
|
||||
}
|
||||
onVisibleColumnsChange(next);
|
||||
};
|
||||
|
||||
const handleToggleGroup = (groupId: string, select: boolean) => {
|
||||
const groupColumns = ALL_COLUMNS.filter((c) => c.group === groupId);
|
||||
const next = new Set(visibleColumns);
|
||||
|
||||
groupColumns.forEach((column) => {
|
||||
if (column.required) return;
|
||||
if (select) {
|
||||
next.add(column.id);
|
||||
} else {
|
||||
next.delete(column.id);
|
||||
}
|
||||
});
|
||||
|
||||
onVisibleColumnsChange(next);
|
||||
};
|
||||
|
||||
const isGroupFullySelected = (groupId: string): boolean => {
|
||||
const groupColumns = ALL_COLUMNS.filter((c) => c.group === groupId);
|
||||
return groupColumns.every((c) => c.required || visibleColumns.has(c.id));
|
||||
};
|
||||
|
||||
const isGroupPartiallySelected = (groupId: string): boolean => {
|
||||
const groupColumns = ALL_COLUMNS.filter((c) => c.group === groupId);
|
||||
return groupColumns.some((c) => visibleColumns.has(c.id)) && !isGroupFullySelected(groupId);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
onVisibleColumnsChange(new Set(DEFAULT_VISIBLE_COLUMNS));
|
||||
};
|
||||
|
||||
const handleSelectAll = () => {
|
||||
onVisibleColumnsChange(new Set(ALL_COLUMNS.map((c) => c.id)));
|
||||
};
|
||||
|
||||
const handleSelectNone = () => {
|
||||
onVisibleColumnsChange(
|
||||
new Set(ALL_COLUMNS.filter((c) => c.required).map((c) => c.id))
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Выбор колонок</DialogTitle>
|
||||
<DialogDescription>
|
||||
Выберите колонки для отображения в таблице размещений
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-6 py-4">
|
||||
{/* Quick actions */}
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" onClick={handleReset}>
|
||||
По умолчанию
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={handleSelectAll}>
|
||||
Выбрать все
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={handleSelectNone}>
|
||||
Снять выбор
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Column groups */}
|
||||
{COLUMN_GROUPS.map((group) => {
|
||||
const groupColumns = ALL_COLUMNS.filter((c) => c.group === group.id);
|
||||
return (
|
||||
<div key={group.id} className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Checkbox
|
||||
id={`group-${group.id}`}
|
||||
checked={isGroupFullySelected(group.id)}
|
||||
onCheckedChange={(checked) => handleToggleGroup(group.id, !!checked)}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`group-${group.id}`}
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
>
|
||||
{group.label}
|
||||
</label>
|
||||
{isGroupPartiallySelected(group.id) && (
|
||||
<span className="text-xs text-muted-foreground">(частично выбрано)</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 gap-2 ml-6">
|
||||
{groupColumns.map((column) => (
|
||||
<div key={column.id} className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id={`col-${column.id}`}
|
||||
checked={visibleColumns.has(column.id)}
|
||||
onCheckedChange={() => handleToggleColumn(column.id)}
|
||||
disabled={column.required}
|
||||
/>
|
||||
<label
|
||||
htmlFor={`col-${column.id}`}
|
||||
className={cn(
|
||||
"text-xs cursor-pointer truncate",
|
||||
column.required && "font-medium"
|
||||
)}
|
||||
>
|
||||
{column.label}
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user