Skip to content

feat(ui): display copyable goal ID in goal detail page#1910

Open
bluzername wants to merge 2 commits intopaperclipai:masterfrom
bluzername:fix/goal-detail-show-id
Open

feat(ui): display copyable goal ID in goal detail page#1910
bluzername wants to merge 2 commits intopaperclipai:masterfrom
bluzername:fix/goal-detail-show-id

Conversation

@bluzername
Copy link
Copy Markdown

@bluzername bluzername commented Mar 27, 2026

Thinking Path

  • Paperclip orchestrates ai-agents for zero-human companies
  • But humans want to watch the agents and oversee their work
  • Human users work with goals to track what the agents are doing
  • When user look at a goal detail page, they sometimes need to reference the goal ID - for sharing with teammate or for API work
  • But the goal ID was not displayed anywhere on the page, user had to dig into URL or dev tools
  • So we added a small copyable goal ID button in the header, same pattern as issue identifier
  • The clipboard write is properly awaited with error handling so user get correct feedback

Problem

When looking at a goal detail page, the goal ID was not visible anywhere. The header show the level badge (like "TEAM" or "COMPANY") and the status badge, but no ID.

This is inconsistent with the issue detail page where we show the issue identifier (like PAP-123) and user can click it to copy. When working with API or sharing goal reference with teammate, user had to inspect the URL or look in browser dev tools to find the goal ID. Very annoying.

What I changed

Added the first 8 characters of the goal ID as a small monospaced text button next to the level and status badges. Same visual pattern as the issue identifier we added before:

  • Small font-mono text in muted color
  • Get darker on hover to show it's interactive
  • Click to copy full goal ID to clipboard
  • Show "Goal ID copied" success toast
  • On clipboard failure, show "Failed to copy goal ID" error toast (properly awaited Promise with try/catch)

Also added useToast import since it wasn't in this file yet.

How to test

  1. Go to any goal detail page
  2. Next to the level label and status badge, should see a short ID like "a1b2c3d4"
  3. Hover over it - text should darken and cursor change to pointer
  4. Click it - full goal ID copied to clipboard, success toast appear
  5. (Optional) Test clipboard failure: in browser devtools, override navigator.clipboard.writeText to reject - should see error toast instead of success

1 file, 13 lines added.

The goal detail page was not showing the goal ID anywhere.
Unlike issue detail which show the issue identifier (like
PAP-123) and let you click to copy it, goal detail only
show the level badge and status badge.

Added the first 8 characters of the goal ID as a small
monospaced text button next to the status badge. Click it
to copy the full goal ID to clipboard with success toast.

Same visual pattern as the issue identifier copy button -
muted text that get darker on hover, cursor pointer, and
title tooltip saying "Click to copy goal ID".
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 27, 2026

Greptile Summary

This PR adds a small copyable goal ID button (showing the first 8 characters of goal.id) in the GoalDetail header, consistent with the identifier pattern used on the issue detail page. The change is focused and minimal (one file, 13 lines).\n\nKey findings:\n\n- Unhandled clipboard Promise / misleading toast (P1): navigator.clipboard.writeText() is called without await or error handling, so the "Goal ID copied" toast fires unconditionally — even if the clipboard write fails. RoutineDetail.tsx in the same codebase already demonstrates the correct try/catch pattern.\n- Missing aria-label on the button (P2): The button's visible text is an abbreviated ID, and the title attribute is unreliable for screen readers. An aria-label=\"Copy goal ID\" would fix this.\n- PR description is missing required elements per CONTRIBUTING.md: The description does not include a "thinking path" section, nor does it cover why this change matters / the benefits, or any risks. Since this is a visible UI change, before/after screenshots should also be included. Please update the PR description to follow the contribution guidelines.

Confidence Score: 4/5

Safe to merge after fixing the unhandled clipboard Promise that gives users false-positive 'copied' feedback on failure.

One P1 finding: the success toast fires regardless of whether the clipboard write actually succeeds, which is a real UX defect. Once that is fixed (wrap in async/try/catch as shown in the suggestion) and the PR description is updated with the required thinking path and screenshots, this is a clean, well-scoped change.

ui/src/pages/GoalDetail.tsx — the onClick handler for the copy button needs error handling.

Important Files Changed

Filename Overview
ui/src/pages/GoalDetail.tsx Adds a copyable 8-char goal ID badge next to the level/status header; the clipboard write is unhandled — success toast fires even on failure, and no aria-label is present on the button.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/src/pages/GoalDetail.tsx
Line: 131-134

Comment:
**Success toast fires regardless of clipboard outcome**

`navigator.clipboard.writeText()` returns a Promise that can be rejected (e.g., clipboard permission denied, non-HTTPS context, document not focused). Because the call is neither `await`-ed nor given a `.catch()` handler, the "Goal ID copied" toast fires unconditionally — giving the user false positive feedback even when the copy silently failed.

