Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 135 additions & 78 deletions src/client/Preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,18 @@ import {
LoaderIcon,
PlayIcon,
ScrollTextIcon,
SearchCodeIcon,
XIcon,
} from "lucide-react";
import { AnimatePresence, motion } from "motion/react";
import { type FC, useCallback, useEffect, useMemo, useState } from "react";
import {
type FC,
type PropsWithChildren,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { useSearchParams } from "react-router";
import {
Select,
Expand Down Expand Up @@ -196,7 +204,7 @@ export const Preview: FC = () => {
($errors.show && $errors.diagnostics.length > 0)
}
className={cn(
"flex h-full w-full flex-col items-start gap-6 p-6 ",
"flex h-full w-full flex-col items-start gap-4 p-5 ",
($wasmState !== "loaded" ||
($errors.show && $errors.diagnostics.length > 0)) &&
"pointer-events-none",
Expand Down Expand Up @@ -226,16 +234,7 @@ export const Preview: FC = () => {
) : null}
</AnimatePresence>
</div>
<div className="flex w-full items-center justify-end gap-3">
<UserSelect />
<Button
variant="destructive"
onClick={$resetForm}
className="w-fit"
>
Reset form
</Button>
</div>
<UserSelect />
</div>
}
{$parameters.length === 0 ? (
Expand All @@ -247,6 +246,12 @@ export const Preview: FC = () => {
<Form parameters={$parameters} />
</div>
)}
<div className="flex w-full justify-between gap-3">
<Button variant="outline" onClick={$resetForm} className="w-fit">
Reset form
</Button>
<ViewOutput />
</div>
</div>
</Tabs.Content>

Expand Down Expand Up @@ -486,15 +491,17 @@ const LogsEmptyState = () => {

type LogProps = { log: ParserLog };
const Log: FC<LogProps> = ({ log }) => {
const [showTable, setShowTable] = useState(() => false);
const data = Object.entries(log).reduce<Record<string, unknown>>(
(acc, [key, value]) => {
acc[key] = value;
return acc;
},
{},
);

return (
<Dialog.Root
modal={true}
open={showTable}
onOpenChange={(show) => setShowTable(() => show)}
>
<Dialog.Trigger
<TableDrawer data={data}>
<button
className={cn(
"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",
log.level.toLowerCase() === "info" && "border-l-content-link",
Expand All @@ -509,7 +516,91 @@ const Log: FC<LogProps> = ({ log }) => {
<p className="col-span-6 break-all p-2 text-left font-mono text-content-primary text-xs">
{JSON.stringify(log)}
</p>
</Dialog.Trigger>
</button>
</TableDrawer>
);
};

type FormProps = { parameters: ParameterWithSource[] };

const Form: FC<FormProps> = ({ parameters }) => {
return parameters
.sort((a, b) => a.order - b.order)
.map((p, index) => <FormElement key={index} parameter={p} />);
};

type FormElementProps = { parameter: ParameterWithSource };
const FormElement: FC<FormElementProps> = ({ parameter }) => {
const $form = useStore((state) => state.form);
const $setForm = useStore((state) => state.setFormState);

const value = useMemo(
() =>
$form[parameter.name] ??
(parameter.default_value.value === "??"
? ""
: parameter.default_value.value),
[$form, parameter],
);

const onValueChange = (value: string) => {
$setForm(parameter.name, value);
};

return (
<DynamicParameter
parameter={parameter}
value={value}
autofill={false}
onChange={onValueChange}
disabled={parameter.styling.disabled}
/>
);
};

const UserSelect: FC = () => {
const $setWorkspaceOwner = useStore((state) => state.setWorkspaceOwner);

return (
<Select
defaultValue="admin"
onValueChange={(value) => {
const users: Record<string, WorkspaceOwner | undefined> = mockUsers;
$setWorkspaceOwner(users[value] ?? mockUsers.admin);
}}
>
<SelectTrigger className="w-fit min-w-40">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="admin">Administrator</SelectItem>
<SelectItem value="developer">Developer</SelectItem>
<SelectItem value="contractor">Contractor</SelectItem>
<SelectItem value="eu-developer">EU Developer</SelectItem>
</SelectContent>
</Select>
);
};

type TableDrawerProps = {
data: Record<string, unknown>;
headers?: [string, string];
} & PropsWithChildren;

const TableDrawer: FC<TableDrawerProps> = ({
data,
headers = ["field", "value"],
children,
}) => {
const [showTable, setShowTable] = useState(() => false);

return (
<Dialog.Root
modal={true}
open={showTable}
onOpenChange={(show) => setShowTable(() => show)}
>
<Dialog.Trigger asChild={true}>{children}</Dialog.Trigger>

<Dialog.Portal forceMount={true}>
<AnimatePresence propagate={true}>
Expand All @@ -528,11 +619,11 @@ const Log: FC<LogProps> = ({ log }) => {
initial={{ opacity: 0, transform: "translateX(100px)" }}
animate={{ opacity: 1, transform: "translateX(0px)" }}
exit={{ opacity: 0, transform: "translateX(100px)" }}
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"
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"
>
<div className="flex items-center justify-between">
<Dialog.Title className="font-semibold text-2xl text-content-primary">
Log
Parameter Values
</Dialog.Title>
<Dialog.Close asChild={true}>
<Button
Expand All @@ -544,16 +635,16 @@ const Log: FC<LogProps> = ({ log }) => {
</Button>
</Dialog.Close>
</div>
<div className="flex w-full flex-col overflow-clip rounded-lg border font-mono text-content-primary text-xs">
<div className="flex w-full flex-col overflow-scroll rounded-lg border font-mono text-content-primary text-xs">
<div className="grid grid-cols-8 border-b bg-surface-secondary">
<div className="col-span-2 flex min-h-8 items-center border-r px-2 py-1">
<p className="text-left uppercase">field</p>
<p className="text-left uppercase">{headers[0]}</p>
</div>
<div className="col-span-6 flex min-h-8 items-center px-2 py-1">
<p className="text-left uppercase">value</p>
<p className="text-left uppercase">{headers[1]}</p>
</div>
</div>
{Object.entries(log).map(([key, value], index) => {
{Object.entries(data).map(([key, value], index) => {
const displayValue = JSON.stringify(value);

return (
Expand Down Expand Up @@ -593,63 +684,29 @@ const Log: FC<LogProps> = ({ log }) => {
);
};

type FormProps = { parameters: ParameterWithSource[] };

const Form: FC<FormProps> = ({ parameters }) => {
return parameters
.sort((a, b) => a.order - b.order)
.map((p, index) => <FormElement key={index} parameter={p} />);
};

type FormElementProps = { parameter: ParameterWithSource };
const FormElement: FC<FormElementProps> = ({ parameter }) => {
const $form = useStore((state) => state.form);
const $setForm = useStore((state) => state.setFormState);
const ViewOutput: FC = () => {
const $parameters = useStore((state) => state.parameters);

const value = useMemo(
() =>
$form[parameter.name] ??
(parameter.default_value.value === "??"
? ""
: parameter.default_value.value),
[$form, parameter],
const isInvalid = useMemo(
() => $parameters.length === 0 || $parameters.some((p) => !p.value.valid),
[$parameters],
);

const onValueChange = (value: string) => {
$setForm(parameter.name, value);
};

return (
<DynamicParameter
parameter={parameter}
value={value}
autofill={false}
onChange={onValueChange}
disabled={parameter.styling.disabled}
/>
const data = useMemo(
() =>
$parameters.reduce<Record<string, string>>((acc, p) => {
acc[p.name] = p.value.value;
return acc;
}, {}),
[$parameters],
);
};

const UserSelect: FC = () => {
const $setWorkspaceOwner = useStore((state) => state.setWorkspaceOwner);

return (
<Select
defaultValue="admin"
onValueChange={(value) => {
const users: Record<string, WorkspaceOwner | undefined> = mockUsers;
$setWorkspaceOwner(users[value] ?? mockUsers.admin);
}}
>
<SelectTrigger className="w-fit min-w-40">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="admin">Administrator</SelectItem>
<SelectItem value="developer">Developer</SelectItem>
<SelectItem value="contractor">Contractor</SelectItem>
<SelectItem value="eu-developer">EU Developer</SelectItem>
</SelectContent>
</Select>
<TableDrawer data={data} headers={["Parameter", "Value"]}>
<Button variant="default" disabled={isInvalid}>
<SearchCodeIcon />
View output
</Button>
</TableDrawer>
);
};