"use client"; import { Button } from "@/components/ui/button"; import { HelpTip } from "@/components/ui/help-tip"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { ArrowDown, ArrowUp, Plus, X } from "lucide-react"; import type { EnvironmentRung } from "@/types"; interface EnvironmentLadderEditorProps { // null/empty => degenerate 1-rung ladder synthesized from default_branch. rungs: EnvironmentRung[] | null; onChange: (rungs: EnvironmentRung[] | null) => void; } // An empty editor (no rungs) means "inherit default_branch" via the backend // shim, so we keep a plain array internally and emit null when it empties. function toRungs(value: EnvironmentRung[] | null): EnvironmentRung[] { return value ?? []; } export function EnvironmentLadderEditor({ rungs, onChange, }: EnvironmentLadderEditorProps) { const items = toRungs(rungs); const emit = (next: EnvironmentRung[]) => { onChange(next.length ? next : null); }; const handleAdd = () => { emit([...items, { name: "", branch: "" }]); }; const handleRemove = (index: number) => { emit(items.filter((_, i) => i !== index)); }; const handleUpdate = (index: number, field: keyof EnvironmentRung, value: string) => { const next = items.map((r, i) => (i === index ? { ...r, [field]: value } : r)); emit(next); }; const handleMove = (index: number, direction: -1 | 1) => { const target = index + direction; if (target < 0 || target >= items.length) return; const next = [...items]; [next[index], next[target]] = [next[target], next[index]]; emit(next); }; return (
Top to bottom is the promotion flow: PRs land on the
first branch, each rung promotes to the next, and{" "}
releases are cut from the last — e.g.{" "}
dev → qa → staging → prod. Leave empty to use{" "}
default branch for everything; when set, this overrides it for
both the PR target and the release target.