cfe7ad56ff
Split the dense single-row filter bar into two rows: search + saved views on top, filter selectors below. Fix CTI selectors to use design system tokens instead of hardcoded dark classes, and upgrade the saved views button with an icon and badge count. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
131 lines
3.9 KiB
TypeScript
131 lines
3.9 KiB
TypeScript
import { useCategories, useItems, useTypes } from '../api/queries';
|
|
|
|
interface CTISelectProps {
|
|
value: { categoryId: string; typeId: string; itemId: string };
|
|
onChange: (value: { categoryId: string; typeId: string; itemId: string }) => void;
|
|
disabled?: boolean;
|
|
compact?: boolean;
|
|
}
|
|
|
|
export default function CTISelect({ value, onChange, disabled, compact }: CTISelectProps) {
|
|
const { data: categories = [] } = useCategories();
|
|
const { data: types = [] } = useTypes(value.categoryId || undefined);
|
|
const { data: items = [] } = useItems(value.typeId || undefined);
|
|
|
|
const handleCategory = (categoryId: string) => {
|
|
onChange({ categoryId, typeId: '', itemId: '' });
|
|
};
|
|
|
|
const handleType = (typeId: string) => {
|
|
onChange({ ...value, typeId, itemId: '' });
|
|
};
|
|
|
|
const handleItem = (itemId: string) => {
|
|
onChange({ ...value, itemId });
|
|
};
|
|
|
|
const selectClass =
|
|
'block w-full rounded-md border border-input bg-background px-3 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50';
|
|
|
|
if (compact) {
|
|
return (
|
|
<div className="flex gap-2">
|
|
<select
|
|
value={value.categoryId}
|
|
onChange={(e) => handleCategory(e.target.value)}
|
|
disabled={disabled}
|
|
className={selectClass}
|
|
>
|
|
<option value="">Category...</option>
|
|
{categories.map((c) => (
|
|
<option key={c.id} value={c.id}>
|
|
{c.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<select
|
|
value={value.typeId}
|
|
onChange={(e) => handleType(e.target.value)}
|
|
disabled={disabled || !value.categoryId}
|
|
className={selectClass}
|
|
>
|
|
<option value="">Type...</option>
|
|
{types.map((t) => (
|
|
<option key={t.id} value={t.id}>
|
|
{t.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<select
|
|
value={value.itemId}
|
|
onChange={(e) => handleItem(e.target.value)}
|
|
disabled={disabled || !value.typeId}
|
|
className={selectClass}
|
|
>
|
|
<option value="">Item...</option>
|
|
{items.map((i) => (
|
|
<option key={i.id} value={i.id}>
|
|
{i.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-3 gap-3">
|
|
<div>
|
|
<label className="block text-xs font-medium text-muted-foreground mb-1">Category</label>
|
|
<select
|
|
value={value.categoryId}
|
|
onChange={(e) => handleCategory(e.target.value)}
|
|
disabled={disabled}
|
|
className={selectClass}
|
|
>
|
|
<option value="">Select...</option>
|
|
{categories.map((c) => (
|
|
<option key={c.id} value={c.id}>
|
|
{c.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-xs font-medium text-muted-foreground mb-1">Type</label>
|
|
<select
|
|
value={value.typeId}
|
|
onChange={(e) => handleType(e.target.value)}
|
|
disabled={disabled || !value.categoryId}
|
|
className={selectClass}
|
|
>
|
|
<option value="">Select...</option>
|
|
{types.map((t) => (
|
|
<option key={t.id} value={t.id}>
|
|
{t.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-xs font-medium text-muted-foreground mb-1">Item</label>
|
|
<select
|
|
value={value.itemId}
|
|
onChange={(e) => handleItem(e.target.value)}
|
|
disabled={disabled || !value.typeId}
|
|
className={selectClass}
|
|
>
|
|
<option value="">Select...</option>
|
|
{items.map((i) => (
|
|
<option key={i.id} value={i.id}>
|
|
{i.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|