diff --git a/src/features/workspace/IntakeFieldControl.test.tsx b/src/features/workspace/IntakeFieldControl.test.tsx new file mode 100644 index 0000000..259fc25 --- /dev/null +++ b/src/features/workspace/IntakeFieldControl.test.tsx @@ -0,0 +1,54 @@ +// @vitest-environment jsdom +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { IntakeFieldControl } from "./IntakeFieldControl"; + +describe("IntakeFieldControl", () => { + afterEach(() => cleanup()); + + it("renders a single-line text input when no rows prop is provided", () => { + render( + , + ); + + const input = screen.getByLabelText("Issue summary") as HTMLInputElement; + expect(input.tagName).toBe("INPUT"); + expect(input.type).toBe("text"); + expect(input.value).toBe("VPN outage in west region"); + }); + + it("renders a multi-line textarea when rows > 1 and reports new values through onChange", () => { + const onChange = vi.fn(); + render( + , + ); + + const textarea = screen.getByLabelText("Symptoms") as HTMLTextAreaElement; + expect(textarea.tagName).toBe("TEXTAREA"); + expect(textarea.rows).toBe(3); + + fireEvent.change(textarea, { target: { value: "updated symptoms" } }); + expect(onChange).toHaveBeenCalledWith("updated symptoms"); + }); + + it("fires onChange with the latest value on single-line edits", () => { + const onChange = vi.fn(); + render( + , + ); + + fireEvent.change(screen.getByLabelText("Affected user"), { + target: { value: "alice@example.com" }, + }); + expect(onChange).toHaveBeenCalledWith("alice@example.com"); + }); +}); diff --git a/src/features/workspace/IntakeFieldControl.tsx b/src/features/workspace/IntakeFieldControl.tsx new file mode 100644 index 0000000..bbf1dc4 --- /dev/null +++ b/src/features/workspace/IntakeFieldControl.tsx @@ -0,0 +1,74 @@ +import type { NoteAudience } from "../../types/workspace"; + +export type IntakeField = + | "issue" + | "environment" + | "impact" + | "affected_user" + | "affected_system" + | "affected_site" + | "symptoms" + | "steps_tried" + | "blockers" + | "likely_category"; + +export const NOTE_AUDIENCES: Array<{ id: NoteAudience; label: string }> = [ + { id: "internal-note", label: "Internal note" }, + { id: "customer-safe", label: "Customer-safe" }, + { id: "escalation-note", label: "Escalation note" }, +]; + +export const INTAKE_FIELDS: Array<{ + key: IntakeField; + label: string; + rows?: number; +}> = [ + { key: "issue", label: "Issue summary" }, + { key: "environment", label: "Environment" }, + { key: "impact", label: "Impact", rows: 2 }, + { key: "affected_user", label: "Affected user" }, + { key: "affected_system", label: "Affected system" }, + { key: "affected_site", label: "Affected site" }, + { key: "symptoms", label: "Symptoms", rows: 3 }, + { key: "steps_tried", label: "Steps already tried", rows: 3 }, + { key: "blockers", label: "Current blocker", rows: 2 }, + { key: "likely_category", label: "Likely category" }, +]; + +interface IntakeFieldControlProps { + label: string; + value: string; + rows?: number; + onChange: (value: string) => void; +} + +export function IntakeFieldControl({ + label, + value, + rows, + onChange, +}: IntakeFieldControlProps) { + if (rows && rows > 1) { + return ( +