feat: add description field to boards and update related components for onboarding

This commit is contained in:
Abhimanyu Saharan
2026-02-11 18:19:29 +05:30
parent 25eb45bf54
commit c6417bcffb
15 changed files with 148 additions and 6 deletions

View File

@@ -12,6 +12,7 @@ import type { BoardCreateSuccessMetrics } from "./boardCreateSuccessMetrics";
export interface BoardCreate {
name: string;
slug: string;
description: string;
gateway_id?: string | null;
board_group_id?: string | null;
board_type?: string;

View File

@@ -7,7 +7,6 @@
import type { BoardOnboardingAgentCompleteSuccessMetrics } from "./boardOnboardingAgentCompleteSuccessMetrics";
import type { BoardOnboardingLeadAgentDraft } from "./boardOnboardingLeadAgentDraft";
import type { BoardOnboardingUserProfile } from "./boardOnboardingUserProfile";
import { BoardOnboardingAgentCompleteStatus } from "./boardOnboardingAgentCompleteStatus";
/**
* Complete onboarding draft produced by the onboarding assistant.
@@ -17,7 +16,7 @@ export interface BoardOnboardingAgentComplete {
objective?: string | null;
success_metrics?: BoardOnboardingAgentCompleteSuccessMetrics;
target_date?: string | null;
status: BoardOnboardingAgentCompleteStatus;
status: "complete";
user_profile?: BoardOnboardingUserProfile | null;
lead_agent?: BoardOnboardingLeadAgentDraft | null;
}

View File

@@ -12,6 +12,7 @@ import type { BoardReadSuccessMetrics } from "./boardReadSuccessMetrics";
export interface BoardRead {
name: string;
slug: string;
description: string;
gateway_id?: string | null;
board_group_id?: string | null;
board_type?: string;

View File

@@ -12,6 +12,7 @@ import type { BoardUpdateSuccessMetrics } from "./boardUpdateSuccessMetrics";
export interface BoardUpdate {
name?: string | null;
slug?: string | null;
description?: string | null;
gateway_id?: string | null;
board_group_id?: string | null;
board_type?: string | null;

View File

@@ -206,3 +206,4 @@ export * from "./updateAgentApiV1AgentsAgentIdPatchParams";
export * from "./userRead";
export * from "./userUpdate";
export * from "./validationError";
export * from "./validationErrorCtx";

View File

@@ -4,9 +4,12 @@
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
import type { ValidationErrorCtx } from "./validationErrorCtx";
export interface ValidationError {
loc: (string | number)[];
msg: string;
type: string;
input?: unknown;
ctx?: ValidationErrorCtx;
}

View File

@@ -0,0 +1,8 @@
/**
* Generated by orval v8.2.0 🍺
* Do not edit manually.
* Mission Control API
* OpenAPI spec version: 0.1.0
*/
export type ValidationErrorCtx = { [key: string]: unknown };

View File

@@ -65,6 +65,7 @@ export default function EditBoardPage() {
const [board, setBoard] = useState<BoardRead | null>(null);
const [name, setName] = useState<string | undefined>(undefined);
const [description, setDescription] = useState<string | undefined>(undefined);
const [gatewayId, setGatewayId] = useState<string | undefined>(undefined);
const [boardGroupId, setBoardGroupId] = useState<string | undefined>(
undefined,
@@ -182,6 +183,7 @@ export default function EditBoardPage() {
const baseBoard = board ?? loadedBoard;
const resolvedName = name ?? baseBoard?.name ?? "";
const resolvedDescription = description ?? baseBoard?.description ?? "";
const resolvedGatewayId = gatewayId ?? baseBoard?.gateway_id ?? "";
const resolvedBoardGroupId =
boardGroupId ?? baseBoard?.board_group_id ?? "none";
@@ -209,7 +211,9 @@ export default function EditBoardPage() {
boardQuery.error?.message ??
null;
const isFormReady = Boolean(resolvedName.trim() && displayGatewayId);
const isFormReady = Boolean(
resolvedName.trim() && resolvedDescription.trim() && displayGatewayId,
);
const gatewayOptions = useMemo(
() =>
@@ -231,6 +235,7 @@ export default function EditBoardPage() {
const handleOnboardingConfirmed = (updated: BoardRead) => {
setBoard(updated);
setDescription(updated.description ?? "");
setBoardType(updated.board_type ?? "goal");
setObjective(updated.objective ?? "");
setSuccessMetrics(
@@ -256,6 +261,11 @@ export default function EditBoardPage() {
setError("Select a gateway before saving.");
return;
}
const trimmedDescription = resolvedDescription.trim();
if (!trimmedDescription) {
setError("Board description is required.");
return;
}
setError(null);
setMetricsError(null);
@@ -276,6 +286,7 @@ export default function EditBoardPage() {
const payload: BoardUpdate = {
name: trimmedName,
slug: slugify(trimmedName),
description: trimmedDescription,
gateway_id: resolvedGatewayId || null,
board_group_id:
resolvedBoardGroupId === "none" ? null : resolvedBoardGroupId,
@@ -410,6 +421,19 @@ export default function EditBoardPage() {
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Description <span className="text-red-500">*</span>
</label>
<Textarea
value={resolvedDescription}
onChange={(event) => setDescription(event.target.value)}
placeholder="What context should the lead agent know?"
className="min-h-[120px]"
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Objective

View File

@@ -24,6 +24,7 @@ import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout"
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import SearchableSelect from "@/components/ui/searchable-select";
import { Textarea } from "@/components/ui/textarea";
const slugify = (value: string) =>
value
@@ -39,6 +40,7 @@ export default function NewBoardPage() {
const { isAdmin } = useOrganizationMembership(isSignedIn);
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [gatewayId, setGatewayId] = useState<string>("");
const [boardGroupId, setBoardGroupId] = useState<string>("none");
@@ -95,7 +97,9 @@ export default function NewBoardPage() {
const errorMessage =
error ?? gatewaysQuery.error?.message ?? groupsQuery.error?.message ?? null;
const isFormReady = Boolean(name.trim() && displayGatewayId);
const isFormReady = Boolean(
name.trim() && description.trim() && displayGatewayId,
);
const gatewayOptions = useMemo(
() =>
@@ -124,6 +128,11 @@ export default function NewBoardPage() {
setError("Select a gateway before creating a board.");
return;
}
const trimmedDescription = description.trim();
if (!trimmedDescription) {
setError("Board description is required.");
return;
}
setError(null);
@@ -131,6 +140,7 @@ export default function NewBoardPage() {
data: {
name: trimmedName,
slug: slugify(trimmedName),
description: trimmedDescription,
gateway_id: resolvedGatewayId,
board_group_id: boardGroupId === "none" ? null : boardGroupId,
},
@@ -208,6 +218,19 @@ export default function NewBoardPage() {
</p>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-slate-900">
Description <span className="text-red-500">*</span>
</label>
<Textarea
value={description}
onChange={(event) => setDescription(event.target.value)}
placeholder="What context should the lead agent know before onboarding?"
className="min-h-[120px]"
disabled={isLoading}
/>
</div>
</div>
{gateways.length === 0 ? (

View File

@@ -37,6 +37,7 @@ const buildBoard = (overrides: Partial<BoardRead> = {}): BoardRead => ({
id: "board-1",
name: "Ops Board",
slug: "ops-board",
description: "Operations board context.",
organization_id: "org-1",
created_at: "2026-01-01T00:00:00Z",
updated_at: "2026-01-01T00:00:00Z",