Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState } from "react";
import { useRouter } from "next/navigation";
import { z } from "zod";

import * as SCHEMA from "@ctrlplane/db/schema";
import { Button } from "@ctrlplane/ui/button";
import {
Dialog,
Expand Down Expand Up @@ -115,9 +116,13 @@ export const EditVariableDialog: React.FC<EditVariableDialogProps> = ({
<SelectContent>
{variable.values.map((v) => (
<SelectItem key={v.id} value={v.id}>
{typeof v.value === "object"
? JSON.stringify(v.value)
: v.value}
{SCHEMA.isDeploymentVariableValueDirect(v) &&
(typeof v.value === "object"
? JSON.stringify(v.value)
: v.value)}

{SCHEMA.isDeploymentVariableValueReference(v) &&
v.reference}
</SelectItem>
))}
</SelectContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client";

import type * as schema from "@ctrlplane/db/schema";
import React, { useState } from "react";
import Link from "next/link";
import { useParams } from "next/navigation";
Expand All @@ -11,6 +10,7 @@ import {
IconLink,
} from "@tabler/icons-react";

import * as schema from "@ctrlplane/db/schema";
import { cn } from "@ctrlplane/ui";
import { Badge } from "@ctrlplane/ui/badge";
import { Button } from "@ctrlplane/ui/button";
Expand Down Expand Up @@ -135,28 +135,28 @@ const VariableValueRow: React.FC<{
size="icon"
className="h-6 w-6 p-0"
>
{value.valueType === "direct" ? (
{schema.isDeploymentVariableValueDirect(value) ? (
<IconCode className="h-4 w-4 text-blue-400/70" />
) : (
<IconLink className="h-4 w-4 text-amber-400/70" />
)}
</Button>
</TooltipTrigger>
<TooltipContent side="top">
{value.valueType === "direct"
{schema.isDeploymentVariableValueDirect(value)
? "Static value set directly"
: "Computed value based on resource reference"}
</TooltipContent>
</Tooltip>
</TooltipProvider>

{value.valueType === "direct" ? (
{schema.isDeploymentVariableValueDirect(value) ? (
<span className="rounded-md border border-blue-800/40 bg-blue-950/20 px-2 py-0.5 font-mono text-blue-300/90">
{String(value.value)}
</span>
) : (
<span className="flex items-center rounded-md border border-amber-800/40 bg-amber-950/20 px-2 py-0.5">
{[value.reference, ...(value.path ?? [])].map((p, idx) => (
{[value.reference, ...value.path].map((p, idx) => (
<React.Fragment key={p}>
{idx > 0 && (
<span className="mx-0.5 text-neutral-400">.</span>
Expand Down Expand Up @@ -208,28 +208,28 @@ const VariableValueRow: React.FC<{
size="icon"
className="h-6 w-6 p-0"
>
{value.valueType === "direct" ? (
{schema.isDeploymentVariableValueDirect(value) ? (
<IconCode className="h-4 w-4 text-blue-400/70" />
) : (
<IconLink className="h-4 w-4 text-amber-400/70" />
)}
</Button>
</TooltipTrigger>
<TooltipContent side="top">
{value.valueType === "direct"
{schema.isDeploymentVariableValueDirect(value)
? "Static value set directly"
: "Computed value based on resource reference"}
</TooltipContent>
</Tooltip>
</TooltipProvider>

{value.valueType === "direct" ? (
{schema.isDeploymentVariableValueDirect(value) ? (
<span className="rounded-md border border-blue-800/40 bg-blue-950/20 px-2 py-0.5 font-mono text-blue-300/90">
{String(value.value)}
</span>
) : (
<span className="flex items-center rounded-md border border-amber-800/40 bg-amber-950/20 px-2 py-0.5">
{[value.reference, ...(value.path ?? [])].map((p, idx) => (
{[value.reference, ...value.path].map((p, idx) => (
<React.Fragment key={p}>
{idx > 0 && (
<span className="mx-0.5 text-neutral-400">.</span>
Expand Down Expand Up @@ -284,7 +284,11 @@ const VariableValueRow: React.FC<{
key={resource.id}
resource={resource}
workspaceUrls={workspaceUrls}
valueType={value.valueType}
valueType={
schema.isDeploymentVariableValueDirect(value)
? "direct"
: "reference"
}
/>
))}
{value.resources.length !== 0 && (
Expand Down Expand Up @@ -406,7 +410,12 @@ export const VariableTable: React.FC<{
keys: [
"key",
"description",
(i) => i.values.map((v) => JSON.stringify(v.value)),
(i) =>
i.values.map((v) =>
schema.isDeploymentVariableValueDirect(v)
? JSON.stringify(v.value)
: JSON.stringify(v.reference),
),
],
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type * as schema from "@ctrlplane/db/schema";
import React, { useState } from "react";
import { useRouter } from "next/navigation";
import { IconPencil, IconTrash } from "@tabler/icons-react";

import * as schema from "@ctrlplane/db/schema";
import {
AlertDialog,
AlertDialogAction,
Expand All @@ -13,175 +13,25 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from "@ctrlplane/ui/alert-dialog";
import { Button, buttonVariants } from "@ctrlplane/ui/button";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@ctrlplane/ui/dialog";
import { buttonVariants } from "@ctrlplane/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@ctrlplane/ui/dropdown-menu";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
useForm,
} from "@ctrlplane/ui/form";
import { Input } from "@ctrlplane/ui/input";
import { Switch } from "@ctrlplane/ui/switch";
import { defaultCondition } from "@ctrlplane/validators/resources";

import type { VariableValue } from "./variable-data";
import { ResourceConditionRender } from "~/app/[workspaceSlug]/(app)/_components/resources/condition/ResourceConditionRender";
import { api } from "~/trpc/react";
import {
VariableBooleanInput,
VariableChoiceSelect,
VariableStringInput,
} from "./VariableInputs";

const EditVariableValueDialog: React.FC<{
value: VariableValue;
variable: schema.DeploymentVariable;
onClose: () => void;
children: React.ReactNode;
}> = ({ value, variable, children, onClose }) => {
const [open, setOpen] = useState(false);
const update = api.deployment.variable.value.update.useMutation();
const router = useRouter();

const form = useForm({
schema: schema.updateDeploymentVariableValue,
defaultValues: {
...value,
default: variable.defaultValueId === value.id,
},
});

const onSubmit = form.handleSubmit((data) =>
update
.mutateAsync({ id: value.id, data })
.then(() => router.refresh())
.then(onClose),
);

return (
<Dialog
open={open}
onOpenChange={(open) => {
setOpen(open);
if (!open) onClose();
}}
>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="min-w-[1000px]">
<DialogHeader>
<DialogTitle>Edit variable value</DialogTitle>
</DialogHeader>

<Form {...form}>
<form onSubmit={onSubmit} className="space-y-3">
<FormField
control={form.control}
name="value"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Value</FormLabel>
<FormControl>
<>
{variable.config?.type === "string" && (
<VariableStringInput
{...variable.config}
value={String(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "choice" && (
<VariableChoiceSelect
{...variable.config}
value={String(value)}
onSelect={onChange}
/>
)}
{variable.config?.type === "boolean" && (
<VariableBooleanInput
value={value === "" ? null : Boolean(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "number" && (
<Input
type="number"
value={Number(value)}
onChange={(e) => onChange(e.target.valueAsNumber)}
/>
)}
</>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="resourceSelector"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Resource selector</FormLabel>
<FormControl>
<ResourceConditionRender
condition={value ?? defaultCondition}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="default"
render={({ field: { value, onChange } }) => (
<FormItem>
<div className="flex items-center gap-2">
<FormLabel>Default</FormLabel>
<FormControl>
<Switch checked={value} onCheckedChange={onChange} />
</FormControl>
</div>
</FormItem>
)}
/>

<DialogFooter>
<Button
variant="outline"
type="button"
onClick={() => form.setValue("resourceSelector", null)}
>
Clear selector
</Button>
<div className="flex-grow" />
<Button type="submit">Save</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}> = () => {
return null;
};
Comment on lines 28 to 35
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Non-functional component still being used in the UI

The EditVariableValueDialog component has been replaced with a stub that returns null, but it's still being used in the dropdown menu (lines 93-105). This creates a confusing user experience where the "Edit" button is visible but does nothing when clicked.

Either remove the usage of this component from the dropdown menu or add a TODO comment explaining when the implementation will be restored:

-}> = () => {
-  return null;
+}> = () => {
+  // TODO: Implement after variable value refactoring is complete
+  return null;
 };

Or remove the edit option entirely:

       <DropdownMenuContent>
         <DropdownMenuGroup>
-          <EditVariableValueDialog
-            value={value}
-            variable={variable}
-            onClose={() => setOpen(false)}
-          >
-            <DropdownMenuItem
-              onSelect={(e) => e.preventDefault()}
-              className="flex items-center gap-2"
-            >
-              <IconPencil className="h-4 w-4" />
-              Edit
-            </DropdownMenuItem>
-          </EditVariableValueDialog>
           <DeleteVariableValueDialog

🏁 Script executed:

#!/bin/bash
# Locate definition and usages of EditVariableValueDialog
rg "EditVariableValueDialog" -n apps/webservice/src/app

Length of output: 668


Address non-functional EditVariableValueDialog usage

The EditVariableValueDialog is a stub returning null but still rendered in the dropdown (lines 93–105), so the “Edit” option does nothing and will confuse users.

Please either implement the dialog (or add a clear TODO) or remove its usage entirely:

• File: apps/webservice/src/app/[workspaceSlug]/…/VariableValueDropdown.tsx
– Definition at lines 28–35
– Usage at lines 93–105

Option 1: Add a TODO in the stub

 const EditVariableValueDialog: React.FC<{
   value: VariableValue;
   variable: schema.DeploymentVariable;
   onClose: () => void;
   children: React.ReactNode;
 }> = () => {
+  // TODO: implement EditVariableValueDialog after refactoring variable values
   return null;
 };

Option 2: Remove the edit menu item

        <DropdownMenuContent>
          <DropdownMenuGroup>
-          <EditVariableValueDialog
-            value={value}
-            variable={variable}
-            onClose={() => setOpen(false)}
-          >
-            <DropdownMenuItem
-              onSelect={(e) => e.preventDefault()}
-              className="flex items-center gap-2"
-            >
-              <IconPencil className="h-4 w-4" />
-              Edit
-            </DropdownMenuItem>
-          </EditVariableValueDialog>
           <DeleteVariableValueDialog
             value={value}
             variable={variable}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const EditVariableValueDialog: React.FC<{
value: VariableValue;
variable: schema.DeploymentVariable;
onClose: () => void;
children: React.ReactNode;
}> = ({ value, variable, children, onClose }) => {
const [open, setOpen] = useState(false);
const update = api.deployment.variable.value.update.useMutation();
const router = useRouter();
const form = useForm({
schema: schema.updateDeploymentVariableValue,
defaultValues: {
...value,
default: variable.defaultValueId === value.id,
},
});
const onSubmit = form.handleSubmit((data) =>
update
.mutateAsync({ id: value.id, data })
.then(() => router.refresh())
.then(onClose),
);
return (
<Dialog
open={open}
onOpenChange={(open) => {
setOpen(open);
if (!open) onClose();
}}
>
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="min-w-[1000px]">
<DialogHeader>
<DialogTitle>Edit variable value</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={onSubmit} className="space-y-3">
<FormField
control={form.control}
name="value"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Value</FormLabel>
<FormControl>
<>
{variable.config?.type === "string" && (
<VariableStringInput
{...variable.config}
value={String(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "choice" && (
<VariableChoiceSelect
{...variable.config}
value={String(value)}
onSelect={onChange}
/>
)}
{variable.config?.type === "boolean" && (
<VariableBooleanInput
value={value === "" ? null : Boolean(value)}
onChange={onChange}
/>
)}
{variable.config?.type === "number" && (
<Input
type="number"
value={Number(value)}
onChange={(e) => onChange(e.target.valueAsNumber)}
/>
)}
</>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="resourceSelector"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Resource selector</FormLabel>
<FormControl>
<ResourceConditionRender
condition={value ?? defaultCondition}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="default"
render={({ field: { value, onChange } }) => (
<FormItem>
<div className="flex items-center gap-2">
<FormLabel>Default</FormLabel>
<FormControl>
<Switch checked={value} onCheckedChange={onChange} />
</FormControl>
</div>
</FormItem>
)}
/>
<DialogFooter>
<Button
variant="outline"
type="button"
onClick={() => form.setValue("resourceSelector", null)}
>
Clear selector
</Button>
<div className="flex-grow" />
<Button type="submit">Save</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}> = () => {
return null;
};
const EditVariableValueDialog: React.FC<{
value: VariableValue;
variable: schema.DeploymentVariable;
onClose: () => void;
children: React.ReactNode;
}> = () => {
// TODO: implement EditVariableValueDialog after refactoring variable values
return null;
};
🤖 Prompt for AI Agents
In
apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/deployments/[deploymentSlug]/(sidebar)/variables/VariableValueDropdown.tsx
around lines 28 to 35 and usage at lines 93 to 105, the EditVariableValueDialog
component is a stub returning null but still used in the dropdown menu, causing
the Edit button to do nothing. To fix this, either add a clear TODO comment
inside the EditVariableValueDialog stub indicating when the implementation will
be restored or remove the edit menu item and all references to
EditVariableValueDialog from the dropdown menu to avoid confusing users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will address in separate PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adityachoudhari26 Understood! That makes sense to keep this PR focused on the variable refactoring.

Would you like me to create a GitHub issue to track the EditVariableValueDialog fix as a follow-up task? I can include the details about the non-functional component and reference this PR and comment.


const DeleteVariableValueDialog: React.FC<{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ export default async function VariablesPage(props: {
const deployment = await api.deployment.bySlug(params);
if (deployment == null) notFound();

const variables = await api.deployment.variable.byDeploymentId(deployment.id);
const variablesResult = await api.deployment.variable.byDeploymentId(
deployment.id,
);
const variables = variablesResult.map((variable) => ({
...variable,
values: [...variable.directValues, ...variable.referenceValues],
}));

return (
<div className="h-full overflow-y-auto pb-[100px]">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ export default async function VariablesPage(props: {
deploymentVariables.map((v) => (
<TableRow key={v.key}>
<TableCell>{v.key}</TableCell>
<TableCell>{String(v.value.value)}</TableCell>
<TableCell></TableCell>
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve user experience with empty value cell.

The deployment variable value cell is now completely empty, which could confuse users. Consider:

  • Adding a placeholder message explaining the refactoring
  • Implementing proper value display using the new direct/reference structure
  • Adding a "Not available" or similar indicator

This appears to be an incomplete implementation that needs user-facing improvements.

- <TableCell></TableCell>
+ <TableCell className="text-muted-foreground">
+   <span className="italic">Value display pending refactor</span>
+ </TableCell>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TableCell></TableCell>
<TableCell className="text-muted-foreground">
<span className="italic">Value display pending refactor</span>
</TableCell>
🤖 Prompt for AI Agents
In
apps/webservice/src/app/[workspaceSlug]/(app)/resources/(raw)/[resourceId]/variables/page.tsx
at line 252, the TableCell for deployment variable value is empty, which can
confuse users. Update this cell to display a meaningful placeholder such as "Not
available" or a message explaining the refactoring. Additionally, implement
logic to properly render the variable value using the new direct/reference
structure to improve clarity and user experience.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will address in separate PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adityachoudhari26 That sounds like a good approach to keep this PR focused on the refactoring!

Would you like me to open a GitHub issue to track implementing proper deployment variable value display using the new direct/reference structure? This would help ensure the UI improvement doesn't get lost.

<TableCell>
<Link
href={urls
Expand Down
Loading
Loading