@@ -31,10 +31,18 @@ import {
31
31
LoaderIcon ,
32
32
PlayIcon ,
33
33
ScrollTextIcon ,
34
+ SearchCodeIcon ,
34
35
XIcon ,
35
36
} from "lucide-react" ;
36
37
import { AnimatePresence , motion } from "motion/react" ;
37
- import { type FC , useCallback , useEffect , useMemo , useState } from "react" ;
38
+ import {
39
+ type FC ,
40
+ type PropsWithChildren ,
41
+ useCallback ,
42
+ useEffect ,
43
+ useMemo ,
44
+ useState ,
45
+ } from "react" ;
38
46
import { useSearchParams } from "react-router" ;
39
47
import {
40
48
Select ,
@@ -196,7 +204,7 @@ export const Preview: FC = () => {
196
204
( $errors . show && $errors . diagnostics . length > 0 )
197
205
}
198
206
className = { cn (
199
- "flex h-full w-full flex-col items-start gap-6 p-6 " ,
207
+ "flex h-full w-full flex-col items-start gap-4 p-5 " ,
200
208
( $wasmState !== "loaded" ||
201
209
( $errors . show && $errors . diagnostics . length > 0 ) ) &&
202
210
"pointer-events-none" ,
@@ -226,16 +234,7 @@ export const Preview: FC = () => {
226
234
) : null }
227
235
</ AnimatePresence >
228
236
</ div >
229
- < div className = "flex w-full items-center justify-end gap-3" >
230
- < UserSelect />
231
- < Button
232
- variant = "destructive"
233
- onClick = { $resetForm }
234
- className = "w-fit"
235
- >
236
- Reset form
237
- </ Button >
238
- </ div >
237
+ < UserSelect />
239
238
</ div >
240
239
}
241
240
{ $parameters . length === 0 ? (
@@ -247,6 +246,12 @@ export const Preview: FC = () => {
247
246
< Form parameters = { $parameters } />
248
247
</ div >
249
248
) }
249
+ < div className = "flex w-full justify-between gap-3" >
250
+ < Button variant = "outline" onClick = { $resetForm } className = "w-fit" >
251
+ Reset form
252
+ </ Button >
253
+ < ViewOutput />
254
+ </ div >
250
255
</ div >
251
256
</ Tabs . Content >
252
257
@@ -486,15 +491,17 @@ const LogsEmptyState = () => {
486
491
487
492
type LogProps = { log : ParserLog } ;
488
493
const Log : FC < LogProps > = ( { log } ) => {
489
- const [ showTable , setShowTable ] = useState ( ( ) => false ) ;
494
+ const data = Object . entries ( log ) . reduce < Record < string , unknown > > (
495
+ ( acc , [ key , value ] ) => {
496
+ acc [ key ] = value ;
497
+ return acc ;
498
+ } ,
499
+ { } ,
500
+ ) ;
490
501
491
502
return (
492
- < Dialog . Root
493
- modal = { true }
494
- open = { showTable }
495
- onOpenChange = { ( show ) => setShowTable ( ( ) => show ) }
496
- >
497
- < Dialog . Trigger
503
+ < TableDrawer data = { data } >
504
+ < button
498
505
className = { cn (
499
506
"group grid h-fit min-h-10 w-full grid-cols-8 items-center border-b border-l-4 border-l-content-destructive hover:bg-surface-primary" ,
500
507
log . level . toLowerCase ( ) === "info" && "border-l-content-link" ,
@@ -509,7 +516,91 @@ const Log: FC<LogProps> = ({ log }) => {
509
516
< p className = "col-span-6 break-all p-2 text-left font-mono text-content-primary text-xs" >
510
517
{ JSON . stringify ( log ) }
511
518
</ p >
512
- </ Dialog . Trigger >
519
+ </ button >
520
+ </ TableDrawer >
521
+ ) ;
522
+ } ;
523
+
524
+ type FormProps = { parameters : ParameterWithSource [ ] } ;
525
+
526
+ const Form : FC < FormProps > = ( { parameters } ) => {
527
+ return parameters
528
+ . sort ( ( a , b ) => a . order - b . order )
529
+ . map ( ( p , index ) => < FormElement key = { index } parameter = { p } /> ) ;
530
+ } ;
531
+
532
+ type FormElementProps = { parameter : ParameterWithSource } ;
533
+ const FormElement : FC < FormElementProps > = ( { parameter } ) => {
534
+ const $form = useStore ( ( state ) => state . form ) ;
535
+ const $setForm = useStore ( ( state ) => state . setFormState ) ;
536
+
537
+ const value = useMemo (
538
+ ( ) =>
539
+ $form [ parameter . name ] ??
540
+ ( parameter . default_value . value === "??"
541
+ ? ""
542
+ : parameter . default_value . value ) ,
543
+ [ $form , parameter ] ,
544
+ ) ;
545
+
546
+ const onValueChange = ( value : string ) => {
547
+ $setForm ( parameter . name , value ) ;
548
+ } ;
549
+
550
+ return (
551
+ < DynamicParameter
552
+ parameter = { parameter }
553
+ value = { value }
554
+ autofill = { false }
555
+ onChange = { onValueChange }
556
+ disabled = { parameter . styling . disabled }
557
+ />
558
+ ) ;
559
+ } ;
560
+
561
+ const UserSelect : FC = ( ) => {
562
+ const $setWorkspaceOwner = useStore ( ( state ) => state . setWorkspaceOwner ) ;
563
+
564
+ return (
565
+ < Select
566
+ defaultValue = "admin"
567
+ onValueChange = { ( value ) => {
568
+ const users : Record < string , WorkspaceOwner | undefined > = mockUsers ;
569
+ $setWorkspaceOwner ( users [ value ] ?? mockUsers . admin ) ;
570
+ } }
571
+ >
572
+ < SelectTrigger className = "w-fit min-w-40" >
573
+ < SelectValue />
574
+ </ SelectTrigger >
575
+ < SelectContent >
576
+ < SelectItem value = "admin" > Administrator</ SelectItem >
577
+ < SelectItem value = "developer" > Developer</ SelectItem >
578
+ < SelectItem value = "contractor" > Contractor</ SelectItem >
579
+ < SelectItem value = "eu-developer" > EU Developer</ SelectItem >
580
+ </ SelectContent >
581
+ </ Select >
582
+ ) ;
583
+ } ;
584
+
585
+ type TableDrawerProps = {
586
+ data : Record < string , unknown > ;
587
+ headers ?: [ string , string ] ;
588
+ } & PropsWithChildren ;
589
+
590
+ const TableDrawer : FC < TableDrawerProps > = ( {
591
+ data,
592
+ headers = [ "field" , "value" ] ,
593
+ children,
594
+ } ) => {
595
+ const [ showTable , setShowTable ] = useState ( ( ) => false ) ;
596
+
597
+ return (
598
+ < Dialog . Root
599
+ modal = { true }
600
+ open = { showTable }
601
+ onOpenChange = { ( show ) => setShowTable ( ( ) => show ) }
602
+ >
603
+ < Dialog . Trigger asChild = { true } > { children } </ Dialog . Trigger >
513
604
514
605
< Dialog . Portal forceMount = { true } >
515
606
< AnimatePresence propagate = { true } >
@@ -528,11 +619,11 @@ const Log: FC<LogProps> = ({ log }) => {
528
619
initial = { { opacity : 0 , transform : "translateX(100px)" } }
529
620
animate = { { opacity : 1 , transform : "translateX(0px)" } }
530
621
exit = { { opacity : 0 , transform : "translateX(100px)" } }
531
- className = "fixed top-0 right-0 z-20 flex h-full w-full max-w-md flex-col justify-start gap-6 border-l bg-surface-primary p-4"
622
+ className = "fixed top-0 right-0 z-20 flex h-full w-full max-w-lg flex-col justify-start gap-6 border-l bg-surface-primary p-4"
532
623
>
533
624
< div className = "flex items-center justify-between" >
534
625
< Dialog . Title className = "font-semibold text-2xl text-content-primary" >
535
- Log
626
+ Parameter Values
536
627
</ Dialog . Title >
537
628
< Dialog . Close asChild = { true } >
538
629
< Button
@@ -544,16 +635,16 @@ const Log: FC<LogProps> = ({ log }) => {
544
635
</ Button >
545
636
</ Dialog . Close >
546
637
</ div >
547
- < div className = "flex w-full flex-col overflow-clip rounded-lg border font-mono text-content-primary text-xs" >
638
+ < div className = "flex w-full flex-col overflow-scroll rounded-lg border font-mono text-content-primary text-xs" >
548
639
< div className = "grid grid-cols-8 border-b bg-surface-secondary" >
549
640
< div className = "col-span-2 flex min-h-8 items-center border-r px-2 py-1" >
550
- < p className = "text-left uppercase" > field </ p >
641
+ < p className = "text-left uppercase" > { headers [ 0 ] } </ p >
551
642
</ div >
552
643
< div className = "col-span-6 flex min-h-8 items-center px-2 py-1" >
553
- < p className = "text-left uppercase" > value </ p >
644
+ < p className = "text-left uppercase" > { headers [ 1 ] } </ p >
554
645
</ div >
555
646
</ div >
556
- { Object . entries ( log ) . map ( ( [ key , value ] , index ) => {
647
+ { Object . entries ( data ) . map ( ( [ key , value ] , index ) => {
557
648
const displayValue = JSON . stringify ( value ) ;
558
649
559
650
return (
@@ -593,63 +684,29 @@ const Log: FC<LogProps> = ({ log }) => {
593
684
) ;
594
685
} ;
595
686
596
- type FormProps = { parameters : ParameterWithSource [ ] } ;
597
-
598
- const Form : FC < FormProps > = ( { parameters } ) => {
599
- return parameters
600
- . sort ( ( a , b ) => a . order - b . order )
601
- . map ( ( p , index ) => < FormElement key = { index } parameter = { p } /> ) ;
602
- } ;
603
-
604
- type FormElementProps = { parameter : ParameterWithSource } ;
605
- const FormElement : FC < FormElementProps > = ( { parameter } ) => {
606
- const $form = useStore ( ( state ) => state . form ) ;
607
- const $setForm = useStore ( ( state ) => state . setFormState ) ;
687
+ const ViewOutput : FC = ( ) => {
688
+ const $parameters = useStore ( ( state ) => state . parameters ) ;
608
689
609
- const value = useMemo (
610
- ( ) =>
611
- $form [ parameter . name ] ??
612
- ( parameter . default_value . value === "??"
613
- ? ""
614
- : parameter . default_value . value ) ,
615
- [ $form , parameter ] ,
690
+ const isInvalid = useMemo (
691
+ ( ) => $parameters . length === 0 || $parameters . some ( ( p ) => ! p . value . valid ) ,
692
+ [ $parameters ] ,
616
693
) ;
617
694
618
- const onValueChange = ( value : string ) => {
619
- $setForm ( parameter . name , value ) ;
620
- } ;
621
-
622
- return (
623
- < DynamicParameter
624
- parameter = { parameter }
625
- value = { value }
626
- autofill = { false }
627
- onChange = { onValueChange }
628
- disabled = { parameter . styling . disabled }
629
- />
695
+ const data = useMemo (
696
+ ( ) =>
697
+ $parameters . reduce < Record < string , string > > ( ( acc , p ) => {
698
+ acc [ p . name ] = p . value . value ;
699
+ return acc ;
700
+ } , { } ) ,
701
+ [ $parameters ] ,
630
702
) ;
631
- } ;
632
-
633
- const UserSelect : FC = ( ) => {
634
- const $setWorkspaceOwner = useStore ( ( state ) => state . setWorkspaceOwner ) ;
635
703
636
704
return (
637
- < Select
638
- defaultValue = "admin"
639
- onValueChange = { ( value ) => {
640
- const users : Record < string , WorkspaceOwner | undefined > = mockUsers ;
641
- $setWorkspaceOwner ( users [ value ] ?? mockUsers . admin ) ;
642
- } }
643
- >
644
- < SelectTrigger className = "w-fit min-w-40" >
645
- < SelectValue />
646
- </ SelectTrigger >
647
- < SelectContent >
648
- < SelectItem value = "admin" > Administrator</ SelectItem >
649
- < SelectItem value = "developer" > Developer</ SelectItem >
650
- < SelectItem value = "contractor" > Contractor</ SelectItem >
651
- < SelectItem value = "eu-developer" > EU Developer</ SelectItem >
652
- </ SelectContent >
653
- </ Select >
705
+ < TableDrawer data = { data } headers = { [ "Parameter" , "Value" ] } >
706
+ < Button variant = "default" disabled = { isInvalid } >
707
+ < SearchCodeIcon />
708
+ View output
709
+ </ Button >
710
+ </ TableDrawer >
654
711
) ;
655
712
} ;
0 commit comments