1- import { BoltIcon , ChevronLeftIcon , ChevronRightIcon } from "@heroicons/react/20/solid" ;
1+ import { ChevronLeftIcon , ChevronRightIcon } from "@heroicons/react/20/solid" ;
22import { useEffect , useMemo , useState } from "react" ;
33import { useTypedFetcher } from "remix-typedjson" ;
4+ import { SmartColumnIcon } from "~/assets/icons/SmartColumnIcon" ;
45import { Button } from "~/components/primitives/Buttons" ;
56import { Dialog , DialogContent , DialogFooter , DialogHeader } from "~/components/primitives/Dialog" ;
7+ import { Hint } from "~/components/primitives/Hint" ;
68import { Input } from "~/components/primitives/Input" ;
9+ import { InputGroup } from "~/components/primitives/InputGroup" ;
710import { Label } from "~/components/primitives/Label" ;
811import { Paragraph } from "~/components/primitives/Paragraph" ;
12+ import { RadioGroup , RadioGroupItem } from "~/components/primitives/RadioButton" ;
913import { useEnvironment } from "~/hooks/useEnvironment" ;
1014import { useOrganization } from "~/hooks/useOrganizations" ;
1115import { useProject } from "~/hooks/useProject" ;
@@ -167,44 +171,49 @@ export function AddSmartColumnDialog({
167171 header or footer off a short screen. */ }
168172 < DialogContent className = "max-h-[90vh] grid-rows-[auto_minmax(0,1fr)_auto] sm:max-w-[860px]!" >
169173 < DialogHeader > { editing ? "Edit smart column" : "Add smart column" } </ DialogHeader >
170- < div className = "flex min-h-0 flex-col gap-5" >
171- < Paragraph variant = "small/dimmed " >
172- Pull a single value out of a run's payload, metadata, or output by JSON path . Smart
173- columns are display only — you can't sort or filter by them.
174+ < div className = "flex min-h-0 flex-col gap-5 pt-3 " >
175+ < Paragraph variant = "base/bright " >
176+ Pick a source, then click a value in the sample to turn it into a column . Smart columns
177+ are display only — you can't sort or filter by them.
174178 </ Paragraph >
175179
176- < div className = "grid min-h-0 grid-cols-1 items-stretch gap-5 md:grid-cols-3" >
180+ < div className = "grid min-h-0 grid-cols-1 items-stretch gap-2. 5 md:grid-cols-3" >
177181 < div className = "flex flex-col gap-4 overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control" >
178- < div className = "flex flex-col gap-1.5" >
182+ < InputGroup fullWidth >
179183 < Label > Source</ Label >
180- < div className = "flex flex-col gap-2" >
184+ < RadioGroup
185+ className = "flex flex-col gap-2"
186+ value = { source }
187+ onValueChange = { ( next ) => handleSourceChange ( next as SmartColumnSource ) }
188+ >
181189 { SOURCE_CARDS . map ( ( card ) => (
182- < SourceCard
190+ < RadioGroupItem
183191 key = { card . value }
192+ id = { `smart-source-${ card . value } ` }
193+ value = { card . value }
194+ variant = "description"
184195 label = { card . label }
185196 description = { card . description }
186- selected = { source === card . value }
187- onSelect = { ( ) => handleSourceChange ( card . value ) }
188197 />
189198 ) ) }
190- </ div >
191- </ div >
199+ </ RadioGroup >
200+ </ InputGroup >
192201
193- < div className = "flex flex-col gap-1.5" >
202+ < InputGroup fullWidth >
194203 < Label > JSON path</ Label >
195204 < Input
196205 value = { path }
197206 onChange = { ( e ) => setPath ( e . target . value ) }
198207 placeholder = "$.order.total"
199208 spellCheck = { false }
200209 />
201- < Paragraph variant = "extra-small" className = "text-balance text-text-dimmed " >
210+ < Hint className = "text-balance" >
202211 e.g. < code > $.order.total</ code > , < code > $.items[0].sku</ code > ,{ " " }
203212 < code > $.items.length</ code >
204- </ Paragraph >
205- </ div >
213+ </ Hint >
214+ </ InputGroup >
206215
207- < div className = "flex flex-col gap-1.5" >
216+ < InputGroup fullWidth >
208217 < Label > Column label</ Label >
209218 < Input
210219 value = { effectiveLabel }
@@ -214,21 +223,27 @@ export function AddSmartColumnDialog({
214223 } }
215224 placeholder = { labelFromPath ( path ) }
216225 />
217- </ div >
226+ </ InputGroup >
218227
219- < div className = "flex flex-col gap-1.5" >
228+ < InputGroup fullWidth >
220229 < Label > Display as</ Label >
221- < div className = "grid grid-cols-2 gap-2" >
230+ < RadioGroup
231+ className = "grid grid-cols-2 gap-2"
232+ value = { displayAs }
233+ onValueChange = { ( next ) => setDisplayAs ( next as SmartColumnDisplay ) }
234+ >
222235 { DISPLAY_OPTIONS . map ( ( option ) => (
223- < SourceCard
236+ < RadioGroupItem
224237 key = { option . value }
238+ id = { `smart-display-${ option . value } ` }
239+ value = { option . value }
240+ variant = "button/small"
225241 label = { option . label }
226- selected = { displayAs === option . value }
227- onSelect = { ( ) => setDisplayAs ( option . value ) }
242+ className = "w-full"
228243 />
229244 ) ) }
230- </ div >
231- </ div >
245+ </ RadioGroup >
246+ </ InputGroup >
232247 </ div >
233248
234249 < div className = "flex min-h-0 flex-col gap-1.5" >
@@ -279,7 +294,7 @@ export function AddSmartColumnDialog({
279294 </ div >
280295 </ div >
281296 < DialogFooter >
282- < Button variant = "tertiary /medium" onClick = { ( ) => onOpenChange ( false ) } >
297+ < Button variant = "secondary /medium" onClick = { ( ) => onOpenChange ( false ) } >
283298 Cancel
284299 </ Button >
285300 < Button variant = "primary/medium" disabled = { ! canSubmit } onClick = { handleSubmit } >
@@ -326,46 +341,6 @@ function SampleRunPicker({
326341 ) ;
327342}
328343
329- /** Radio card used for both Source (with a description) and Display as (without). */
330- function SourceCard ( {
331- label,
332- description,
333- selected,
334- onSelect,
335- } : {
336- label : string ;
337- description ?: string ;
338- selected : boolean ;
339- onSelect : ( ) => void ;
340- } ) {
341- return (
342- < button
343- type = "button"
344- onClick = { onSelect }
345- aria-pressed = { selected }
346- className = { cn (
347- "flex cursor-pointer flex-col gap-1 rounded-lg border p-2.5 text-left transition" ,
348- selected
349- ? "border-blue-500 bg-blue-500/10"
350- : "border-grid-bright bg-background-dimmed hover:border-text-dimmed"
351- ) }
352- >
353- < span className = "flex items-center gap-1.5 text-sm font-medium text-text-bright" >
354- < span
355- className = { cn (
356- "grid size-3.5 flex-none place-items-center rounded-full border" ,
357- selected ? "border-blue-500" : "border-text-dimmed"
358- ) }
359- >
360- { selected && < span className = "size-1.5 rounded-full bg-blue-500" /> }
361- </ span >
362- { label }
363- </ span >
364- { description && < span className = "text-xs text-text-dimmed" > { description } </ span > }
365- </ button >
366- ) ;
367- }
368-
369344function SmartColumnPreview ( {
370345 rows,
371346 def,
@@ -381,7 +356,7 @@ function SmartColumnPreview({
381356 return (
382357 < div className = "flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border border-grid-dimmed" >
383358 < div className = "flex flex-none items-center gap-1 border-b border-grid-dimmed bg-background-dimmed px-2.5 py-1.5" >
384- < BoltIcon className = "size-3.5 flex-none text-text-dimmed" />
359+ < SmartColumnIcon className = "size-3.5 flex-none text-text-dimmed" />
385360 < span className = "truncate text-xs font-medium text-text-bright" >
386361 { def . label || "Column" }
387362 </ span >
0 commit comments