Skip to content
Merged
3 changes: 3 additions & 0 deletions packages/connect-react/src/components/Control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ControlBoolean } from "./ControlBoolean";
import { ControlInput } from "./ControlInput";
import { ControlObject } from "./ControlObject";
import { ControlSelect } from "./ControlSelect";
import { ControlSql } from "./ControlSql";
import { RemoteOptionsContainer } from "./RemoteOptionsContainer";

export type ControlProps<T extends ConfigurableProps, U extends ConfigurableProp> = {
Expand Down Expand Up @@ -82,6 +83,8 @@ export function Control<T extends ConfigurableProps, U extends ConfigurableProp>
return <ControlInput />;
case "object":
return <ControlObject />;
case "sql":
return <ControlSql />;
default:
// TODO "not supported prop type should bubble up"
throw new Error("Unsupported property type: " + prop.type);
Expand Down
64 changes: 64 additions & 0 deletions packages/connect-react/src/components/ControlSql.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useFormFieldContext } from "../hooks/form-field-context";
import { useFormContext } from "../hooks/form-context";
import { useCustomize } from "../hooks/customization-context";
import type { CSSProperties } from "react";

export function ControlSql() {
const formFieldContext = useFormFieldContext();
const formContext = useFormContext();
const {
id, onChange, prop, value,
} = formFieldContext;
const {
getProps, theme,
} = useCustomize();

// Find the first app prop to determine which database app to use
const appProp = formContext.configurableProps.find((p: any) => p.type === "app");
const appName = appProp?.app || "postgresql"; // Default to postgresql

// Extract the query string from the structured value or use empty string
const queryValue = typeof value === "object" && value && "query" in value
? (value as any).query
: typeof value === "string"
? value
: "";

const handleChange = (queryText: string) => {
// Transform the simple query string into the structured SQL object
const sqlObject = {
app: appName,
query: queryText,
params: [], // For now, always empty array
};
onChange(sqlObject);
};

const baseStyles: CSSProperties = {
color: theme.colors.neutral60,
display: "block",
border: "1px solid",
borderColor: theme.colors.neutral20,
padding: 6,
width: "100%",
borderRadius: theme.borderRadius,
gridArea: "control",
boxShadow: theme.boxShadow.input,
fontSize: "0.875rem",
fontFamily: "monospace",
minHeight: "120px",
resize: "vertical",
};

return (
<textarea
id={id}
name={prop.name}
value={queryValue}
onChange={(e) => handleChange(e.target.value)}
placeholder="SELECT * FROM table_name"
required={!prop.optional}
{...getProps("controlSql", baseStyles, formFieldContext)}
/>
);
}
2 changes: 2 additions & 0 deletions packages/connect-react/src/hooks/customization-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { ControlBoolean } from "../components/ControlBoolean";
import { ControlInput } from "../components/ControlInput";
import { ControlObject } from "../components/ControlObject";
import { ControlSelect } from "../components/ControlSelect";
import { ControlSql } from "../components/ControlSql";
import { ControlSubmit } from "../components/ControlSubmit";
import { Description } from "../components/Description";
import { Errors } from "../components/Errors";
Expand Down Expand Up @@ -73,6 +74,7 @@ export type CustomizableProps = {
controlBoolean: ComponentProps<typeof ControlBoolean> & FormFieldContext<ConfigurableProp>;
controlInput: ComponentProps<typeof ControlInput> & FormFieldContext<ConfigurableProp>;
controlObject: ComponentProps<typeof ControlObject> & FormFieldContext<ConfigurableProp>;
controlSql: ComponentProps<typeof ControlSql> & FormFieldContext<ConfigurableProp>;
controlSubmit: ComponentProps<typeof ControlSubmit>;
description: ComponentProps<typeof Description>;
error: ComponentProps<typeof Errors>;
Expand Down
1 change: 1 addition & 0 deletions packages/connect-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { ControlApp } from "./components/ControlApp";
export { ControlBoolean } from "./components/ControlBoolean";
export { ControlInput } from "./components/ControlInput";
export { ControlSelect } from "./components/ControlSelect";
export { ControlSql } from "./components/ControlSql";
export { ControlSubmit } from "./components/ControlSubmit";
export { Description } from "./components/Description";
export { ErrorBoundary } from "./components/ErrorBoundary";
Expand Down
7 changes: 6 additions & 1 deletion packages/sdk/src/shared/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,14 @@ export type ConfigurablePropStringArray = BaseConfigurableProp & {
type: "string[]";
secret?: boolean; // TODO is this supported
} & Defaultable<string[]>; // TODO
export type ConfigurablePropSql = BaseConfigurableProp & {
type: "sql";
} & Defaultable<string>;
// | { type: "$.interface.http" } // source only
// | { type: "$.interface.timer" } // source only
// | { type: "$.service.db" }
// | { type: "data_store" }
// | { type: "http_request" }
// | { type: "sql" } -- not in component api docs!
export type ConfigurableProp =
| ConfigurablePropAlert
| ConfigurablePropAny
Expand All @@ -101,6 +103,7 @@ export type ConfigurableProp =
| ConfigurablePropObject
| ConfigurablePropString
| ConfigurablePropStringArray
| ConfigurablePropSql
| (BaseConfigurableProp & { type: "$.discord.channel"; });

export type ConfigurableProps = Readonly<ConfigurableProp[]>;
Expand All @@ -121,6 +124,8 @@ export type PropValue<T extends ConfigurableProp["type"]> = T extends "alert"
? string
: T extends "string[]"
? string[] // XXX support arrays differently?
: T extends "sql"
? { app: string; query: string; params: any[]; }
: never;

export type ConfiguredProps<T extends ConfigurableProps> = {
Expand Down
Loading