Skip to content

Replace CSV export with Copy SOQL to Clipboard#14

Open
jillian-k wants to merge 4 commits intobhanudas:mainfrom
jillian-k:closinator-soql-toast
Open

Replace CSV export with Copy SOQL to Clipboard#14
jillian-k wants to merge 4 commits intobhanudas:mainfrom
jillian-k:closinator-soql-toast

Conversation

@jillian-k
Copy link
Copy Markdown

Summary

  • Replace the CSV download button in the Log Viewer with a "Copy SOQL to Clipboard" button that copies the equivalent SOQL query and displays it in a sticky toast
  • The previous CSV export exceeded the 6 MB Apex heap size limit for batch runs with more than ~6,000 case logs, causing errors for users
  • Uses document.execCommand('copy') fallback since navigator.clipboard is unavailable in Lightning Locker Service

Changes

File Change
util_closer_LogViewerController.cls Replaced exportCaseLogsAsCsv with getCaseLogExportQuery -- returns SOQL string without executing it
util_closer_LogViewerController_Test.cls Updated tests to validate the returned SOQL string
util_closer_LogViewer.js Replaced Blob/download handler with clipboard copy + sticky toast; added Locker Service-compatible clipboard fallback
util_closer_LogViewer.html Changed button icon to utility:copy_to_clipboard, updated tooltip
docs/closinator-soql-toast/soql-toast-feature.md Feature documentation

Test plan

  • Open Closinator tab > Log Viewer > select a batch to open Case Logs panel
  • Click the clipboard icon button in the Case Logs panel header
  • Verify a sticky success toast appears showing the full SOQL query
  • Verify the SOQL is in the clipboard (paste into Dev Console or text editor)
  • Apply filters (result, rule, search) and click the button again -- verify the SOQL WHERE clause includes the filter conditions
  • Dismiss the toast with the X button

Made with Cursor

- Correct Child_Filter_Field__c from dot-notation to bare field API name (UJET__Fail_Reason__c)
- Remove literal quotes from Child_Filter_Value__c (eu_abandoned)
- Add Abandoned_Calls custom metadata record to source control with corrected values
- Update inlineHelpText on Child_Filter_Field__c, Child_Filter_Value__c, Owner_Name_Like__c, and Last_Modified_By_Name_Like__c to prevent misconfiguration
- Add closinator-child-object-debug debug doc with full root cause analysis and fix summary

Made-with: Cursor
New LWC (util_closer_RuleViewer) displays active Case Status Auto-Closer
rules in a collapsible accordion layout so users can see what rules and
criteria are configured without navigating to Setup. Includes new Apex
controller, test class with 100% coverage, and feature documentation.

Made-with: Cursor
The CSV download exceeded the 6MB Apex heap limit for batches with
more than ~6,000 case logs. Replace with a button that builds the
SOQL query server-side and copies it to the clipboard, shown in a
sticky toast so the user can run it in Dev Console or Workbench.

Made-with: Cursor
navigator.clipboard is not available in Locker Service context.
Use textarea + execCommand('copy') fallback instead.

Made-with: Cursor
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 3, 2026

Greptile Summary

This PR replaces the CSV export (which hit Apex heap limits at ~6,000+ records) with a "Copy SOQL to Clipboard" button that calls a new getCaseLogExportQuery Apex method, builds a filtered SOQL string server-side, and copies it via a document.execCommand('copy') textarea fallback (required by Lightning Locker Service).

  • The copyToClipboard helper ignores the boolean return value of document.execCommand('copy'). If the copy fails, handleCopySoql still fires a "SOQL Copied to Clipboard" success toast, giving the user false confirmation — they may paste into Dev Console only to find their clipboard unchanged.

Confidence Score: 4/5

Safe to merge after fixing the clipboard failure path — the Apex side is solid but the LWC shows a success toast even when the copy silently fails.

One P1 finding: document.execCommand('copy') return value is ignored, leading to a misleading success toast on clipboard failure. All other changes (Apex controller, tests, HTML) are clean. Score is 4 rather than 5 because of this present UX defect on the changed path.

force-app/main/default/lwc/util_closer_LogViewer/util_closer_LogViewer.js — copyToClipboard and handleCopySoql need to handle a false return from execCommand.

Important Files Changed

Filename Overview
force-app/main/default/lwc/util_closer_LogViewer/util_closer_LogViewer.js Replaces CSV download with clipboard copy + sticky toast; copyToClipboard does not check document.execCommand return value, causing a false success toast on failure.
force-app/main/default/classes/util_closer_LogViewerController.cls Replaces exportCaseLogsAsCsv with getCaseLogExportQuery; builds and returns a properly escaped SOQL string using the existing buildCaseWhereClause helper without executing the query.
force-app/main/default/classes/util_closer_LogViewerController_Test.cls Tests updated to validate the returned SOQL string structure and filter inclusion; coverage is adequate for the new method.
force-app/main/default/lwc/util_closer_LogViewer/util_closer_LogViewer.html Button icon and tooltip updated from download/CSV to copy_to_clipboard/SOQL; straightforward markup change.
docs/closinator-soql-toast/soql-toast-feature.md New feature doc added, but step 3 incorrectly states navigator.clipboard.writeText() is used when the code actually uses document.execCommand('copy').

Sequence Diagram

sequenceDiagram
    participant U as User
    participant LWC as util_closer_LogViewer.js
    participant Apex as LogViewerController.cls
    participant CB as Clipboard (execCommand)

    U->>LWC: Click "Copy SOQL to Clipboard"
    LWC->>Apex: getCaseLogExportQuery(batchLogId, filterJson)
    Apex-->>LWC: SOQL string
    LWC->>CB: document.execCommand('copy') via hidden textarea
    CB-->>LWC: true / false (⚠️ return value ignored)
    LWC->>U: ShowToastEvent — "SOQL Copied" (always, even on failure)