`RoutineDetail.tsx` shows the established codebase pattern for this:

```suggestion
            onClick={async () => {
              try {
                await navigator.clipboard.writeText(goal.id);
                pushToast({ title: "Goal ID copied", tone: "success" });
              } catch {
                pushToast({ title: "Failed to copy goal ID", tone: "error" });
              }
            }}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: ui/src/pages/GoalDetail.tsx
Line: 127-137

Comment:
**Button lacks accessible label for screen readers**

The `title` attribute provides a tooltip on hover but is not reliably announced by screen readers. Since the button's visible text (`a1b2c3d4`) doesn't describe its action, adding an `aria-label` would improve accessibility and be consistent with how the rest of the UI handles icon-only or abbreviated controls.

```suggestion
          <button
            type="button"
            className="text-xs font-mono text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
            title="Click to copy goal ID"
            aria-label="Copy goal ID"
            onClick={async () => {
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(ui): show copyable goal ID in goal ..." | Re-trigger Greptile

Comment on lines +131 to +134
onClick={() => {
navigator.clipboard.writeText(goal.id);
pushToast({ title: "Goal ID copied", tone: "success" });
}}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Success toast fires regardless of clipboard outcome

navigator.clipboard.writeText() returns a Promise that can be rejected (e.g., clipboard permission denied, non-HTTPS context, document not focused). Because the call is neither await-ed nor given a .catch() handler, the "Goal ID copied" toast fires unconditionally — giving the user false positive feedback even when the copy silently failed.

RoutineDetail.tsx shows the established codebase pattern for this:

Suggested change
onClick={() => {
navigator.clipboard.writeText(goal.id);
pushToast({ title: "Goal ID copied", tone: "success" });
}}
onClick={async () => {
try {
await navigator.clipboard.writeText(goal.id);
pushToast({ title: "Goal ID copied", tone: "success" });
} catch {
pushToast({ title: "Failed to copy goal ID", tone: "error" });
}
}}
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/pages/GoalDetail.tsx
Line: 131-134

Comment:
**Success toast fires regardless of clipboard outcome**

`navigator.clipboard.writeText()` returns a Promise that can be rejected (e.g., clipboard permission denied, non-HTTPS context, document not focused). Because the call is neither `await`-ed nor given a `.catch()` handler, the "Goal ID copied" toast fires unconditionally — giving the user false positive feedback even when the copy silently failed.

`RoutineDetail.tsx` shows the established codebase pattern for this:

```suggestion
            onClick={async () => {
              try {
                await navigator.clipboard.writeText(goal.id);
                pushToast({ title: "Goal ID copied", tone: "success" });
              } catch {
                pushToast({ title: "Failed to copy goal ID", tone: "error" });
              }
            }}
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +127 to +137
<button
type="button"
className="text-xs font-mono text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
title="Click to copy goal ID"
onClick={() => {
navigator.clipboard.writeText(goal.id);
pushToast({ title: "Goal ID copied", tone: "success" });
}}
>
{goal.id.slice(0, 8)}
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Button lacks accessible label for screen readers

The title attribute provides a tooltip on hover but is not reliably announced by screen readers. Since the button's visible text (a1b2c3d4) doesn't describe its action, adding an aria-label would improve accessibility and be consistent with how the rest of the UI handles icon-only or abbreviated controls.

Suggested change
<button
type="button"
className="text-xs font-mono text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
title="Click to copy goal ID"
onClick={() => {
navigator.clipboard.writeText(goal.id);
pushToast({ title: "Goal ID copied", tone: "success" });
}}
>
{goal.id.slice(0, 8)}
</button>
<button
type="button"
className="text-xs font-mono text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
title="Click to copy goal ID"
aria-label="Copy goal ID"
onClick={async () => {
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/pages/GoalDetail.tsx
Line: 127-137

Comment:
**Button lacks accessible label for screen readers**

The `title` attribute provides a tooltip on hover but is not reliably announced by screen readers. Since the button's visible text (`a1b2c3d4`) doesn't describe its action, adding an `aria-label` would improve accessibility and be consistent with how the rest of the UI handles icon-only or abbreviated controls.

```suggestion
          <button
            type="button"
            className="text-xs font-mono text-muted-foreground hover:text-foreground transition-colors cursor-pointer"
            title="Click to copy goal ID"
            aria-label="Copy goal ID"
            onClick={async () => {
```

How can I resolve this? If you propose a fix, please make it concise.

navigator.clipboard.writeText() return Promise that was not awaited,
so success toast was showing even when clipboard write fail. Now we
await the Promise inside try/catch and show error toast on failure.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant