Canvas: Disable all inputs during non-edit mode
Change-Id: Ifca28e7cb78cb38175d5463311ff3d5006d412f8
diff --git a/apps/canvas/front/src/components/details.tsx b/apps/canvas/front/src/components/details.tsx
index cb5aba5..288390a 100644
--- a/apps/canvas/front/src/components/details.tsx
+++ b/apps/canvas/front/src/components/details.tsx
@@ -1,5 +1,5 @@
import { useNodes } from "@xyflow/react";
-import { AppNode, nodeLabel, NodeType } from "@/lib/state";
+import { AppNode, nodeLabel, NodeType, useMode } from "@/lib/state";
import { NodeDetails } from "@/components/node-details";
import { Accordion, AccordionContent, AccordionTrigger } from "./ui/accordion";
import { AccordionItem } from "@radix-ui/react-accordion";
@@ -41,6 +41,9 @@
const [open, setOpen] = useState<string[]>([]);
const selected = useMemo(() => nodes.filter((n) => n.selected).map((n) => n.id), [nodes]);
const all = useMemo(() => open.concat(selected).filter(unique), [open, selected]);
+ const mode = useMode();
+ const isDeployMode = mode === "deploy";
+
return (
<Accordion
type="multiple"
@@ -59,7 +62,7 @@
</div>
</AccordionTrigger>
<AccordionContent className="pt-1">
- <NodeDetails {...n} />
+ <NodeDetails {...n} disabled={isDeployMode} />
</AccordionContent>
</AccordionItem>
</>
diff --git a/apps/canvas/front/src/components/node-app.tsx b/apps/canvas/front/src/components/node-app.tsx
index f01bd98..7516d19 100644
--- a/apps/canvas/front/src/components/node-app.tsx
+++ b/apps/canvas/front/src/components/node-app.tsx
@@ -91,7 +91,7 @@
subdomain: z.string().min(1, "required"),
});
-export function NodeAppDetails({ id, data }: ServiceNode) {
+export function NodeAppDetails({ id, data, disabled }: ServiceNode & { disabled?: boolean }) {
const store = useStateStore();
const nodes = useNodes<AppNode>();
const env = useEnv();
@@ -640,6 +640,7 @@
className="lowercase"
{...field}
ref={focus(field, "name")}
+ disabled={disabled}
/>
</FormControl>
<FormMessage />
@@ -651,7 +652,12 @@
name="type"
render={({ field }) => (
<FormItem>
- <Select onValueChange={field.onChange} defaultValue={field.value} {...typeProps}>
+ <Select
+ onValueChange={field.onChange}
+ defaultValue={field.value}
+ {...typeProps}
+ disabled={disabled}
+ >
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Runtime" />
@@ -679,7 +685,7 @@
name="id"
render={({ field }) => (
<FormItem>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
+ <Select onValueChange={field.onChange} defaultValue={field.value} disabled={disabled}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Repository" />
@@ -708,7 +714,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="master" className="lowercase" {...field} />
+ <Input placeholder="master" className="lowercase" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
@@ -720,7 +726,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="/" {...field} />
+ <Input placeholder="/" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
@@ -739,6 +745,7 @@
variant={"ghost"}
onClick={() => removePort(p.id)}
className="w-4 h-4"
+ disabled={disabled}
>
<XIcon />
</Button>
@@ -756,7 +763,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="name" className="lowercase" {...field} />
+ <Input placeholder="name" className="lowercase" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
@@ -768,13 +775,15 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="value" {...field} />
+ <Input placeholder="value" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
- <Button type="submit">Add Port</Button>
+ <Button type="submit" disabled={disabled}>
+ Add Port
+ </Button>
</form>
</Form>
Env Vars
@@ -794,6 +803,7 @@
onKeyUp={saveAliasOnEnter(v)}
onBlur={saveAliasOnBlur(v)}
autoFocus={true}
+ disabled={disabled}
/>
</li>
);
@@ -823,6 +833,7 @@
placeholder="new line separated list of commands to run before running the service"
value={data.preBuildCommands}
onChange={setPreBuildCommands}
+ disabled={disabled}
/>
Dev
<Form {...devForm}>
@@ -833,7 +844,12 @@
render={({ field }) => (
<FormItem>
<div className="flex flex-row gap-1 items-center">
- <Checkbox id="devEnabled" onCheckedChange={field.onChange} checked={field.value} />
+ <Checkbox
+ id="devEnabled"
+ onCheckedChange={field.onChange}
+ checked={field.value}
+ disabled={disabled}
+ />
<Label htmlFor="devEnabled">Enabled</Label>
</div>
<FormMessage />
@@ -850,7 +866,11 @@
name="network"
render={({ field }) => (
<FormItem>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
+ <Select
+ onValueChange={field.onChange}
+ defaultValue={field.value}
+ disabled={disabled}
+ >
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Network" />
@@ -875,7 +895,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="subdomain" {...field} />
+ <Input placeholder="subdomain" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
diff --git a/apps/canvas/front/src/components/node-details.tsx b/apps/canvas/front/src/components/node-details.tsx
index 503d5ee..2d9cd58 100644
--- a/apps/canvas/front/src/components/node-details.tsx
+++ b/apps/canvas/front/src/components/node-details.tsx
@@ -7,7 +7,11 @@
import { NodeGithubDetails } from "./node-github";
import { NodeGatewayTCPDetails } from "./node-gateway-tcp";
-export function NodeDetails(props: AppNode) {
+type NodeDetailsProps = AppNode & {
+ disabled?: boolean;
+};
+
+export function NodeDetails(props: NodeDetailsProps) {
return (
<div className="px-1 flex flex-col gap-2">
<NodeDetailsImpl {...props} />
@@ -15,7 +19,7 @@
);
}
-function NodeDetailsImpl(props: AppNode) {
+function NodeDetailsImpl(props: NodeDetailsProps) {
switch (props.type) {
case "app":
return <NodeAppDetails {...props} />;
diff --git a/apps/canvas/front/src/components/node-gateway-https.tsx b/apps/canvas/front/src/components/node-gateway-https.tsx
index 3785d59..e88902d 100644
--- a/apps/canvas/front/src/components/node-gateway-https.tsx
+++ b/apps/canvas/front/src/components/node-gateway-https.tsx
@@ -71,7 +71,7 @@
);
}
-export function NodeGatewayHttpsDetails({ id, data }: GatewayHttpsNode) {
+export function NodeGatewayHttpsDetails({ id, data, disabled }: GatewayHttpsNode & { disabled?: boolean }) {
const store = useStateStore();
const env = useEnv();
const form = useForm<z.infer<typeof schema>>({
@@ -301,7 +301,7 @@
<Select
onValueChange={field.onChange}
defaultValue={field.value}
- disabled={data.readonly}
+ disabled={data.readonly || disabled}
>
<FormControl>
<SelectTrigger>
@@ -327,7 +327,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="subdomain" {...field} disabled={data.readonly} />
+ <Input placeholder="subdomain" {...field} disabled={data.readonly || disabled} />
</FormControl>
<FormMessage />
</FormItem>
@@ -345,7 +345,7 @@
<Select
onValueChange={field.onChange}
defaultValue={field.value}
- disabled={data.readonly}
+ disabled={data.readonly || disabled}
>
<FormControl>
<SelectTrigger>
@@ -372,7 +372,7 @@
<Select
onValueChange={field.onChange}
defaultValue={field.value}
- disabled={data.readonly}
+ disabled={data.readonly || disabled}
>
<FormControl>
<SelectTrigger>
@@ -403,7 +403,12 @@
render={({ field }) => (
<FormItem>
<div className="flex flex-row gap-1 items-center">
- <Checkbox id="authEnabled" onCheckedChange={field.onChange} checked={field.value} />
+ <Checkbox
+ id="authEnabled"
+ onCheckedChange={field.onChange}
+ checked={field.value}
+ disabled={disabled}
+ />
<Label htmlFor="authEnabled">Enabled</Label>
</div>
<FormMessage />
@@ -418,7 +423,12 @@
<ul>
{(data.auth.groups || []).map((p) => (
<li key={p} className="flex flex-row gap-1 items-center">
- <Button size={"icon"} variant={"ghost"} onClick={() => removeGroup(p)}>
+ <Button
+ size={"icon"}
+ variant={"ghost"}
+ onClick={() => removeGroup(p)}
+ disabled={disabled}
+ >
<XIcon />
</Button>
<div>{p}</div>
@@ -433,20 +443,27 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="group" {...field} />
+ <Input placeholder="group" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
- <Button type="submit">Add Group</Button>
+ <Button type="submit" disabled={disabled}>
+ Add Group
+ </Button>
</form>
</Form>
Auth optional path patterns
<ul>
{(data.auth.noAuthPathPatterns || []).map((p) => (
<li key={p} className="flex flex-row gap-1 items-center">
- <Button size={"icon"} variant={"ghost"} onClick={() => removeNoAuthPathPattern(p)}>
+ <Button
+ size={"icon"}
+ variant={"ghost"}
+ onClick={() => removeNoAuthPathPattern(p)}
+ disabled={disabled}
+ >
<XIcon />
</Button>
<div>{p}</div>
@@ -464,13 +481,15 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="group" {...field} />
+ <Input placeholder="group" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
- <Button type="submit">Add</Button>
+ <Button type="submit" disabled={disabled}>
+ Add
+ </Button>
</form>
</Form>
</>
diff --git a/apps/canvas/front/src/components/node-gateway-tcp.tsx b/apps/canvas/front/src/components/node-gateway-tcp.tsx
index 86fa493..bd1e5b6 100644
--- a/apps/canvas/front/src/components/node-gateway-tcp.tsx
+++ b/apps/canvas/front/src/components/node-gateway-tcp.tsx
@@ -48,7 +48,7 @@
);
}
-export function NodeGatewayTCPDetails({ id, data }: GatewayTCPNode) {
+export function NodeGatewayTCPDetails({ id, data, disabled }: GatewayTCPNode & { disabled?: boolean }) {
const store = useStateStore();
const env = useEnv();
const form = useForm<z.infer<typeof schema>>({
@@ -235,7 +235,7 @@
<Select
onValueChange={field.onChange}
defaultValue={field.value}
- disabled={data.readonly}
+ disabled={data.readonly || disabled}
>
<FormControl>
<SelectTrigger>
@@ -261,7 +261,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="subdomain" {...field} disabled={data.readonly} />
+ <Input placeholder="subdomain" {...field} disabled={data.readonly || disabled} />
</FormControl>
<FormMessage />
</FormItem>
@@ -287,7 +287,7 @@
<Select
onValueChange={field.onChange}
defaultValue={field.value}
- disabled={data.readonly}
+ disabled={data.readonly || disabled}
>
<FormControl>
<SelectTrigger>
@@ -312,7 +312,7 @@
<Select
onValueChange={field.onChange}
defaultValue={field.value}
- disabled={data.readonly}
+ disabled={data.readonly || disabled}
>
<FormControl>
<SelectTrigger>
@@ -332,7 +332,9 @@
</FormItem>
)}
/>
- <Button type="submit">Expose</Button>
+ <Button type="submit" disabled={disabled}>
+ Expose
+ </Button>
</form>
</Form>
</>
diff --git a/apps/canvas/front/src/components/node-github.tsx b/apps/canvas/front/src/components/node-github.tsx
index ae9d2ab..4c065be 100644
--- a/apps/canvas/front/src/components/node-github.tsx
+++ b/apps/canvas/front/src/components/node-github.tsx
@@ -36,8 +36,7 @@
repositoryId: z.number().optional(),
});
-export function NodeGithubDetails(node: GithubNode) {
- const { id, data } = node;
+export function NodeGithubDetails({ id, data, disabled }: GithubNode & { disabled?: boolean }) {
const store = useStateStore();
const projectId = useProjectId();
const [repos, setRepos] = useState<GitHubRepository[]>([]);
@@ -138,7 +137,7 @@
<Select
onValueChange={(value) => field.onChange(Number(value))}
value={field.value?.toString()}
- disabled={loading || !projectId || !githubService}
+ disabled={loading || !projectId || !githubService || disabled}
>
<FormControl>
<SelectTrigger>
diff --git a/apps/canvas/front/src/components/node-mongodb.tsx b/apps/canvas/front/src/components/node-mongodb.tsx
index bb787cd..69d0d0f 100644
--- a/apps/canvas/front/src/components/node-mongodb.tsx
+++ b/apps/canvas/front/src/components/node-mongodb.tsx
@@ -31,7 +31,7 @@
name: z.string().min(1, "required"),
});
-export function NodeMongoDBDetails({ id, data }: MongoDBNode) {
+export function NodeMongoDBDetails({ id, data, disabled }: MongoDBNode & { disabled?: boolean }) {
const store = useStateStore();
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
@@ -66,7 +66,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="name" {...field} />
+ <Input placeholder="name" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
diff --git a/apps/canvas/front/src/components/node-postgresql.tsx b/apps/canvas/front/src/components/node-postgresql.tsx
index 292fd1e..d31cfe2 100644
--- a/apps/canvas/front/src/components/node-postgresql.tsx
+++ b/apps/canvas/front/src/components/node-postgresql.tsx
@@ -31,7 +31,7 @@
name: z.string().min(1, "required"),
});
-export function NodePostgreSQLDetails({ id, data }: PostgreSQLNode) {
+export function NodePostgreSQLDetails({ id, data, disabled }: PostgreSQLNode & { disabled?: boolean }) {
const store = useStateStore();
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
@@ -66,7 +66,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="name" {...field} />
+ <Input placeholder="name" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
diff --git a/apps/canvas/front/src/components/node-volume.tsx b/apps/canvas/front/src/components/node-volume.tsx
index 80d8c99..3118681 100644
--- a/apps/canvas/front/src/components/node-volume.tsx
+++ b/apps/canvas/front/src/components/node-volume.tsx
@@ -39,7 +39,7 @@
size: z.string().min(1).default("1Gi"),
});
-export function NodeVolumeDetails({ id, data }: VolumeNode) {
+export function NodeVolumeDetails({ id, data, disabled }: VolumeNode & { disabled?: boolean }) {
const store = useStateStore();
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
@@ -86,7 +86,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="name" {...field} />
+ <Input placeholder="name" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
@@ -97,7 +97,7 @@
name="type"
render={({ field }) => (
<FormItem>
- <Select onValueChange={field.onChange} defaultValue={field.value}>
+ <Select onValueChange={field.onChange} defaultValue={field.value} disabled={disabled}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Volume Type" />
@@ -121,7 +121,7 @@
render={({ field }) => (
<FormItem>
<FormControl>
- <Input placeholder="size" {...field} />
+ <Input placeholder="size" {...field} disabled={disabled} />
</FormControl>
<FormMessage />
</FormItem>
diff --git a/apps/canvas/front/src/lib/state.ts b/apps/canvas/front/src/lib/state.ts
index a3caf51..9d74d5b 100644
--- a/apps/canvas/front/src/lib/state.ts
+++ b/apps/canvas/front/src/lib/state.ts
@@ -474,6 +474,10 @@
return useStateStore(githubServiceSelector);
}
+export function useMode(): "edit" | "deploy" {
+ return useStateStore((state) => state.mode);
+}
+
const v: Validator = CreateValidators();
function getRandomPosition({ width, height, transformX, transformY, transformZoom }: Viewport): XYPosition {