feat(frontend): simplify feedback report fields
This commit is contained in:
parent
b71dc9b479
commit
429753b7da
|
|
@ -41,12 +41,10 @@ export function ReportProblemDialog({ open, onOpenChange }: ReportProblemDialogP
|
|||
const typeId = useId();
|
||||
const summaryId = useId();
|
||||
const detailsId = useId();
|
||||
const expectedId = useId();
|
||||
const diagnosticsId = useId();
|
||||
const [type, setType] = useState<ReportProblemType>("bug");
|
||||
const [summary, setSummary] = useState("");
|
||||
const [details, setDetails] = useState("");
|
||||
const [expected, setExpected] = useState("");
|
||||
const [includeDiagnostics, setIncludeDiagnostics] = useState(true);
|
||||
const [previewOutput, setPreviewOutput] = useState<ReportProblemOutput>("github");
|
||||
const [copiedOutput, setCopiedOutput] = useState<ReportProblemOutput | null>(null);
|
||||
|
|
@ -73,10 +71,9 @@ export function ReportProblemDialog({ open, onOpenChange }: ReportProblemDialogP
|
|||
type,
|
||||
summary,
|
||||
details,
|
||||
expected,
|
||||
includeDiagnostics,
|
||||
}),
|
||||
[type, summary, details, expected, includeDiagnostics],
|
||||
[type, summary, details, includeDiagnostics],
|
||||
);
|
||||
|
||||
const preview = useMemo(
|
||||
|
|
@ -145,7 +142,7 @@ export function ReportProblemDialog({ open, onOpenChange }: ReportProblemDialogP
|
|||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-[12px] font-medium text-muted-foreground" htmlFor={summaryId}>
|
||||
{fieldCopy.summaryLabel}
|
||||
Summary
|
||||
</label>
|
||||
<Input
|
||||
id={summaryId}
|
||||
|
|
@ -157,30 +154,17 @@ export function ReportProblemDialog({ open, onOpenChange }: ReportProblemDialogP
|
|||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-[12px] font-medium text-muted-foreground" htmlFor={detailsId}>
|
||||
{fieldCopy.detailsLabel}
|
||||
Details
|
||||
</label>
|
||||
<textarea
|
||||
id={detailsId}
|
||||
className="min-h-[112px] w-full resize-y rounded-md border border-border bg-transparent px-3 py-2 text-[13px] leading-relaxed text-foreground outline-none transition placeholder:text-passive focus-visible:border-accent focus-visible:ring-2 focus-visible:ring-accent-weak"
|
||||
className="min-h-[156px] w-full resize-y rounded-md border border-border bg-transparent px-3 py-2 text-[13px] leading-relaxed text-foreground outline-none transition placeholder:text-passive focus-visible:border-accent focus-visible:ring-2 focus-visible:ring-accent-weak"
|
||||
value={details}
|
||||
onChange={(event) => setDetails(event.target.value)}
|
||||
placeholder={fieldCopy.detailsPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-[12px] font-medium text-muted-foreground" htmlFor={expectedId}>
|
||||
{fieldCopy.expectedLabel}
|
||||
</label>
|
||||
<textarea
|
||||
id={expectedId}
|
||||
className="min-h-[84px] w-full resize-y rounded-md border border-border bg-transparent px-3 py-2 text-[13px] leading-relaxed text-foreground outline-none transition placeholder:text-passive focus-visible:border-accent focus-visible:ring-2 focus-visible:ring-accent-weak"
|
||||
value={expected}
|
||||
onChange={(event) => setExpected(event.target.value)}
|
||||
placeholder={fieldCopy.expectedPlaceholder}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label
|
||||
className="flex items-start gap-2 rounded-md border border-border bg-background/40 px-3 py-2 text-[12px] text-muted-foreground"
|
||||
htmlFor={diagnosticsId}
|
||||
|
|
|
|||
|
|
@ -324,12 +324,12 @@ describe("Sidebar", () => {
|
|||
await user.click(feedbackButton);
|
||||
expect(await screen.findByRole("dialog", { name: "Report a problem" })).toBeInTheDocument();
|
||||
|
||||
await user.type(screen.getByLabelText("Bug summary"), "Create project fails in /Users/alice/private-repo");
|
||||
await user.type(screen.getByLabelText("Summary"), "Create project fails in /Users/alice/private-repo");
|
||||
await user.type(
|
||||
screen.getByLabelText("Steps to reproduce"),
|
||||
"Open http://127.0.0.1:5173/projects/demo?access_token=local-secret and click Create.",
|
||||
screen.getByLabelText("Details"),
|
||||
"Open http://127.0.0.1:5173/projects/demo?access_token=local-secret and click Create. Show a clear prerequisite error.",
|
||||
);
|
||||
await user.type(screen.getByLabelText("Expected behavior"), "Show a clear prerequisite error.");
|
||||
expect(screen.queryByLabelText("Expected behavior")).not.toBeInTheDocument();
|
||||
|
||||
expect(screen.getByLabelText("Report preview")).toHaveTextContent("[redacted-local-path]");
|
||||
await user.click(screen.getByRole("button", { name: "Copy and open GitHub" }));
|
||||
|
|
@ -361,7 +361,7 @@ describe("Sidebar", () => {
|
|||
|
||||
await user.click(screen.getAllByRole("button", { name: "Feedback" })[0]);
|
||||
expect(await screen.findByRole("dialog", { name: "Report a problem" })).toBeInTheDocument();
|
||||
await user.type(screen.getByLabelText("Bug summary"), "Need help with setup");
|
||||
await user.type(screen.getByLabelText("Summary"), "Need help with setup");
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Copy and open Discord" }));
|
||||
await user.click(screen.getByRole("button", { name: "Copy email draft" }));
|
||||
|
|
@ -373,36 +373,36 @@ describe("Sidebar", () => {
|
|||
expect(open).toHaveBeenCalledWith("https://discord.com/invite/UZv7JjxbwG", "_blank", "noopener,noreferrer");
|
||||
});
|
||||
|
||||
it("shows report-type specific prompts instead of one generic questionnaire", async () => {
|
||||
it("keeps the report form to summary and details while tailoring placeholder guidance", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderSidebar();
|
||||
|
||||
await user.click(screen.getAllByRole("button", { name: "Feedback" })[0]);
|
||||
expect(await screen.findByRole("dialog", { name: "Report a problem" })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Bug summary")).toHaveAttribute("placeholder", "Short description of the bug");
|
||||
expect(screen.getByLabelText("Steps to reproduce")).toHaveAttribute(
|
||||
expect(screen.getByLabelText("Summary")).toHaveAttribute("placeholder", "Brief title");
|
||||
expect(screen.getByLabelText("Details")).toHaveAttribute(
|
||||
"placeholder",
|
||||
"1. Open...\n2. Click...\n3. See...",
|
||||
);
|
||||
expect(screen.getByLabelText("Expected behavior")).toHaveAttribute(
|
||||
"placeholder",
|
||||
"What should have happened instead?",
|
||||
"What happened, how to reproduce it, and what you expected.",
|
||||
);
|
||||
expect(screen.queryByLabelText("Expected behavior")).not.toBeInTheDocument();
|
||||
|
||||
await chooseOption(screen.getByRole("combobox", { name: "Report type" }), "Feature request");
|
||||
expect(screen.getByLabelText("Feature summary")).toHaveAttribute("placeholder", "Short description of the idea");
|
||||
expect(screen.getByLabelText("Problem / use case")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Requested behavior")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Summary")).toHaveAttribute("placeholder", "Brief title");
|
||||
expect(screen.getByLabelText("Details")).toHaveAttribute(
|
||||
"placeholder",
|
||||
"The problem, use case, and requested behavior.",
|
||||
);
|
||||
|
||||
await chooseOption(screen.getByRole("combobox", { name: "Report type" }), "General feedback");
|
||||
expect(screen.getByLabelText("Feedback summary")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Message")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Optional context")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Summary")).toHaveAttribute("placeholder", "Brief title");
|
||||
expect(screen.getByLabelText("Details")).toHaveAttribute("placeholder", "What should the AO team know?");
|
||||
|
||||
await chooseOption(screen.getByRole("combobox", { name: "Report type" }), "Setup question");
|
||||
expect(screen.getByLabelText("Question summary")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("What are you trying to do?")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("What did you try?")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Summary")).toHaveAttribute("placeholder", "Brief title");
|
||||
expect(screen.getByLabelText("Details")).toHaveAttribute(
|
||||
"placeholder",
|
||||
"What you are trying to do, what you tried, and any error/output.",
|
||||
);
|
||||
});
|
||||
|
||||
it("renames a session inline and persists via the daemon", async () => {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ const diagnostics: ReportProblemDiagnostics = {
|
|||
const completeInput: ReportProblemInput = {
|
||||
type: "bug",
|
||||
summary: "Terminal keeps reconnecting after daemon restart",
|
||||
details: "Open /Users/alice/work/secret-app and visit http://127.0.0.1:5173/?token=secret-token.",
|
||||
expected: "The app should reconnect without losing the current route.",
|
||||
details:
|
||||
"Open /Users/alice/work/secret-app and visit http://127.0.0.1:5173/?token=secret-token. The app should reconnect without losing the current route.",
|
||||
includeDiagnostics: true,
|
||||
};
|
||||
|
||||
|
|
@ -50,8 +50,8 @@ describe("report problem drafts", () => {
|
|||
{
|
||||
type: "question",
|
||||
summary: "Setup fails with OPENAI_API_KEY=sk-proj-secret and password=hunter2",
|
||||
details: "Repo is C:\\Users\\alice\\repo and file:///Users/alice/private/index.html?api_key=abc failed.",
|
||||
expected: "Tell me what prerequisite is missing.",
|
||||
details:
|
||||
"Repo is C:\\Users\\alice\\repo and file:///Users/alice/private/index.html?api_key=abc failed. Tell me what prerequisite is missing.",
|
||||
includeDiagnostics: true,
|
||||
},
|
||||
{
|
||||
|
|
@ -73,7 +73,7 @@ describe("report problem drafts", () => {
|
|||
|
||||
it("produces a useful draft when user input is partial", () => {
|
||||
const draft = formatReportProblemDraft(
|
||||
{ type: "feedback", summary: "", details: "", expected: "", includeDiagnostics: false },
|
||||
{ type: "feedback", summary: "", details: "", includeDiagnostics: false },
|
||||
diagnostics,
|
||||
"email",
|
||||
);
|
||||
|
|
@ -83,34 +83,35 @@ describe("report problem drafts", () => {
|
|||
expect(draft).toContain("No diagnostics included");
|
||||
});
|
||||
|
||||
it("uses report-type specific field labels in generated drafts", () => {
|
||||
it("keeps generated drafts to summary and details for every report type", () => {
|
||||
const featureDraft = formatReportProblemDraft(
|
||||
{
|
||||
type: "feature",
|
||||
summary: "Make feedback reports easier to send",
|
||||
details: "Users want fewer generic bug-report questions for non-bug reports.",
|
||||
expected: "Show feature-specific prompts and output labels.",
|
||||
details:
|
||||
"Users want fewer generic bug-report questions for non-bug reports. Show fewer fields and clearer placeholder guidance.",
|
||||
includeDiagnostics: false,
|
||||
},
|
||||
diagnostics,
|
||||
"github",
|
||||
);
|
||||
expect(featureDraft).toContain("## Problem / use case");
|
||||
expect(featureDraft).toContain("## Requested behavior");
|
||||
expect(featureDraft).toContain("## Summary");
|
||||
expect(featureDraft).toContain("## Details");
|
||||
expect(featureDraft).not.toContain("## Expected");
|
||||
expect(featureDraft).not.toContain("## Requested behavior");
|
||||
|
||||
const questionDraft = formatReportProblemDraft(
|
||||
{
|
||||
type: "question",
|
||||
summary: "Need help setting up Claude Code",
|
||||
details: "Trying to create my first project.",
|
||||
expected: "I installed the CLI and checked PATH.",
|
||||
details: "Trying to create my first project. I installed the CLI and checked PATH.",
|
||||
includeDiagnostics: false,
|
||||
},
|
||||
diagnostics,
|
||||
"email",
|
||||
);
|
||||
expect(questionDraft).toContain("What are you trying to do?:");
|
||||
expect(questionDraft).toContain("What did you try?:");
|
||||
expect(questionDraft).toContain("Details:");
|
||||
expect(questionDraft).not.toContain("What did you try?:");
|
||||
});
|
||||
|
||||
it("builds copy handoff destinations for GitHub and Discord while leaving email copy-only", () => {
|
||||
|
|
|
|||
|
|
@ -8,17 +8,12 @@ export type ReportProblemInput = {
|
|||
type: ReportProblemType;
|
||||
summary: string;
|
||||
details: string;
|
||||
expected: string;
|
||||
includeDiagnostics: boolean;
|
||||
};
|
||||
|
||||
export type ReportProblemFieldCopy = {
|
||||
summaryLabel: string;
|
||||
summaryPlaceholder: string;
|
||||
detailsLabel: string;
|
||||
detailsPlaceholder: string;
|
||||
expectedLabel: string;
|
||||
expectedPlaceholder: string;
|
||||
};
|
||||
|
||||
export type ReportProblemDiagnostics = {
|
||||
|
|
@ -46,36 +41,20 @@ const REPORT_TYPE_LABELS: Record<ReportProblemType, string> = {
|
|||
|
||||
const REPORT_FIELD_COPY: Record<ReportProblemType, ReportProblemFieldCopy> = {
|
||||
bug: {
|
||||
summaryLabel: "Bug summary",
|
||||
summaryPlaceholder: "Short description of the bug",
|
||||
detailsLabel: "Steps to reproduce",
|
||||
detailsPlaceholder: "1. Open...\n2. Click...\n3. See...",
|
||||
expectedLabel: "Expected behavior",
|
||||
expectedPlaceholder: "What should have happened instead?",
|
||||
summaryPlaceholder: "Brief title",
|
||||
detailsPlaceholder: "What happened, how to reproduce it, and what you expected.",
|
||||
},
|
||||
feature: {
|
||||
summaryLabel: "Feature summary",
|
||||
summaryPlaceholder: "Short description of the idea",
|
||||
detailsLabel: "Problem / use case",
|
||||
detailsPlaceholder: "What problem are you trying to solve?",
|
||||
expectedLabel: "Requested behavior",
|
||||
expectedPlaceholder: "What should AO do?",
|
||||
summaryPlaceholder: "Brief title",
|
||||
detailsPlaceholder: "The problem, use case, and requested behavior.",
|
||||
},
|
||||
feedback: {
|
||||
summaryLabel: "Feedback summary",
|
||||
summaryPlaceholder: "Short summary",
|
||||
detailsLabel: "Message",
|
||||
summaryPlaceholder: "Brief title",
|
||||
detailsPlaceholder: "What should the AO team know?",
|
||||
expectedLabel: "Optional context",
|
||||
expectedPlaceholder: "Anything else that would help?",
|
||||
},
|
||||
question: {
|
||||
summaryLabel: "Question summary",
|
||||
summaryPlaceholder: "Short description of the setup question",
|
||||
detailsLabel: "What are you trying to do?",
|
||||
detailsPlaceholder: "Describe the setup or workflow you are attempting.",
|
||||
expectedLabel: "What did you try?",
|
||||
expectedPlaceholder: "Commands, docs, or fixes you already tried.",
|
||||
summaryPlaceholder: "Brief title",
|
||||
detailsPlaceholder: "What you are trying to do, what you tried, and any error/output.",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -134,8 +113,7 @@ export function formatReportProblemDraft(
|
|||
return [
|
||||
`**AO ${fields.typeLabel}**`,
|
||||
`Summary: ${fields.summary}`,
|
||||
`${fields.detailsLabel}: ${fields.details}`,
|
||||
`${fields.expectedLabel}: ${fields.expected}`,
|
||||
`Details: ${fields.details}`,
|
||||
"",
|
||||
"Safe diagnostics:",
|
||||
diagnosticsBlock,
|
||||
|
|
@ -153,12 +131,9 @@ export function formatReportProblemDraft(
|
|||
`Type: ${fields.typeLabel}`,
|
||||
`Summary: ${fields.summary}`,
|
||||
"",
|
||||
`${fields.detailsLabel}:`,
|
||||
"Details:",
|
||||
fields.details,
|
||||
"",
|
||||
`${fields.expectedLabel}:`,
|
||||
fields.expected,
|
||||
"",
|
||||
"Safe diagnostics:",
|
||||
diagnosticsBlock,
|
||||
"",
|
||||
|
|
@ -175,12 +150,9 @@ export function formatReportProblemDraft(
|
|||
"## Summary",
|
||||
fields.summary,
|
||||
"",
|
||||
`## ${fields.detailsLabel}`,
|
||||
"## Details",
|
||||
fields.details,
|
||||
"",
|
||||
`## ${fields.expectedLabel}`,
|
||||
fields.expected,
|
||||
"",
|
||||
"## Safe diagnostics",
|
||||
diagnosticsBlock,
|
||||
"",
|
||||
|
|
@ -206,14 +178,10 @@ export function reportProblemDestinationUrl(
|
|||
}
|
||||
|
||||
function normalizeInput(input: ReportProblemInput) {
|
||||
const fieldCopy = REPORT_FIELD_COPY[input.type];
|
||||
return {
|
||||
typeLabel: REPORT_TYPE_LABELS[input.type],
|
||||
detailsLabel: fieldCopy.detailsLabel,
|
||||
expectedLabel: fieldCopy.expectedLabel,
|
||||
summary: valueOrPlaceholder(input.summary),
|
||||
details: valueOrPlaceholder(input.details),
|
||||
expected: valueOrPlaceholder(input.expected),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue