feat: add description field to boards and update related components for onboarding
This commit is contained in:
@@ -175,6 +175,7 @@ async def start_onboarding(
|
|||||||
prompt = (
|
prompt = (
|
||||||
"BOARD ONBOARDING REQUEST\n\n"
|
"BOARD ONBOARDING REQUEST\n\n"
|
||||||
f"Board Name: {board.name}\n"
|
f"Board Name: {board.name}\n"
|
||||||
|
f"Board Description: {board.description or '(not provided)'}\n"
|
||||||
"You are the gateway agent. Ask the user 6-10 focused questions total:\n"
|
"You are the gateway agent. Ask the user 6-10 focused questions total:\n"
|
||||||
"- 3-6 questions to clarify the board goal.\n"
|
"- 3-6 questions to clarify the board goal.\n"
|
||||||
"- 1 question to choose a unique name for the board lead agent "
|
"- 1 question to choose a unique name for the board lead agent "
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class Board(TenantScoped, table=True):
|
|||||||
organization_id: UUID = Field(foreign_key="organizations.id", index=True)
|
organization_id: UUID = Field(foreign_key="organizations.id", index=True)
|
||||||
name: str
|
name: str
|
||||||
slug: str = Field(index=True)
|
slug: str = Field(index=True)
|
||||||
|
description: str = Field(default="")
|
||||||
gateway_id: UUID | None = Field(default=None, foreign_key="gateways.id", index=True)
|
gateway_id: UUID | None = Field(default=None, foreign_key="gateways.id", index=True)
|
||||||
board_group_id: UUID | None = Field(
|
board_group_id: UUID | None = Field(
|
||||||
default=None,
|
default=None,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from sqlmodel import SQLModel
|
|||||||
|
|
||||||
_ERR_GOAL_FIELDS_REQUIRED = "Confirmed goal boards require objective and success_metrics"
|
_ERR_GOAL_FIELDS_REQUIRED = "Confirmed goal boards require objective and success_metrics"
|
||||||
_ERR_GATEWAY_REQUIRED = "gateway_id is required"
|
_ERR_GATEWAY_REQUIRED = "gateway_id is required"
|
||||||
|
_ERR_DESCRIPTION_REQUIRED = "description is required"
|
||||||
RUNTIME_ANNOTATION_TYPES = (datetime, UUID)
|
RUNTIME_ANNOTATION_TYPES = (datetime, UUID)
|
||||||
|
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ class BoardBase(SQLModel):
|
|||||||
|
|
||||||
name: str
|
name: str
|
||||||
slug: str
|
slug: str
|
||||||
|
description: str
|
||||||
gateway_id: UUID | None = None
|
gateway_id: UUID | None = None
|
||||||
board_group_id: UUID | None = None
|
board_group_id: UUID | None = None
|
||||||
board_type: str = "goal"
|
board_type: str = "goal"
|
||||||
@@ -37,6 +39,10 @@ class BoardCreate(BoardBase):
|
|||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_goal_fields(self) -> Self:
|
def validate_goal_fields(self) -> Self:
|
||||||
"""Require gateway and goal details when creating a confirmed goal board."""
|
"""Require gateway and goal details when creating a confirmed goal board."""
|
||||||
|
description = self.description.strip()
|
||||||
|
if not description:
|
||||||
|
raise ValueError(_ERR_DESCRIPTION_REQUIRED)
|
||||||
|
self.description = description
|
||||||
if self.gateway_id is None:
|
if self.gateway_id is None:
|
||||||
raise ValueError(_ERR_GATEWAY_REQUIRED)
|
raise ValueError(_ERR_GATEWAY_REQUIRED)
|
||||||
if (
|
if (
|
||||||
@@ -53,6 +59,7 @@ class BoardUpdate(SQLModel):
|
|||||||
|
|
||||||
name: str | None = None
|
name: str | None = None
|
||||||
slug: str | None = None
|
slug: str | None = None
|
||||||
|
description: str | None = None
|
||||||
gateway_id: UUID | None = None
|
gateway_id: UUID | None = None
|
||||||
board_group_id: UUID | None = None
|
board_group_id: UUID | None = None
|
||||||
board_type: str | None = None
|
board_type: str | None = None
|
||||||
@@ -68,6 +75,13 @@ class BoardUpdate(SQLModel):
|
|||||||
# Treat explicit null like "unset" is invalid for patch updates.
|
# Treat explicit null like "unset" is invalid for patch updates.
|
||||||
if "gateway_id" in self.model_fields_set and self.gateway_id is None:
|
if "gateway_id" in self.model_fields_set and self.gateway_id is None:
|
||||||
raise ValueError(_ERR_GATEWAY_REQUIRED)
|
raise ValueError(_ERR_GATEWAY_REQUIRED)
|
||||||
|
if "description" in self.model_fields_set:
|
||||||
|
if self.description is None:
|
||||||
|
raise ValueError(_ERR_DESCRIPTION_REQUIRED)
|
||||||
|
description = self.description.strip()
|
||||||
|
if not description:
|
||||||
|
raise ValueError(_ERR_DESCRIPTION_REQUIRED)
|
||||||
|
self.description = description
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
"""Add description field to boards.
|
||||||
|
|
||||||
|
Revision ID: c3b58a391f2e
|
||||||
|
Revises: b308f2876359
|
||||||
|
Create Date: 2026-02-11 00:00:00.000000
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "c3b58a391f2e"
|
||||||
|
down_revision = "b308f2876359"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Add required board description column."""
|
||||||
|
op.add_column(
|
||||||
|
"boards",
|
||||||
|
sa.Column(
|
||||||
|
"description",
|
||||||
|
sa.String(),
|
||||||
|
nullable=False,
|
||||||
|
server_default="",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.alter_column("boards", "description", server_default=None)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Remove board description column."""
|
||||||
|
op.drop_column("boards", "description")
|
||||||
@@ -6,7 +6,7 @@ from uuid import uuid4
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.schemas.board_onboarding import BoardOnboardingConfirm
|
from app.schemas.board_onboarding import BoardOnboardingConfirm
|
||||||
from app.schemas.boards import BoardCreate
|
from app.schemas.boards import BoardCreate, BoardUpdate
|
||||||
|
|
||||||
|
|
||||||
def test_goal_board_requires_objective_and_metrics_when_confirmed() -> None:
|
def test_goal_board_requires_objective_and_metrics_when_confirmed() -> None:
|
||||||
@@ -18,6 +18,7 @@ def test_goal_board_requires_objective_and_metrics_when_confirmed() -> None:
|
|||||||
BoardCreate(
|
BoardCreate(
|
||||||
name="Goal Board",
|
name="Goal Board",
|
||||||
slug="goal",
|
slug="goal",
|
||||||
|
description="Ship onboarding improvements.",
|
||||||
gateway_id=uuid4(),
|
gateway_id=uuid4(),
|
||||||
board_type="goal",
|
board_type="goal",
|
||||||
goal_confirmed=True,
|
goal_confirmed=True,
|
||||||
@@ -26,6 +27,7 @@ def test_goal_board_requires_objective_and_metrics_when_confirmed() -> None:
|
|||||||
BoardCreate(
|
BoardCreate(
|
||||||
name="Goal Board",
|
name="Goal Board",
|
||||||
slug="goal",
|
slug="goal",
|
||||||
|
description="Ship onboarding improvements.",
|
||||||
gateway_id=uuid4(),
|
gateway_id=uuid4(),
|
||||||
board_type="goal",
|
board_type="goal",
|
||||||
goal_confirmed=True,
|
goal_confirmed=True,
|
||||||
@@ -36,7 +38,13 @@ def test_goal_board_requires_objective_and_metrics_when_confirmed() -> None:
|
|||||||
|
|
||||||
def test_goal_board_allows_missing_objective_before_confirmation() -> None:
|
def test_goal_board_allows_missing_objective_before_confirmation() -> None:
|
||||||
"""Draft goal boards may omit objective/success_metrics before confirmation."""
|
"""Draft goal boards may omit objective/success_metrics before confirmation."""
|
||||||
BoardCreate(name="Draft", slug="draft", gateway_id=uuid4(), board_type="goal")
|
BoardCreate(
|
||||||
|
name="Draft",
|
||||||
|
slug="draft",
|
||||||
|
description="Iterate on backlog hygiene.",
|
||||||
|
gateway_id=uuid4(),
|
||||||
|
board_type="goal",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_general_board_allows_missing_objective() -> None:
|
def test_general_board_allows_missing_objective() -> None:
|
||||||
@@ -44,11 +52,30 @@ def test_general_board_allows_missing_objective() -> None:
|
|||||||
BoardCreate(
|
BoardCreate(
|
||||||
name="General",
|
name="General",
|
||||||
slug="general",
|
slug="general",
|
||||||
|
description="General coordination board.",
|
||||||
gateway_id=uuid4(),
|
gateway_id=uuid4(),
|
||||||
board_type="general",
|
board_type="general",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_board_create_requires_description() -> None:
|
||||||
|
"""Board creation should reject empty descriptions."""
|
||||||
|
with pytest.raises(ValueError, match="description is required"):
|
||||||
|
BoardCreate(
|
||||||
|
name="Goal Board",
|
||||||
|
slug="goal",
|
||||||
|
description=" ",
|
||||||
|
gateway_id=uuid4(),
|
||||||
|
board_type="goal",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_board_update_rejects_empty_description_patch() -> None:
|
||||||
|
"""Patch payloads should reject blank descriptions."""
|
||||||
|
with pytest.raises(ValueError, match="description is required"):
|
||||||
|
BoardUpdate(description=" ")
|
||||||
|
|
||||||
|
|
||||||
def test_onboarding_confirm_requires_goal_fields() -> None:
|
def test_onboarding_confirm_requires_goal_fields() -> None:
|
||||||
"""Onboarding confirm should enforce goal fields for goal board types."""
|
"""Onboarding confirm should enforce goal fields for goal board types."""
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type { BoardCreateSuccessMetrics } from "./boardCreateSuccessMetrics";
|
|||||||
export interface BoardCreate {
|
export interface BoardCreate {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
description: string;
|
||||||
gateway_id?: string | null;
|
gateway_id?: string | null;
|
||||||
board_group_id?: string | null;
|
board_group_id?: string | null;
|
||||||
board_type?: string;
|
board_type?: string;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
import type { BoardOnboardingAgentCompleteSuccessMetrics } from "./boardOnboardingAgentCompleteSuccessMetrics";
|
import type { BoardOnboardingAgentCompleteSuccessMetrics } from "./boardOnboardingAgentCompleteSuccessMetrics";
|
||||||
import type { BoardOnboardingLeadAgentDraft } from "./boardOnboardingLeadAgentDraft";
|
import type { BoardOnboardingLeadAgentDraft } from "./boardOnboardingLeadAgentDraft";
|
||||||
import type { BoardOnboardingUserProfile } from "./boardOnboardingUserProfile";
|
import type { BoardOnboardingUserProfile } from "./boardOnboardingUserProfile";
|
||||||
import { BoardOnboardingAgentCompleteStatus } from "./boardOnboardingAgentCompleteStatus";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete onboarding draft produced by the onboarding assistant.
|
* Complete onboarding draft produced by the onboarding assistant.
|
||||||
@@ -17,7 +16,7 @@ export interface BoardOnboardingAgentComplete {
|
|||||||
objective?: string | null;
|
objective?: string | null;
|
||||||
success_metrics?: BoardOnboardingAgentCompleteSuccessMetrics;
|
success_metrics?: BoardOnboardingAgentCompleteSuccessMetrics;
|
||||||
target_date?: string | null;
|
target_date?: string | null;
|
||||||
status: BoardOnboardingAgentCompleteStatus;
|
status: "complete";
|
||||||
user_profile?: BoardOnboardingUserProfile | null;
|
user_profile?: BoardOnboardingUserProfile | null;
|
||||||
lead_agent?: BoardOnboardingLeadAgentDraft | null;
|
lead_agent?: BoardOnboardingLeadAgentDraft | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type { BoardReadSuccessMetrics } from "./boardReadSuccessMetrics";
|
|||||||
export interface BoardRead {
|
export interface BoardRead {
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
description: string;
|
||||||
gateway_id?: string | null;
|
gateway_id?: string | null;
|
||||||
board_group_id?: string | null;
|
board_group_id?: string | null;
|
||||||
board_type?: string;
|
board_type?: string;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import type { BoardUpdateSuccessMetrics } from "./boardUpdateSuccessMetrics";
|
|||||||
export interface BoardUpdate {
|
export interface BoardUpdate {
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
slug?: string | null;
|
slug?: string | null;
|
||||||
|
description?: string | null;
|
||||||
gateway_id?: string | null;
|
gateway_id?: string | null;
|
||||||
board_group_id?: string | null;
|
board_group_id?: string | null;
|
||||||
board_type?: string | null;
|
board_type?: string | null;
|
||||||
|
|||||||
@@ -206,3 +206,4 @@ export * from "./updateAgentApiV1AgentsAgentIdPatchParams";
|
|||||||
export * from "./userRead";
|
export * from "./userRead";
|
||||||
export * from "./userUpdate";
|
export * from "./userUpdate";
|
||||||
export * from "./validationError";
|
export * from "./validationError";
|
||||||
|
export * from "./validationErrorCtx";
|
||||||
|
|||||||
@@ -4,9 +4,12 @@
|
|||||||
* Mission Control API
|
* Mission Control API
|
||||||
* OpenAPI spec version: 0.1.0
|
* OpenAPI spec version: 0.1.0
|
||||||
*/
|
*/
|
||||||
|
import type { ValidationErrorCtx } from "./validationErrorCtx";
|
||||||
|
|
||||||
export interface ValidationError {
|
export interface ValidationError {
|
||||||
loc: (string | number)[];
|
loc: (string | number)[];
|
||||||
msg: string;
|
msg: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
input?: unknown;
|
||||||
|
ctx?: ValidationErrorCtx;
|
||||||
}
|
}
|
||||||
|
|||||||
8
frontend/src/api/generated/model/validationErrorCtx.ts
Normal file
8
frontend/src/api/generated/model/validationErrorCtx.ts
Normal 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 };
|
||||||
@@ -65,6 +65,7 @@ export default function EditBoardPage() {
|
|||||||
|
|
||||||
const [board, setBoard] = useState<BoardRead | null>(null);
|
const [board, setBoard] = useState<BoardRead | null>(null);
|
||||||
const [name, setName] = useState<string | undefined>(undefined);
|
const [name, setName] = useState<string | undefined>(undefined);
|
||||||
|
const [description, setDescription] = useState<string | undefined>(undefined);
|
||||||
const [gatewayId, setGatewayId] = useState<string | undefined>(undefined);
|
const [gatewayId, setGatewayId] = useState<string | undefined>(undefined);
|
||||||
const [boardGroupId, setBoardGroupId] = useState<string | undefined>(
|
const [boardGroupId, setBoardGroupId] = useState<string | undefined>(
|
||||||
undefined,
|
undefined,
|
||||||
@@ -182,6 +183,7 @@ export default function EditBoardPage() {
|
|||||||
const baseBoard = board ?? loadedBoard;
|
const baseBoard = board ?? loadedBoard;
|
||||||
|
|
||||||
const resolvedName = name ?? baseBoard?.name ?? "";
|
const resolvedName = name ?? baseBoard?.name ?? "";
|
||||||
|
const resolvedDescription = description ?? baseBoard?.description ?? "";
|
||||||
const resolvedGatewayId = gatewayId ?? baseBoard?.gateway_id ?? "";
|
const resolvedGatewayId = gatewayId ?? baseBoard?.gateway_id ?? "";
|
||||||
const resolvedBoardGroupId =
|
const resolvedBoardGroupId =
|
||||||
boardGroupId ?? baseBoard?.board_group_id ?? "none";
|
boardGroupId ?? baseBoard?.board_group_id ?? "none";
|
||||||
@@ -209,7 +211,9 @@ export default function EditBoardPage() {
|
|||||||
boardQuery.error?.message ??
|
boardQuery.error?.message ??
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const isFormReady = Boolean(resolvedName.trim() && displayGatewayId);
|
const isFormReady = Boolean(
|
||||||
|
resolvedName.trim() && resolvedDescription.trim() && displayGatewayId,
|
||||||
|
);
|
||||||
|
|
||||||
const gatewayOptions = useMemo(
|
const gatewayOptions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -231,6 +235,7 @@ export default function EditBoardPage() {
|
|||||||
|
|
||||||
const handleOnboardingConfirmed = (updated: BoardRead) => {
|
const handleOnboardingConfirmed = (updated: BoardRead) => {
|
||||||
setBoard(updated);
|
setBoard(updated);
|
||||||
|
setDescription(updated.description ?? "");
|
||||||
setBoardType(updated.board_type ?? "goal");
|
setBoardType(updated.board_type ?? "goal");
|
||||||
setObjective(updated.objective ?? "");
|
setObjective(updated.objective ?? "");
|
||||||
setSuccessMetrics(
|
setSuccessMetrics(
|
||||||
@@ -256,6 +261,11 @@ export default function EditBoardPage() {
|
|||||||
setError("Select a gateway before saving.");
|
setError("Select a gateway before saving.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const trimmedDescription = resolvedDescription.trim();
|
||||||
|
if (!trimmedDescription) {
|
||||||
|
setError("Board description is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setError(null);
|
setError(null);
|
||||||
setMetricsError(null);
|
setMetricsError(null);
|
||||||
@@ -276,6 +286,7 @@ export default function EditBoardPage() {
|
|||||||
const payload: BoardUpdate = {
|
const payload: BoardUpdate = {
|
||||||
name: trimmedName,
|
name: trimmedName,
|
||||||
slug: slugify(trimmedName),
|
slug: slugify(trimmedName),
|
||||||
|
description: trimmedDescription,
|
||||||
gateway_id: resolvedGatewayId || null,
|
gateway_id: resolvedGatewayId || null,
|
||||||
board_group_id:
|
board_group_id:
|
||||||
resolvedBoardGroupId === "none" ? null : resolvedBoardGroupId,
|
resolvedBoardGroupId === "none" ? null : resolvedBoardGroupId,
|
||||||
@@ -410,6 +421,19 @@ export default function EditBoardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</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">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium text-slate-900">
|
<label className="text-sm font-medium text-slate-900">
|
||||||
Objective
|
Objective
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { DashboardPageLayout } from "@/components/templates/DashboardPageLayout"
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import SearchableSelect from "@/components/ui/searchable-select";
|
import SearchableSelect from "@/components/ui/searchable-select";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
|
||||||
const slugify = (value: string) =>
|
const slugify = (value: string) =>
|
||||||
value
|
value
|
||||||
@@ -39,6 +40,7 @@ export default function NewBoardPage() {
|
|||||||
const { isAdmin } = useOrganizationMembership(isSignedIn);
|
const { isAdmin } = useOrganizationMembership(isSignedIn);
|
||||||
|
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
|
const [description, setDescription] = useState("");
|
||||||
const [gatewayId, setGatewayId] = useState<string>("");
|
const [gatewayId, setGatewayId] = useState<string>("");
|
||||||
const [boardGroupId, setBoardGroupId] = useState<string>("none");
|
const [boardGroupId, setBoardGroupId] = useState<string>("none");
|
||||||
|
|
||||||
@@ -95,7 +97,9 @@ export default function NewBoardPage() {
|
|||||||
const errorMessage =
|
const errorMessage =
|
||||||
error ?? gatewaysQuery.error?.message ?? groupsQuery.error?.message ?? null;
|
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(
|
const gatewayOptions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -124,6 +128,11 @@ export default function NewBoardPage() {
|
|||||||
setError("Select a gateway before creating a board.");
|
setError("Select a gateway before creating a board.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const trimmedDescription = description.trim();
|
||||||
|
if (!trimmedDescription) {
|
||||||
|
setError("Board description is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
@@ -131,6 +140,7 @@ export default function NewBoardPage() {
|
|||||||
data: {
|
data: {
|
||||||
name: trimmedName,
|
name: trimmedName,
|
||||||
slug: slugify(trimmedName),
|
slug: slugify(trimmedName),
|
||||||
|
description: trimmedDescription,
|
||||||
gateway_id: resolvedGatewayId,
|
gateway_id: resolvedGatewayId,
|
||||||
board_group_id: boardGroupId === "none" ? null : boardGroupId,
|
board_group_id: boardGroupId === "none" ? null : boardGroupId,
|
||||||
},
|
},
|
||||||
@@ -208,6 +218,19 @@ export default function NewBoardPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
|
|
||||||
{gateways.length === 0 ? (
|
{gateways.length === 0 ? (
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ const buildBoard = (overrides: Partial<BoardRead> = {}): BoardRead => ({
|
|||||||
id: "board-1",
|
id: "board-1",
|
||||||
name: "Ops Board",
|
name: "Ops Board",
|
||||||
slug: "ops-board",
|
slug: "ops-board",
|
||||||
|
description: "Operations board context.",
|
||||||
organization_id: "org-1",
|
organization_id: "org-1",
|
||||||
created_at: "2026-01-01T00:00:00Z",
|
created_at: "2026-01-01T00:00:00Z",
|
||||||
updated_at: "2026-01-01T00:00:00Z",
|
updated_at: "2026-01-01T00:00:00Z",
|
||||||
|
|||||||
Reference in New Issue
Block a user