Skip to content

Commit c679e31

Browse files
frenckclaudeMindFreeze
authored
Add delete option to reauth cards on integrations dashboard (#28020)
* Add delete option to reauth cards on integrations dashboard Users can now delete config entries directly from the reauth card that appears at the top of the integrations dashboard, instead of having to scroll down to find the original integration card. The delete option: - Appears in the three-dot menu on reauth cards - Shows a confirmation dialog before deletion - Handles application credentials cleanup - Shows restart notifications when required - Uses the same styling and localization as the integration entry delete 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Apply suggestions from code review Co-authored-by: Petar Petrov <[email protected]> --------- Co-authored-by: Claude <[email protected]> Co-authored-by: Petar Petrov <[email protected]>
1 parent 37e12a8 commit c679e31

File tree

1 file changed

+139
-3
lines changed

1 file changed

+139
-3
lines changed

src/panels/config/integrations/ha-config-flow-card.ts

Lines changed: 139 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { mdiBookshelf, mdiCog, mdiDotsVertical, mdiOpenInNew } from "@mdi/js";
1+
import {
2+
mdiBookshelf,
3+
mdiCog,
4+
mdiDelete,
5+
mdiDotsVertical,
6+
mdiOpenInNew,
7+
} from "@mdi/js";
28
import type { TemplateResult } from "lit";
39
import { LitElement, css, html } from "lit";
410
import { customElement, property } from "lit/decorators";
@@ -7,6 +13,11 @@ import { fireEvent } from "../../../common/dom/fire_event";
713
import "../../../components/ha-button";
814
import "../../../components/ha-button-menu";
915
import "../../../components/ha-list-item";
16+
import {
17+
deleteApplicationCredential,
18+
fetchApplicationCredentialsConfigEntry,
19+
} from "../../../data/application_credential";
20+
import { deleteConfigEntry } from "../../../data/config_entries";
1021
import {
1122
ATTENTION_SOURCES,
1223
DISCOVERY_SOURCES,
@@ -15,7 +26,10 @@ import {
1526
} from "../../../data/config_flow";
1627
import type { IntegrationManifest } from "../../../data/integration";
1728
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
18-
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
29+
import {
30+
showAlertDialog,
31+
showConfirmationDialog,
32+
} from "../../../dialogs/generic/show-dialog-box";
1933
import type { HomeAssistant } from "../../../types";
2034
import { documentationUrl } from "../../../util/documentation-url";
2135
import type { DataEntryFlowProgressExtended } from "./ha-config-integrations";
@@ -60,7 +74,7 @@ export class HaConfigFlowCard extends LitElement {
6074
: "ui.common.add"
6175
)}
6276
</ha-button>
63-
${this.flow.context.configuration_url || this.manifest
77+
${this.flow.context.configuration_url || this.manifest || attention
6478
? html`<ha-button-menu slot="header-button">
6579
<ha-icon-button
6680
slot="trigger"
@@ -118,6 +132,22 @@ export class HaConfigFlowCard extends LitElement {
118132
</ha-list-item>
119133
</a>`
120134
: ""}
135+
${attention
136+
? html`<ha-list-item
137+
class="warning"
138+
graphic="icon"
139+
@click=${this._handleDelete}
140+
>
141+
<ha-svg-icon
142+
class="warning"
143+
slot="graphic"
144+
.path=${mdiDelete}
145+
></ha-svg-icon>
146+
${this.hass.localize(
147+
"ui.panel.config.integrations.config_entry.delete"
148+
)}
149+
</ha-list-item>`
150+
: ""}
121151
</ha-button-menu>`
122152
: ""}
123153
</ha-integration-action-card>
@@ -175,6 +205,109 @@ export class HaConfigFlowCard extends LitElement {
175205
});
176206
}
177207

208+
// Return an application credentials id for this config entry to prompt the
209+
// user for removal. This is best effort so we don't stop overall removal
210+
// if the integration isn't loaded or there is some other error.
211+
private async _fetchApplicationCredentials(entryId: string) {
212+
try {
213+
return (await fetchApplicationCredentialsConfigEntry(this.hass, entryId))
214+
.application_credentials_id;
215+
} catch (_err: any) {
216+
// We won't prompt the user to remove credentials
217+
return null;
218+
}
219+
}
220+
221+
private async _removeApplicationCredential(applicationCredentialsId: string) {
222+
const confirmed = await showConfirmationDialog(this, {
223+
title: this.hass.localize(
224+
"ui.panel.config.integrations.config_entry.application_credentials.delete_title"
225+
),
226+
text: html`${this.hass.localize(
227+
"ui.panel.config.integrations.config_entry.application_credentials.delete_prompt"
228+
)},
229+
<br />
230+
<br />
231+
${this.hass.localize(
232+
"ui.panel.config.integrations.config_entry.application_credentials.delete_detail"
233+
)}
234+
<br />
235+
<br />
236+
<a
237+
href="https://www.home-assistant.io/integrations/application_credentials"
238+
target="_blank"
239+
rel="noreferrer"
240+
>
241+
${this.hass.localize(
242+
"ui.panel.config.integrations.config_entry.application_credentials.learn_more"
243+
)}
244+
</a>`,
245+
confirmText: this.hass.localize("ui.common.delete"),
246+
dismissText: this.hass.localize("ui.common.cancel"),
247+
destructive: true,
248+
});
249+
250+
if (!confirmed) {
251+
return;
252+
}
253+
254+
try {
255+
await deleteApplicationCredential(this.hass, applicationCredentialsId);
256+
} catch (err: any) {
257+
showAlertDialog(this, {
258+
title: this.hass.localize(
259+
"ui.panel.config.integrations.config_entry.application_credentials.delete_error_title"
260+
),
261+
text: err.message,
262+
});
263+
}
264+
}
265+
266+
private async _handleDelete() {
267+
const entryId = this.flow.context.entry_id;
268+
269+
if (!entryId) {
270+
// This shouldn't happen for reauth flows, but handle gracefully
271+
return;
272+
}
273+
274+
const applicationCredentialsId =
275+
await this._fetchApplicationCredentials(entryId);
276+
277+
const confirmed = await showConfirmationDialog(this, {
278+
title: this.hass.localize(
279+
"ui.panel.config.integrations.config_entry.delete_confirm_title",
280+
{ title: localizeConfigFlowTitle(this.hass.localize, this.flow) }
281+
),
282+
text: this.hass.localize(
283+
"ui.panel.config.integrations.config_entry.delete_confirm_text"
284+
),
285+
confirmText: this.hass!.localize("ui.common.delete"),
286+
dismissText: this.hass!.localize("ui.common.cancel"),
287+
destructive: true,
288+
});
289+
290+
if (!confirmed) {
291+
return;
292+
}
293+
294+
const result = await deleteConfigEntry(this.hass, entryId);
295+
296+
if (result.require_restart) {
297+
showAlertDialog(this, {
298+
text: this.hass.localize(
299+
"ui.panel.config.integrations.config_entry.restart_confirm"
300+
),
301+
});
302+
}
303+
304+
if (applicationCredentialsId) {
305+
this._removeApplicationCredential(applicationCredentialsId);
306+
}
307+
308+
this._handleFlowUpdated();
309+
}
310+
178311
static styles = css`
179312
a {
180313
text-decoration: none;
@@ -191,6 +324,9 @@ export class HaConfigFlowCard extends LitElement {
191324
--mdc-theme-primary: var(--error-color);
192325
--ha-card-border-color: var(--error-color);
193326
}
327+
.warning {
328+
--mdc-theme-text-primary-on-background: var(--error-color);
329+
}
194330
`;
195331
}
196332

0 commit comments

Comments
 (0)