blob: 14d5b4cac763cec88e69e674fa2291b3fceb00fa [file] [log] [blame]
giod0026612025-05-08 13:00:36 +00001import { NodeRect } from "./node-rect";
2import { nodeIsConnectable, nodeLabel, useStateStore, VolumeNode } from "@/lib/state";
3import { useEffect, useMemo } from "react";
gio5f2f1002025-03-20 18:38:48 +04004import { z } from "zod";
giod0026612025-05-08 13:00:36 +00005import { DeepPartial, EventType, useForm } from "react-hook-form";
6import { zodResolver } from "@hookform/resolvers/zod";
7import { Form, FormControl, FormField, FormItem, FormMessage } from "./ui/form";
8import { Input } from "./ui/input";
gio5f2f1002025-03-20 18:38:48 +04009import { Handle, Position } from "@xyflow/react";
10import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
11
12export function NodeVolume(node: VolumeNode) {
giod0026612025-05-08 13:00:36 +000013 const { id, data, selected } = node;
14 const isConnectable = useMemo(() => nodeIsConnectable(node, "volume"), [node]);
15 return (
16 <NodeRect id={id} selected={selected} type={node.type} state={node.data.state}>
17 <div style={{ padding: "10px 20px" }}>
18 <div>{nodeLabel(node)}</div>
19 <div>{data.type && `${data.type}`}</div>
20 <div>{data.size && `${data.size}`}</div>
21 <Handle
22 id="volume"
23 type={"source"}
24 position={Position.Top}
25 isConnectableStart={isConnectable}
26 isConnectableEnd={isConnectable}
27 isConnectable={isConnectable}
28 />
29 </div>
30 </NodeRect>
31 );
gio5f2f1002025-03-20 18:38:48 +040032}
33
34const volumeTypes = ["ReadWriteOnce", "ReadOnlyMany", "ReadWriteMany", "ReadWriteOncePod"] as const;
35
36const schema = z.object({
giod0026612025-05-08 13:00:36 +000037 name: z.string().min(1),
38 type: z.enum(volumeTypes),
39 size: z.string().min(1).default("1Gi"),
gio5f2f1002025-03-20 18:38:48 +040040});
41
42export function NodeVolumeDetails({ id, data }: VolumeNode) {
giod0026612025-05-08 13:00:36 +000043 const store = useStateStore();
44 const form = useForm<z.infer<typeof schema>>({
45 resolver: zodResolver(schema),
46 mode: "onChange",
47 defaultValues: {
48 name: "",
49 type: undefined,
50 size: "",
51 },
52 });
53 useEffect(() => {
54 const sub = form.watch(
55 (
56 value: DeepPartial<z.infer<typeof schema>>,
57 { name, type }: { name?: keyof z.infer<typeof schema> | undefined; type?: EventType | undefined },
58 ) => {
59 if (type !== "change") {
60 return;
61 }
62 console.log({ name, type, value });
63 store.updateNodeData<"volume">(id, {
64 label: value.name,
65 type: value.type,
66 size: value.size,
67 });
68 },
69 );
70 return () => sub.unsubscribe();
71 }, [id, form, store]);
72 useEffect(() => {
73 form.reset({
74 name: data.label,
75 type: data.type,
76 size: data.size,
77 });
78 }, [form, data]);
79 return (
80 <>
81 <Form {...form}>
82 <form className="space-y-2">
83 <FormField
84 control={form.control}
85 name="name"
86 render={({ field }) => (
87 <FormItem>
88 <FormControl>
89 <Input placeholder="name" className="border border-black" {...field} />
90 </FormControl>
91 <FormMessage />
92 </FormItem>
93 )}
94 />
95 <FormField
96 control={form.control}
97 name="type"
98 render={({ field }) => (
99 <FormItem>
100 <Select onValueChange={field.onChange} defaultValue={field.value}>
101 <FormControl>
102 <SelectTrigger>
103 <SelectValue placeholder="Volume Type" />
104 </SelectTrigger>
105 </FormControl>
106 <SelectContent>
107 {volumeTypes.map((t) => (
108 <SelectItem key={t} value={t}>
109 {t}
110 </SelectItem>
111 ))}
112 </SelectContent>
113 </Select>
114 <FormMessage />
115 </FormItem>
116 )}
117 />
118 <FormField
119 control={form.control}
120 name="size"
121 render={({ field }) => (
122 <FormItem>
123 <FormControl>
124 <Input placeholder="size" className="border border-black" {...field} />
125 </FormControl>
126 <FormMessage />
127 </FormItem>
128 )}
129 />
130 </form>
131 </Form>
132 </>
133 );
134}