Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 92 additions & 61 deletions apps/web/core/components/issues/create-issue-toast-action-items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,70 +21,101 @@ type TCreateIssueToastActionItems = {
isEpic?: boolean;
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export const CreateIssueToastActionItems = observer(function CreateIssueToastActionItems(
props: TCreateIssueToastActionItems
) {
const { workspaceSlug, projectId, issueId, isEpic = false } = props;
// state
const [copied, setCopied] = useState(false);
// store hooks
const {
issue: { getIssueById },
} = useIssueDetail();
const { getProjectIdentifierById } = useProject();
export const CreateIssueToastActionItems = observer(
function CreateIssueToastActionItems(props: TCreateIssueToastActionItems) {
const { workspaceSlug, projectId, issueId, isEpic = false } = props;
// state
const [copied, setCopied] = useState(false);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
// store hooks
const {
issue: { getIssueById },
} = useIssueDetail();
const { getProjectIdentifierById } = useProject();

// derived values
const issue = getIssueById(issueId);
const projectIdentifier = getProjectIdentifierById(issue?.project_id);
// derived values
const issue = getIssueById(issueId);
const projectIdentifier = getProjectIdentifierById(issue?.project_id);

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.

⚠️ Potential issue | 🟡 Minor

Guard the visible work item ID until the project identifier exists.

getProjectIdentifierById can return undefined, and Line 64 interpolates that directly into the rendered/copiable ID. In that state the toast shows undefined-<sequence_id>, which is worse than temporarily hiding the ID affordances.

Also applies to: 64-65, 82-88

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/core/components/issues/create-issue-toast-action-items.tsx` around
lines 37 - 38, The code uses getProjectIdentifierById(issue?.project_id) and
then interpolates projectIdentifier into the displayed/copiable ID, which can
produce "undefined-<sequence_id>"; wrap all places that render or build the work
item ID (where projectIdentifier is used — e.g., the visible ID string, copy
button, and any handlers that construct
`${projectIdentifier}-${issue.sequence_id}`) in a conditional that only shows
those affordances when projectIdentifier is truthy, and otherwise render a
placeholder or nothing; also update the copy handler to guard against undefined
projectIdentifier so it never constructs or copies an invalid string.

if (!issue) return null;
if (!issue) return null;

const workItemLink = generateWorkItemLink({
workspaceSlug,
projectId: issue?.project_id,
issueId,
projectIdentifier,
sequenceId: issue?.sequence_id,
isEpic,
});
const workItemLink = generateWorkItemLink({
workspaceSlug,
projectId: issue?.project_id,
issueId,
projectIdentifier,
sequenceId: issue?.sequence_id,
isEpic,
});

const copyToClipboard = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
try {
await copyUrlToClipboard(workItemLink);
setCopied(true);
setTimeout(() => setCopied(false), 3000);
} catch (error) {
setCopied(false);
}
e.preventDefault();
e.stopPropagation();
};
const copyToClipboard = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
) => {
try {
await copyUrlToClipboard(workItemLink);
setCopied(true);
setTimeout(() => setCopied(false), 3000);
} catch (error) {
setCopied(false);
}
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.

⚠️ Potential issue | 🟡 Minor

Clipboard failures are swallowed silently.

Both handlers catch the write error, clear local state, and then do nothing else. A denied clipboard permission will look like a no-op to the user and leave no diagnostic trail. Please log/report the error and surface a failure state.

As per coding guidelines, "Use try-catch with proper error types and log errors appropriately for error handling."

Also applies to: 69-75

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/core/components/issues/create-issue-toast-action-items.tsx` around
lines 53 - 59, The clipboard catch blocks in create-issue-toast-action-items.tsx
(around the copyUrlToClipboard calls) swallow errors; update both catch handlers
to (1) distinguish and handle proper Error types, (2) call the app
logging/reporting utility (e.g., logger.error or reportError) with the caught
error and context (workItemLink, action name), and (3) surface a visible failure
state by adding/using a state variable such as copyFailed or copyError
(alongside setCopied) so the UI can show a toast/error indicator when copy
fails; ensure the handlers for copyUrlToClipboard and the other copy action both
follow this pattern and include the error details in the log/report.

e.preventDefault();
e.stopPropagation();
};

return (
<div className="-ml-2 flex items-center gap-1 text-11 text-secondary">
<a
href={workItemLink}
target="_blank"
rel="noopener noreferrer"
className="rounded-sm px-2 py-1 font-medium text-accent-primary hover:bg-surface-2"
>
{`View ${isEpic ? "epic" : "work item"}`}
</a>
const workItemId = `${projectIdentifier}-${issue?.sequence_id}`;

{copied ? (
<>
<span className="cursor-default px-2 py-1 text-secondary">Copied!</span>
</>
) : (
<>
<button
className="hidden cursor-pointer rounded-sm px-2 py-1 text-tertiary group-hover:flex hover:bg-surface-2 hover:text-secondary"
onClick={copyToClipboard}
>
Copy link
</button>
</>
)}
</div>
);
});
const copyWorkItemId = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
) => {
try {
await copyUrlToClipboard(workItemId);
setCopied(true);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
setTimeout(() => setCopied(false), 3000);
} catch (error) {
setCopied(false);
}
e.preventDefault();
e.stopPropagation();
};

return (
<div className="-ml-2 flex items-center gap-1 text-11 text-secondary">
<span className="rounded-sm px-2 py-1 font-medium text-secondary">
{workItemId}
</span>

<button
className="cursor-pointer rounded-sm px-2 py-1 text-tertiary hover:bg-surface-2 hover:text-secondary"
onClick={copyWorkItemId}
>
{copied ? "Copied!" : "Copy ID"}
</button>

<a
href={workItemLink}
target="_blank"
rel="noopener noreferrer"
className="rounded-sm px-2 py-1 font-medium text-accent-primary hover:bg-surface-2"
>
{`View ${isEpic ? "epic" : "work item"}`}
</a>

{copied ? (
<>
<span className="cursor-default px-2 py-1 text-secondary">
Link Copied!
</span>
</>
) : (
<>
<button
className="hidden cursor-pointer rounded-sm px-2 py-1 text-tertiary group-hover:flex hover:bg-surface-2 hover:text-secondary"
onClick={copyToClipboard}
>
Copy link
</button>
</>
)}
</div>
);
},
);