Loading

Comments Outside Diff (1)

  1. docs/closinator-soql-toast/soql-toast-feature.md, line 413 (link)

    P2 Doc refers to navigator.clipboard but code uses execCommand

    Step 3 of "How the new button works" says the SOQL is copied "via navigator.clipboard.writeText()", but the actual implementation in util_closer_LogViewer.js uses the document.execCommand('copy') textarea fallback — navigator.clipboard is intentionally avoided due to Lightning Locker Service restrictions. This will mislead anyone trying to debug clipboard issues.

    I tried to write documentation once but I kept making typos. I guess you could say my writing was just a bit… off the record. 📝

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: docs/closinator-soql-toast/soql-toast-feature.md
    Line: 413
    
    Comment:
    **Doc refers to `navigator.clipboard` but code uses `execCommand`**
    
    Step 3 of "How the new button works" says the SOQL is copied "via `navigator.clipboard.writeText()`", but the actual implementation in `util_closer_LogViewer.js` uses the `document.execCommand('copy')` textarea fallback — `navigator.clipboard` is intentionally avoided due to Lightning Locker Service restrictions. This will mislead anyone trying to debug clipboard issues.
    
    
    
    I tried to write documentation once but I kept making typos. I guess you could say my writing was just a bit… *off the record*. 📝
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: force-app/main/default/lwc/util_closer_LogViewer/util_closer_LogViewer.js
Line: 659-660

Comment:
**Silent clipboard failure shows false success toast**

`document.execCommand('copy')` returns `false` when the copy fails (e.g., the element wasn't focused, the browser blocks it, or the API is not supported). The current code ignores this return value, so `handleCopySoql` always calls `showToast('SOQL Copied to Clipboard', ...)` even when nothing was actually written to the clipboard — the user gets a success message for a failed operation.

```suggestion
        const success = document.execCommand('copy');
        document.body.removeChild(textarea);
        return success;
```

Then in `handleCopySoql`, check the result:

```js
const copied = this.copyToClipboard(soql);
if (copied) {
    this.showToast('SOQL Copied to Clipboard', soql, 'success', 'sticky');
} else {
    this.showError('Clipboard Copy Failed', { message: 'Could not copy SOQL. Please copy it manually.' });
}
```

Why did the programmer break up with the clipboard? Because it kept taking things without asking! 📋

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

---

This is a comment left during a code review.
Path: docs/closinator-soql-toast/soql-toast-feature.md
Line: 413

Comment:
**Doc refers to `navigator.clipboard` but code uses `execCommand`**

Step 3 of "How the new button works" says the SOQL is copied "via `navigator.clipboard.writeText()`", but the actual implementation in `util_closer_LogViewer.js` uses the `document.execCommand('copy')` textarea fallback — `navigator.clipboard` is intentionally avoided due to Lightning Locker Service restrictions. This will mislead anyone trying to debug clipboard issues.

```suggestion
3. The SOQL string is copied to the clipboard via a `document.execCommand('copy')` textarea fallback. (`navigator.clipboard` is unavailable in Lightning Locker Service.)
```

I tried to write documentation once but I kept making typos. I guess you could say my writing was just a bit… *off the record*. 📝

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

Reviews (1): Last reviewed commit: "Fix clipboard copy for Lightning Locker ..." | Re-trigger Greptile

Comment on lines +659 to +660
document.execCommand('copy');
document.body.removeChild(textarea);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Silent clipboard failure shows false success toast

document.execCommand('copy') returns false when the copy fails (e.g., the element wasn't focused, the browser blocks it, or the API is not supported). The current code ignores this return value, so handleCopySoql always calls showToast('SOQL Copied to Clipboard', ...) even when nothing was actually written to the clipboard — the user gets a success message for a failed operation.

Suggested change
document.execCommand('copy');
document.body.removeChild(textarea);
const success = document.execCommand('copy');
document.body.removeChild(textarea);
return success;

Then in handleCopySoql, check the result:

const copied = this.copyToClipboard(soql);
if (copied) {
    this.showToast('SOQL Copied to Clipboard', soql, 'success', 'sticky');
} else {
    this.showError('Clipboard Copy Failed', { message: 'Could not copy SOQL. Please copy it manually.' });
}

Why did the programmer break up with the clipboard? Because it kept taking things without asking! 📋

Prompt To Fix With AI
This is a comment left during a code review.
Path: force-app/main/default/lwc/util_closer_LogViewer/util_closer_LogViewer.js
Line: 659-660

Comment:
**Silent clipboard failure shows false success toast**

`document.execCommand('copy')` returns `false` when the copy fails (e.g., the element wasn't focused, the browser blocks it, or the API is not supported). The current code ignores this return value, so `handleCopySoql` always calls `showToast('SOQL Copied to Clipboard', ...)` even when nothing was actually written to the clipboard — the user gets a success message for a failed operation.

```suggestion
        const success = document.execCommand('copy');
        document.body.removeChild(textarea);
        return success;
```

Then in `handleCopySoql`, check the result:

```js
const copied = this.copyToClipboard(soql);
if (copied) {
    this.showToast('SOQL Copied to Clipboard', soql, 'success', 'sticky');
} else {
    this.showError('Clipboard Copy Failed', { message: 'Could not copy SOQL. Please copy it manually.' });
}
```

Why did the programmer break up with the clipboard? Because it kept taking things without asking! 📋

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

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