Skip to content

fix(ai-agent): confirm before deleting a conversation, and surface delete failures - #617

Merged
navneetkumar-pim-webkul merged 4 commits into
unopim:3.0from
midego1:fix/confirm-session-delete
Aug 10, 2026
Merged

navneetkumar-pim-webkul merged 4 commits into
unopim:3.0from
midego1:fix/confirm-session-delete

Conversation

@midego1

@midego1 midego1 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Deleting a conversation in the Agentic PIM widget is destructive and irreversible, but deleteSession() did it on a single click with no confirmation, and swallowed any server error:

async deleteSession(sessionId) {
    this.sessions = this.sessions.filter(s => s.id !== sessionId);
    this.persistSessions();
    if (typeof sessionId === "number") {
        try {
            await this.$axios.delete(".../ai-agent/conversations/" + sessionId);
        } catch (e) { /* ignore */ }   // <-
    }
}

Two problems:

  1. No confirmation. A stray click on the trash icon destroys a conversation with no way back.
  2. Silent failure. The local list is filtered before the server call, and the error is ignored. If the server delete fails, the session disappears from the UI while its record remains, and the user is told nothing.

The change

  • The trash icon arms on first click, swapping to a confirm (✓) / cancel (✗) pair on that row. Only confirm deletes. This reuses the widget's own visual language rather than a browser confirm().
  • The server delete runs first. On failure the session is kept in the list and an error is surfaced via this.$emitter?.emit?.("add-flash", ...), the same mechanism the rest of the admin (e.g. the data-transfer job tracker) uses. Optional-chained so it degrades quietly if the emitter is absent.
  • Three widget strings added to all 34 locales (English text, for translators to localise): confirm-delete, cancel, delete-session-failed.

Verification

Live on UnoPim 3.0.0:

Action Result
First click on trash Arms; session not deleted; Confirm/Cancel shown
Cancel Disarms; all sessions intact
Confirm Exactly the armed session removed

Screens driven through the real widget in the admin, asserting on the persisted session list at each step.

midego1 and others added 3 commits August 10, 2026 10:12
…ilures

deleteSession() removed a conversation on a single click, with no
confirmation, and swallowed any server error (catch (e) { /* ignore */ }).
A stray click destroyed a conversation irreversibly, and if the server-side
delete failed the session still vanished from the list, leaving the UI and
the database out of step with nothing shown to the user.

The trash icon now arms on first click, swapping to a confirm/cancel pair on
that row; only confirm deletes. The server delete runs first, and on failure
the session is kept in the list and an error flash is emitted via the admin's
$emitter, matching how the rest of the admin reports failures.

Three widget strings added across all locales (English text, to be
localised): confirm-delete, cancel, delete-session-failed.

Verified live on UnoPim 3.0.0: first click arms without deleting, cancel
disarms leaving all sessions, confirm removes exactly the armed one.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NPG1N6dCsWFQjMUo1caPHQ
The arm-on-first-click trash button duplicated behaviour the admin already
provides. `<x-admin::modal.confirm />` is mounted in the layout and the widget
renders inside the same Vue root, so `open-delete-modal` reaches it directly and
brings its own title, message, button styling and translations.

Dropping the inline confirm UI also removes the three widget strings the PR
added in English across all 34 locale files; the failure flash reuses
`widget.error-generic`, which is already translated everywhere.

The delete ordering fix is unchanged: the server call runs first, and a failure
keeps the session in the list instead of hiding it locally.
@navneetkumar-pim-webkul

Copy link
Copy Markdown
Collaborator

Thanks for catching this — the underlying bug is real and your fix for it is correct: the server delete has to run before the local filter, and a failure must keep the session in the list rather than hiding it. That part is untouched.

I've pushed 8345bd32 on top, which changes how the confirmation and the error message are delivered, so they go through what the admin already provides.

Confirmation → the existing admin modal

<x-admin::modal.confirm /> is already mounted in the layout (layouts/index.blade.php:74), and the widget is injected on unopim.admin.layout.content.after — inside the same <div id="app">, so it shares the Vue root and the $emitter bus. That makes it reachable with one emit, the same way the DataGrid does it:

confirmDeleteSession(sessionId) {
    this.$emitter.emit('open-delete-modal', {
        agree: () => this.deleteSession(sessionId),
    });
},

openDelete() supplies the title, message, danger-button / transparent-button classes and their translations from admin::app.components.modal.delete.*. So the arm-on-first-click template, the two hand-rolled SVG buttons, the inline style= attributes and the confirmingDeleteId state all come out.

Translations

The three new widget keys were added to all 34 locale files with the English value. Our rule is that a key lands in en_US first and is then translated naturally into every locale — an English string sitting in ja_JP ships as untranslated UI, and unopim:translations:check won't flag it because it only checks key presence.

Reusing the modal removes the need for confirm-delete and cancel entirely, and the failure flash now uses widget.error-generic ("Something went wrong. Please try again."), which already exists in the widget's own translation map and is already used for the other widget error paths. Net result: all 34 locale files are byte-identical to 3.0 again and the change is confined to one file.

Comments

The Blade block comment and the three-line comment inside deleteSession() were dropped. House rule is no comments inside method bodies or Blade markup — a non-obvious rationale belongs in the class/method PHPDoc or in the commit message, which is where that reasoning now lives.

One smaller thing

this.$emitter?.emit?.(…) was optional-chained to degrade if the emitter is absent. Since the widget is a component of the same app instance the emitter is always present, and a silent no-op would hide the very failure the flash is there to report — so it's a plain this.$emitter.emit(…), matching app.js.

Verified

Blade compiles and the compiled output lints clean; Pint passes; no dangling references; the v-if/v-else chain is back to its original two-branch form with @click.stop intact; git diff 3.0 -- Resources/lang/ is empty; the panel is z-index: 10000 against the modal's z-[10002], so the modal renders above it.

Not verified, and worth your time: a live run. Your verification table asserts on the arm/confirm rows, which no longer exist — the flow is now trash → delete modal → Delete/Cancel. Could you re-run that against the modal before this merges?

@navneetkumar-pim-webkul
navneetkumar-pim-webkul merged commit 44082d8 into unopim:3.0 Aug 10, 2026
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.

2 participants