diff --git a/apps/dashboard/src/components/rule-builder/condition-builder.tsx b/apps/dashboard/src/components/rule-builder/condition-builder.tsx
index 24a92e5b1..bb6fc412b 100644
--- a/apps/dashboard/src/components/rule-builder/condition-builder.tsx
+++ b/apps/dashboard/src/components/rule-builder/condition-builder.tsx
@@ -1,6 +1,7 @@
"use client";
import { Button, cn, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui";
+import { DesignInput, DesignSelectorDropdown } from "@/components/design-components";
import {
type ConditionNode,
createEmptyCondition,
@@ -15,6 +16,13 @@ import { validateCountryCode } from "@stackframe/stack-shared/dist/utils/country
import { type ConditionField, type ConditionOperator, conditionFields, fieldMetadata, getOperatorsForField, isNumericField, validateNumericFieldValue } from "@stackframe/stack-shared/dist/utils/cel-fields";
import React from "react";
+const conditionRowSurfaceClass =
+ "bg-white/90 dark:bg-background/60 shadow-sm ring-1 ring-black/[0.06] dark:ring-white/[0.06]";
+const conditionFieldClass = "h-8 min-w-[120px]";
+const conditionValueClass = "h-8 w-full";
+
+const UNSET_SELECT_VALUE = "__unset__";
+
/**
* Validates whether a string is a valid regular expression.
* Returns null if valid, or an error message if invalid.
@@ -183,28 +191,24 @@ function ConditionRow({
};
return (
-
- {/* Field selector */}
-
+ onValueChange={(value) => handleFieldChange(value as ConditionField)}
+ options={FIELD_OPTIONS.map((opt) => ({ value: opt.value, label: opt.label }))}
+ size="sm"
+ className={conditionFieldClass}
+ triggerClassName={conditionFieldClass}
+ />
- {/* Operator selector */}
-
+ onValueChange={(value) => handleOperatorChange(value as ConditionOperator)}
+ options={availableOperators.map((op) => ({ value: op, label: OPERATOR_LABELS[op] }))}
+ size="sm"
+ className={conditionFieldClass}
+ triggerClassName={conditionFieldClass}
+ />
{/* Value input */}
@@ -216,7 +220,7 @@ function ConditionRow({
value={countryCode || null}
onChange={(val) => handleCountryCodeListItemChange(index, val ?? "")}
className={cn(
- "h-8 text-sm flex-1",
+ conditionValueClass,
countryCodeError !== null && "border-destructive ring-1 ring-destructive/30",
)}
/>
@@ -244,19 +248,20 @@ function ConditionRow({
) : condition.operator === 'in_list' ? (
-
{
const items = e.target.value.split(',').map(s => s.trim()).filter(Boolean);
handleValueChange(items);
}}
placeholder="value1, value2, ... (values cannot contain commas)"
className={cn(
- "h-8 px-2 text-sm bg-background/60 border rounded-md w-full",
+ conditionValueClass,
countryCodeError !== null
? "border-destructive ring-1 ring-destructive/30"
- : "border-border/50",
+ : undefined,
)}
/>
) : isCountryCodeField ? (
@@ -264,36 +269,39 @@ function ConditionRow({
value={typeof condition.value === 'string' && condition.value ? condition.value : null}
onChange={(val) => handleValueChange(val ?? "")}
className={cn(
- "h-8 text-sm w-full",
+ conditionValueClass,
countryCodeError !== null && "border-destructive ring-1 ring-destructive/30",
)}
/>
) : predefinedValues ? (
-
+
handleValueChange(value === UNSET_SELECT_VALUE ? "" : value)}
+ options={[
+ { value: UNSET_SELECT_VALUE, label: "Select..." },
+ ...predefinedValues.map((val) => ({ value: val, label: val })),
+ ]}
+ size="sm"
+ className="w-full"
+ triggerClassName={conditionValueClass}
+ />
) : isNumericField(condition.field) ? (
- handleValueChange(e.target.value === '' ? 0 : Number(e.target.value))}
placeholder="0-100"
- className="h-8 px-2 text-sm bg-background/60 border border-border/50 rounded-md w-full"
+ className={conditionValueClass}
/>
) : (
- handleValueChange(e.target.value)}
placeholder={
@@ -302,10 +310,10 @@ function ConditionRow({
: "Enter value..."
}
className={cn(
- "h-8 px-2 text-sm bg-background/60 border rounded-md flex-1",
+ conditionValueClass,
regexError !== null || countryCodeError !== null
? "border-destructive ring-1 ring-destructive/30"
- : "border-border/50"
+ : undefined,
)}
/>
{(regexError !== null || countryCodeError !== null) && (