blob: be624a3ad7a9ab3e58c28addbed74485273f0acd [file] [log] [blame]
import { useStateStore, AppNode, GatewayHttpsNode, ServiceNode, nodeLabel, useEnv, nodeIsConnectable } from '@/lib/state';
import { Handle, Position, useNodes } from '@xyflow/react';
import { NodeRect } from './node-rect';
import { useEffect, useMemo } from 'react';
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, EventType, DeepPartial } from 'react-hook-form';
import { Form, FormControl, FormField, FormItem, FormMessage } from './ui/form';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
const schema = z.object({
network: z.string().min(1, "reqired"),
subdomain: z.string().min(1, "required"),
});
const connectedToSchema = z.object({
id: z.string(),
portId: z.string(),
});
export function NodeGatewayHttps(node: GatewayHttpsNode) {
const { id, selected } = node;
const isConnectable = useMemo(() => nodeIsConnectable(node, "https"), [node]);
return (
<NodeRect id={id} selected={selected} type={node.type}>
{nodeLabel(node)}
<Handle
type={"target"}
id="https"
position={Position.Bottom}
isConnectable={isConnectable}
isConnectableStart={isConnectable}
isConnectableEnd={isConnectable}
/>
</NodeRect>
);
}
export function NodeGatewayHttpsDetails({ id, data }: GatewayHttpsNode) {
const store = useStateStore();
const env = useEnv();
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
mode: "onChange",
defaultValues: {
network: data.network,
subdomain: data.subdomain,
},
});
useEffect(() => {
const sub = form.watch((value: DeepPartial<z.infer<typeof schema>>, { name }: { name?: keyof z.infer<typeof schema> | undefined, type?: EventType | undefined }) => {
if (name === "network") {
if (value.network !== undefined) {
store.updateNodeData<"gateway-https">(id, { network: value.network });
} else {
}
} else if (name === "subdomain") {
store.updateNodeData<"gateway-https">(id, { subdomain: value.subdomain });
}
});
return () => sub.unsubscribe();
}, [form, store]);
const connectedToForm = useForm<z.infer<typeof connectedToSchema>>({
resolver: zodResolver(connectedToSchema),
mode: "onChange",
defaultValues: {
id: data.https?.serviceId,
portId: data.https?.portId,
},
});
useEffect(() => {
connectedToForm.reset({
id: data.https?.serviceId,
portId: data.https?.portId,
});
}, [connectedToForm, data]);
const nodes = useNodes<AppNode>();
const selected = useMemo(() => {
if (data !== undefined && data.https !== undefined) {
const https = data.https;
return nodes.find((n) => n.id === https.serviceId)! as ServiceNode;
}
return null;
}, [data]);
const selectable = useMemo(() => {
return nodes.filter((n) => {
if (n.id === id) {
return false;
}
if (selected !== null && selected.id === id) {
return true;
}
if (n.type !== "app") {
return false;
}
return n.data && n.data.ports && n.data.ports.length > 0;
})
}, [nodes, selected]);
useEffect(() => {
const sub = connectedToForm.watch((value: DeepPartial<z.infer<typeof connectedToSchema>>, { name, type }: { name?: keyof z.infer<typeof connectedToSchema> | undefined, type?: EventType | undefined }) => {
console.log({ name, type });
if (type !== "change") {
return;
}
switch (name) {
case "id":
if (!value.id) {
break;
}
const current = store.edges.filter((e) => e.target === id);
const cid = current[0] ? current[0].id : undefined;
store.replaceEdge({
source: value.id,
sourceHandle: "ports",
target: id,
targetHandle: "https",
}, cid);
break;
case "portId":
store.updateNodeData<"gateway-https">(id, {
https: {
serviceId: value.id,
portId: value.portId,
}
});
break;
}
});
return () => sub.unsubscribe();
}, [connectedToForm, store, selectable]);
return (
<>
<Form {...form}>
<form className="space-y-2">
<FormField
control={form.control}
name="network"
render={({ field }) => (
<FormItem>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Network" />
</SelectTrigger>
</FormControl>
<SelectContent>
{env.networks.map((n) => (
<SelectItem key={n.name} value={n.domain}>{`${n.name} - ${n.domain}`}</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="subdomain"
render={({ field }) => (
<FormItem>
<FormControl>
<Input placeholder="subdomain" className="border border-black" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
<Form {...connectedToForm}>
<form className="space-y-2">
<FormField
control={connectedToForm.control}
name="id"
render={({ field }) => (
<FormItem>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Service" />
</SelectTrigger>
</FormControl>
<SelectContent>
{selectable.map((n) => (
<SelectItem key={n.id} value={n.id}>{nodeLabel(n)}</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={connectedToForm.control}
name="portId"
render={({ field }) => (
<FormItem>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Port" />
</SelectTrigger>
</FormControl>
<SelectContent>
{selected && selected.data.ports.map((p) => (
<SelectItem key={p.id} value={p.id}>{p.name} - {p.value}</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
</>
);
}