Skip to content

Commit

Permalink
fix: Settings page runbooks (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
adityachoudhari26 authored Dec 1, 2024
1 parent 06f2d1b commit 21e76f9
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,18 @@ import { useRouter } from "next/navigation";
import { z } from "zod";

import { createRunbookVariable } from "@ctrlplane/db/schema";
import { Button } from "@ctrlplane/ui/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@ctrlplane/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
FormRootError,
useForm,
} from "@ctrlplane/ui/form";
import { Input } from "@ctrlplane/ui/input";
import { Textarea } from "@ctrlplane/ui/textarea";
import { useForm } from "@ctrlplane/ui/form";

import { JobAgentConfig } from "~/components/form/job-agent/JobAgentConfig";
import { JobAgentSelector } from "~/components/form/job-agent/JobAgentSelector";
import type { EditRunbookFormSchema } from "./EditRunbookForm";
import { api } from "~/trpc/react";
import { RunbookVariablesEditor } from "./create/RunbookVariableEditor";
import { EditRunbookForm } from "./EditRunbookForm";

const updateRunbookSchema = z.object({
name: z.string().min(1),
Expand Down Expand Up @@ -60,12 +47,12 @@ export const EditRunbookDialog: React.FC<{
});

const router = useRouter();
const onSubmit = form.handleSubmit(async (data) =>

const onSubmit = (data: EditRunbookFormSchema) =>
update
.mutateAsync({ id: runbook.id, data })
.then(() => router.refresh())
.then(() => setOpen(false)),
);
.then(() => setOpen(false));

const jobAgentId = form.watch("jobAgentId");
const jobAgent = jobAgents.find((j) => j.id === jobAgentId);
Expand All @@ -76,118 +63,13 @@ export const EditRunbookDialog: React.FC<{
<DialogHeader>
<DialogTitle>Edit Runbook</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={onSubmit} className="space-y-8">
<div className="space-y-3">
<div>General</div>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Deploy Hotfix, Rollback Release, Scale Service..."
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea
placeholder="Describe the purpose of this runbook..."
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<div className="space-y-3">
<div>Variables</div>

<div className="text-sm text-muted-foreground">
Variables in runbooks make automation flexible and reusable.
They let you customize runbooks with user inputs and use
environment-specific values without hardcoding. This allows
runbooks to adapt to different scenarios without changing their
core logic.
</div>

<FormField
control={form.control}
name="variables"
render={({ field }) => (
<FormItem>
<FormControl>
<RunbookVariablesEditor
value={field.value}
onChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<div className="space-y-3">
<div>Agent</div>

<FormField
control={form.control}
name="jobAgentId"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Job Agent</FormLabel>
<FormControl>
<JobAgentSelector
jobAgents={jobAgents}
workspace={workspace}
value={value}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="jobAgentConfig"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Config</FormLabel>
<FormControl>
<JobAgentConfig
jobAgent={jobAgent}
workspace={workspace}
value={value}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<Button type="submit">Save</Button>
<FormRootError />
</form>
</Form>
<EditRunbookForm
form={form}
jobAgents={jobAgents}
jobAgent={jobAgent}
workspace={workspace}
onSubmit={onSubmit}
/>
</DialogContent>
</Dialog>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"use client";

import type { UseFormReturn } from "react-hook-form";
import { z } from "zod";

import * as SCHEMA from "@ctrlplane/db/schema";
import { Button } from "@ctrlplane/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
FormRootError,
} from "@ctrlplane/ui/form";
import { Input } from "@ctrlplane/ui/input";
import { Textarea } from "@ctrlplane/ui/textarea";

import { JobAgentConfig } from "~/components/form/job-agent/JobAgentConfig";
import { JobAgentSelector } from "~/components/form/job-agent/JobAgentSelector";
import { RunbookVariablesEditor } from "./create/RunbookVariableEditor";

export const updateRunbookSchema = z.object({
name: z.string().min(1),
description: z.string(),
variables: z.array(SCHEMA.createRunbookVariable),
jobAgentId: z.string().uuid({ message: "Must be a valid job agent ID" }),
jobAgentConfig: z.record(z.any()),
});

export type EditRunbookFormSchema = z.infer<typeof updateRunbookSchema>;

export type EditRunbookFormProps = {
form: UseFormReturn<EditRunbookFormSchema>;
jobAgents: SCHEMA.JobAgent[];
jobAgent?: SCHEMA.JobAgent;
workspace: SCHEMA.Workspace;
onSubmit: (data: EditRunbookFormSchema) => void;
};

export const EditRunbookForm: React.FC<EditRunbookFormProps> = ({
form,
jobAgents,
jobAgent,
workspace,
onSubmit,
}) => (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<div className="space-y-3">
<div>General</div>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Name</FormLabel>
<FormControl>
<Input
placeholder="Deploy Hotfix, Rollback Release, Scale Service..."
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="description"
render={({ field }) => (
<FormItem>
<FormLabel>Description</FormLabel>
<FormControl>
<Textarea
placeholder="Describe the purpose of this runbook..."
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<div className="space-y-3">
<div>Variables</div>

<div className="text-sm text-muted-foreground">
Variables in runbooks make automation flexible and reusable. They let
you customize runbooks with user inputs and use environment-specific
values without hardcoding. This allows runbooks to adapt to different
scenarios without changing their core logic.
</div>

<FormField
control={form.control}
name="variables"
render={({ field }) => (
<FormItem>
<FormControl>
<RunbookVariablesEditor
value={field.value}
onChange={field.onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<div className="space-y-3">
<div>Agent</div>

<FormField
control={form.control}
name="jobAgentId"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Job Agent</FormLabel>
<FormControl>
<JobAgentSelector
jobAgents={jobAgents}
workspace={workspace}
value={value}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="jobAgentConfig"
render={({ field: { value, onChange } }) => (
<FormItem>
<FormLabel>Config</FormLabel>
<FormControl>
<JobAgentConfig
jobAgent={jobAgent}
workspace={workspace}
value={value}
onChange={onChange}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>

<Button type="submit">Save</Button>
<FormRootError />
</form>
</Form>
);
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import type { RouterOutputs } from "@ctrlplane/api";
import type React from "react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
Expand All @@ -18,9 +19,13 @@ import { nFormatter } from "../../_components/nFormatter";

type RunbookNavBarProps = {
totalJobs: number;
runbook: NonNullable<RouterOutputs["runbook"]["byId"]>;
};

export const RunbookNavBar: React.FC<RunbookNavBarProps> = ({ totalJobs }) => {
export const RunbookNavBar: React.FC<RunbookNavBarProps> = ({
totalJobs,
runbook,
}) => {
const { workspaceSlug, systemSlug, runbookId } = useParams<{
workspaceSlug: string;
systemSlug: string;
Expand Down Expand Up @@ -56,16 +61,18 @@ export const RunbookNavBar: React.FC<RunbookNavBarProps> = ({ totalJobs }) => {
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={settingsUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isSettingsActive}
>
Settings
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
{runbook.runhooks.length === 0 && (
<NavigationMenuItem>
<Link href={settingsUrl} legacyBehavior passHref>
<NavigationMenuLink
className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent/50 hover:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
active={isSettingsActive}
>
Settings
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
)}
</NavigationMenuList>
</NavigationMenu>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default async function RunbookLayout({
<SystemBreadcrumbNavbar params={params} />
</div>
</TopNav>
<RunbookNavBar totalJobs={total} />
<RunbookNavBar totalJobs={total} runbook={runbook} />

<div className="scrollbar-thin scrollbar-thumb-neutral-800 scrollbar-track-neutral-900 h-[calc(100vh-53px-49px)] overflow-auto">
{children}
Expand Down
Loading

0 comments on commit 21e76f9

Please sign in to comment.