mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-07 11:02:12 +02:00
feat: allow filter by agents in agent runs
This commit is contained in:
parent
9966940624
commit
cd0939d072
5 changed files with 214 additions and 10 deletions
|
|
@ -1,10 +1,11 @@
|
|||
import { AlertCircle, Calendar, CheckSquare, Hash, Radio, RefreshCw, Tag, X } from "lucide-react";
|
||||
import { AlertCircle, Calendar, CheckSquare, Hash, ListFilter, Radio, RefreshCw, Tag, X } from "lucide-react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { DateRangeFilter } from "@/components/filters/DateRangeFilter";
|
||||
import { MultiSelectFilter } from "@/components/filters/MultiSelectFilter";
|
||||
import { NumberFilter } from "@/components/filters/NumberFilter";
|
||||
import { NumberRangeFilter } from "@/components/filters/NumberRangeFilter";
|
||||
import { NumberSelectFilter } from "@/components/filters/NumberSelectFilter";
|
||||
import { RadioFilter } from "@/components/filters/RadioFilter";
|
||||
import { TagInputFilter } from "@/components/filters/TagInputFilter";
|
||||
import { TextFilter } from "@/components/filters/TextFilter";
|
||||
|
|
@ -165,6 +166,8 @@ export const FilterBuilder: React.FC<FilterBuilderProps> = ({
|
|||
case "number":
|
||||
case "numberRange":
|
||||
return <Hash className="h-4 w-4" />;
|
||||
case "numberSelect":
|
||||
return <ListFilter className="h-4 w-4" />;
|
||||
case "radio":
|
||||
return <Radio className="h-4 w-4" />;
|
||||
case "tags":
|
||||
|
|
@ -188,6 +191,12 @@ export const FilterBuilder: React.FC<FilterBuilderProps> = ({
|
|||
const value = filter.value as NumberValue;
|
||||
return value.value !== null ? value.value.toString() : "No value";
|
||||
}
|
||||
case "numberSelect": {
|
||||
const value = filter.value as NumberValue;
|
||||
if (value.value === null) return "No value";
|
||||
return filter.attribute.config.numberSelectOptions?.find(option => option.value === value.value)?.label
|
||||
|| value.value.toString();
|
||||
}
|
||||
case "numberRange":
|
||||
return formatNumberRange(filter.value as NumberRangeValue, filter.attribute.config.unit);
|
||||
case "radio": {
|
||||
|
|
@ -244,6 +253,18 @@ export const FilterBuilder: React.FC<FilterBuilderProps> = ({
|
|||
step={filter.attribute.config.step}
|
||||
/>
|
||||
);
|
||||
case "numberSelect":
|
||||
return (
|
||||
<NumberSelectFilter
|
||||
value={filter.value as NumberValue}
|
||||
onChange={(value) => updateFilter(index, value)}
|
||||
error={error}
|
||||
label={filter.attribute.config.numberSelectLabel}
|
||||
placeholder={filter.attribute.config.placeholder}
|
||||
options={filter.attribute.config.numberSelectOptions || []}
|
||||
isLoading={filter.attribute.config.numberSelectOptionsLoading}
|
||||
/>
|
||||
);
|
||||
case "numberRange":
|
||||
return (
|
||||
<NumberRangeFilter
|
||||
|
|
|
|||
74
ui/src/components/filters/NumberSelectFilter.tsx
Normal file
74
ui/src/components/filters/NumberSelectFilter.tsx
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import type { NumberFilterOption, NumberValue } from "@/types/filters";
|
||||
|
||||
interface NumberSelectFilterProps {
|
||||
value: NumberValue;
|
||||
onChange: (value: NumberValue) => void;
|
||||
error?: string;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
options: NumberFilterOption[];
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export const NumberSelectFilter: React.FC<NumberSelectFilterProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
error,
|
||||
label = "Option",
|
||||
placeholder = "Select an option",
|
||||
options,
|
||||
isLoading = false,
|
||||
}) => {
|
||||
const selectedOptionExists = options.some(option => option.value === value.value);
|
||||
const unavailableSelection =
|
||||
value.value !== null && !selectedOptionExists
|
||||
? { label: `Unavailable agent (#${value.value})`, value: value.value }
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2">
|
||||
<Label>{label}</Label>
|
||||
<Select
|
||||
value={value.value === null ? "" : value.value.toString()}
|
||||
onValueChange={(selectedValue) => {
|
||||
const numericValue = parseInt(selectedValue, 10);
|
||||
onChange({ value: Number.isNaN(numericValue) ? null : numericValue });
|
||||
}}
|
||||
disabled={isLoading || options.length === 0}
|
||||
>
|
||||
<SelectTrigger className={error ? "border-red-500" : ""}>
|
||||
<SelectValue placeholder={isLoading ? "Loading options..." : placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{unavailableSelection && (
|
||||
<SelectItem value={unavailableSelection.value.toString()} disabled>
|
||||
{unavailableSelection.label}
|
||||
</SelectItem>
|
||||
)}
|
||||
{options.length === 0 ? (
|
||||
<div className="px-2 py-1.5 text-sm text-muted-foreground">
|
||||
No options found
|
||||
</div>
|
||||
) : (
|
||||
options.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value.toString()}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
{error && <p className="text-xs text-red-500">{error}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue