The charger row asks the same way everything else does

Removing a home charger kept its own inline prompt, written before there
was an app-wide one. Two mechanisms for one question is one too many: it
now calls askConfirm() like every other destructive action, and the panel,
its pending-removal state and the ask/cancel pair go with it. The button
still disables while the delete is in flight.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-09-01 12:09:59 +02:00
co-authored by Claude Opus 5
parent c9ffb9c698
commit 9686cae8b9
+9 -43
View File
@@ -1,6 +1,7 @@
<script setup>
import { ref, computed, onMounted, watch } from "vue";
import { t } from "../i18n";
import { askConfirm } from "../lib/confirm.js";
import { api } from "../api";
import { formatDateTime } from "../lib/format.js";
import ChargerImportModal from "../components/ChargerImportModal.vue";
@@ -232,30 +233,17 @@ function onChargerImported(charger) {
loadChargerLive(true);
}
// Removing asks first, inline. A window.confirm() would be shorter, but a
// browser that suppresses dialogs — an embedded webview, a blocked-popups
// setting — hands back false, and the button then does nothing at all with
// nothing said about why. The prompt is part of the page instead, the way the
// charger reset above already asks.
const pendingRemoval = ref(""); // charger id awaiting a yes
// Removing asks first — through the app's own prompt, never window.confirm(),
// which a browser that suppresses dialogs answers with a silent no.
const removing = ref("");
function askRemoveHomeCharger(c) {
homeChargersError.value = "";
pendingRemoval.value = c.id;
}
function cancelRemoveHomeCharger() {
pendingRemoval.value = "";
}
async function removeHomeCharger(c) {
homeChargersError.value = "";
if (!(await askConfirm(t("charging.home.removeConfirm", { name: c.name })))) return;
removing.value = c.id;
try {
await api.deleteHomeCharger(c.id);
homeChargers.value = homeChargers.value.filter((x) => x.id !== c.id);
pendingRemoval.value = "";
} catch (e) {
homeChargersError.value = e.message;
} finally {
@@ -674,8 +662,9 @@ onMounted(async () => {
</button>
</div>
<div v-for="c in homeChargers" :key="c.id">
<div
v-for="c in homeChargers"
:key="c.id"
class="flex w-full items-center gap-3 rounded-control p-3 text-left transition-colors"
:class="selected === c.id ? 'bg-brand-100' : 'hover:bg-sunken'"
>
@@ -694,38 +683,15 @@ onMounted(async () => {
</button>
<button
type="button"
class="shrink-0 text-xs font-medium text-muted hover:text-danger"
class="shrink-0 text-xs font-medium text-muted hover:text-danger disabled:opacity-50"
:title="t('charging.home.remove')"
@click="askRemoveHomeCharger(c)"
:disabled="removing === c.id"
@click="removeHomeCharger(c)"
>
{{ t("charging.home.remove") }}
</button>
</div>
<!-- Asked here rather than in a dialog the browser may never show. -->
<div
v-if="pendingRemoval === c.id"
class="mx-3 mb-2 rounded-control border border-danger/40 bg-danger-soft p-3"
>
<p class="text-xs font-medium text-danger">
{{ t("charging.home.removeConfirm", { name: c.name }) }}
</p>
<div class="mt-2 flex gap-2">
<button type="button" class="dh-btn dh-btn-ghost grow" @click="cancelRemoveHomeCharger">
{{ t("common.cancel") }}
</button>
<button
type="button"
class="dh-btn dh-btn-danger grow"
:disabled="removing === c.id"
@click="removeHomeCharger(c)"
>
{{ t("charging.home.remove") }}
</button>
</div>
</div>
</div>
<!-- Nothing imported yet: say what this list is for and offer the import. -->
<div v-if="homeChargers.length === 0" class="px-3 pb-3 pt-1">
<p class="text-sm text-muted">{{ t("charging.home.empty") }}</p>