Skip to content

Commit 2190bee

Browse files
authored
Merge pull request #19 from coder/brett/check-output
feat: form output
2 parents 428595e + 19b0518 commit 2190bee

File tree

1 file changed

+135
-78
lines changed

1 file changed

+135
-78
lines changed

src/client/Preview.tsx

Lines changed: 135 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,18 @@ import {
3131
LoaderIcon,
3232
PlayIcon,
3333
ScrollTextIcon,
34+
SearchCodeIcon,
3435
XIcon,
3536
} from "lucide-react";
3637
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";
3846
import { useSearchParams } from "react-router";
3947
import {
4048
Select,
@@ -196,7 +204,7 @@ export const Preview: FC = () => {
196204
($errors.show && $errors.diagnostics.length > 0)
197205
}
198206
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 ",
200208
($wasmState !== "loaded" ||
201209
($errors.show && $errors.diagnostics.length > 0)) &&
202210
"pointer-events-none",
@@ -226,16 +234,7 @@ export const Preview: FC = () => {
226234
) : null}
227235
</AnimatePresence>
228236
</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 />
239238
</div>
240239
}
241240
{$parameters.length === 0 ? (
@@ -247,6 +246,12 @@ export const Preview: FC = () => {
247246
<Form parameters={$parameters} />
248247
</div>
249248
)}
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>
250255
</div>
251256
</Tabs.Content>
252257

@@ -486,15 +491,17 @@ const LogsEmptyState = () => {
486491

487492
type LogProps = { log: ParserLog };
488493
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+
);
490501

491502
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
498505
className={cn(
499506
"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",
500507
log.level.toLowerCase() === "info" && "border-l-content-link",
@@ -509,7 +516,91 @@ const Log: FC<LogProps> = ({ log }) => {
509516
<p className="col-span-6 break-all p-2 text-left font-mono text-content-primary text-xs">
510517
{JSON.stringify(log)}
511518
</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>
513604

514605
<Dialog.Portal forceMount={true}>
515606
<AnimatePresence propagate={true}>
@@ -528,11 +619,11 @@ const Log: FC<LogProps> = ({ log }) => {
528619
initial={{ opacity: 0, transform: "translateX(100px)" }}
529620
animate={{ opacity: 1, transform: "translateX(0px)" }}
530621
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"
532623
>
533624
<div className="flex items-center justify-between">
534625
<Dialog.Title className="font-semibold text-2xl text-content-primary">
535-
Log
626+
Parameter Values
536627
</Dialog.Title>
537628
<Dialog.Close asChild={true}>
538629
<Button
@@ -544,16 +635,16 @@ const Log: FC<LogProps> = ({ log }) => {
544635
</Button>
545636
</Dialog.Close>
546637
</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">
548639
<div className="grid grid-cols-8 border-b bg-surface-secondary">
549640
<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>
551642
</div>
552643
<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>
554645
</div>
555646
</div>
556-
{Object.entries(log).map(([key, value], index) => {
647+
{Object.entries(data).map(([key, value], index) => {
557648
const displayValue = JSON.stringify(value);
558649

559650
return (
@@ -593,63 +684,29 @@ const Log: FC<LogProps> = ({ log }) => {
593684
);
594685
};
595686

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);
608689

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],
616693
);
617694

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],
630702
);
631-
};
632-
633-
const UserSelect: FC = () => {
634-
const $setWorkspaceOwner = useStore((state) => state.setWorkspaceOwner);
635703

636704
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>
654711
);
655712
};

0 commit comments

Comments
 (0)