mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 06:59:46 +00:00
feat: validate vacuum map routes and clean them up with their space
User-Visible: no Issue: #162
This commit is contained in:
@@ -226,7 +226,7 @@ import {
|
||||
} from './zero-walls';
|
||||
import { snapNearAxisEndpoint } from './near-axis';
|
||||
import type { SpaceReferenceRepairContext } from './space-reference-repair';
|
||||
import { collectSpaceMarkerDependencies } from './space-deletion';
|
||||
import { collectSpaceMarkerDependencies, spaceDeletionMessage } from './space-deletion';
|
||||
import {
|
||||
checkSpacePhysicalGeometry,
|
||||
checkOptimizeGeometry,
|
||||
@@ -8863,7 +8863,8 @@ public async _deleteSpace(): Promise<void> {
|
||||
key: 'delete-space',
|
||||
kind: 'destructive',
|
||||
title: this.host._t('confirm.delete_space_title'),
|
||||
message: this.host._t('confirm.delete_space_body'),
|
||||
message: spaceDeletionMessage(this.host._t('confirm.delete_space_body'),
|
||||
this.host._t('confirm.delete_space_vac_routes'), dependencies.routeCount),
|
||||
objectName: sp.title,
|
||||
confirmLabel: this.host._t('btn.delete'),
|
||||
cancelLabel: this.host._t('btn.cancel'),
|
||||
|
||||
@@ -292,6 +292,7 @@
|
||||
"confirm.erase_decor": "Das Objekt „{kind}“ löschen? Diese Aktion kann im Editor rückgängig gemacht werden.",
|
||||
"confirm.delete_space_title": "Bereich löschen?",
|
||||
"confirm.delete_space_body": "Der Bereich, alle seine Räume und die gesamte Planmarkierung werden gelöscht.",
|
||||
"confirm.delete_space_vac_routes": "Zuordnungen von Roboterkarten werden ebenfalls entfernt: {count}.",
|
||||
"space.delete_blocked": "Dieser Bereich wird noch von {n} Gerät(en) verwendet. Die Geräte zuerst in einen anderen Bereich verschieben oder löschen.",
|
||||
"toast.pos_save_failed": "Position nicht gespeichert: {err}",
|
||||
"toast.no_entity": "Das Gerät hat keine geeignete Entität",
|
||||
@@ -1153,6 +1154,7 @@
|
||||
"backup.error.invalid_value_badge_attribute": "Ungültiges Wert-Badge-Attribut",
|
||||
"backup.error.invalid_value_badge_position": "Ungültige Wert-Badge-Position",
|
||||
"backup.error.invalid_value_badge_source": "Ungültige Wert-Badge-Quelle",
|
||||
"backup.error.invalid_vacuum_map_route": "Prüfen Sie die Zuordnung der Roboterkarten zu Räumen.",
|
||||
"backup.error.invalid_value_source": "Ungültige Wertquelle",
|
||||
"backup.error.invalid_value_source_attribute": "Ungültiges Wertquellen-Attribut",
|
||||
"backup.error.io_error": "Eine Dateioperation auf dem Server ist fehlgeschlagen",
|
||||
|
||||
@@ -292,6 +292,7 @@
|
||||
"confirm.erase_decor": "Erase the {kind} object? You can undo this action from the editor history.",
|
||||
"confirm.delete_space_title": "Delete space?",
|
||||
"confirm.delete_space_body": "The space, all its rooms and all its plan markup will be deleted.",
|
||||
"confirm.delete_space_vac_routes": "Robot map assignments will also be removed: {count}.",
|
||||
"space.delete_blocked": "This space is still used by {n} device(s). Move them to another space or delete them first.",
|
||||
"toast.pos_save_failed": "Failed to save position: {err}",
|
||||
"toast.no_entity": "The device has no suitable entity",
|
||||
@@ -1153,6 +1154,7 @@
|
||||
"backup.error.invalid_value_badge_attribute": "Invalid value badge attribute",
|
||||
"backup.error.invalid_value_badge_position": "Invalid value badge position",
|
||||
"backup.error.invalid_value_badge_source": "Invalid value badge source",
|
||||
"backup.error.invalid_vacuum_map_route": "Check how the robot's maps are assigned to spaces.",
|
||||
"backup.error.invalid_value_source": "Invalid value source",
|
||||
"backup.error.invalid_value_source_attribute": "Invalid value source attribute",
|
||||
"backup.error.io_error": "A file operation failed on the server",
|
||||
|
||||
@@ -292,6 +292,7 @@
|
||||
"confirm.erase_decor": "Effacer l’objet {kind} ? Vous pouvez annuler cette action depuis l’historique de l’éditeur.",
|
||||
"confirm.delete_space_title": "Supprimer l’espace ?",
|
||||
"confirm.delete_space_body": "L’espace, toutes ses pièces et tout le tracé du plan seront supprimés.",
|
||||
"confirm.delete_space_vac_routes": "Les affectations de cartes de robots seront aussi supprimées : {count}.",
|
||||
"space.delete_blocked": "Cet espace est encore utilisé par {n} appareil(s). Déplacez-les vers un autre espace ou supprimez-les d’abord.",
|
||||
"toast.pos_save_failed": "Échec de l’enregistrement de la position : {err}",
|
||||
"toast.no_entity": "L’appareil n’a aucune entité adaptée",
|
||||
@@ -1153,6 +1154,7 @@
|
||||
"backup.error.invalid_value_badge_attribute": "Attribut de badge de valeur invalide",
|
||||
"backup.error.invalid_value_badge_position": "Position de badge de valeur invalide",
|
||||
"backup.error.invalid_value_badge_source": "Source de badge de valeur invalide",
|
||||
"backup.error.invalid_vacuum_map_route": "Vérifiez l'affectation des cartes du robot aux espaces.",
|
||||
"backup.error.invalid_value_source": "Source de valeur invalide",
|
||||
"backup.error.invalid_value_source_attribute": "Attribut de source de valeur invalide",
|
||||
"backup.error.io_error": "Une opération de fichier a échoué sur le serveur",
|
||||
|
||||
@@ -292,6 +292,7 @@
|
||||
"confirm.erase_decor": "Стереть объект «{kind}»? Действие можно отменить из истории редактора.",
|
||||
"confirm.delete_space_title": "Удалить пространство?",
|
||||
"confirm.delete_space_body": "Пространство, все его комнаты и вся разметка плана будут удалены.",
|
||||
"confirm.delete_space_vac_routes": "Также будет снято сопоставление карт роботов: {count}.",
|
||||
"space.delete_blocked": "Это пространство всё ещё используется устройствами: {n}. Сначала перенесите их в другое пространство или удалите.",
|
||||
"toast.pos_save_failed": "Не удалось сохранить позицию: {err}",
|
||||
"toast.no_entity": "У устройства нет подходящей сущности",
|
||||
@@ -1153,6 +1154,7 @@
|
||||
"backup.error.invalid_value_badge_attribute": "Некорректный атрибут бейджа значения",
|
||||
"backup.error.invalid_value_badge_position": "Некорректная позиция бейджа значения",
|
||||
"backup.error.invalid_value_badge_source": "Некорректный источник бейджа значения",
|
||||
"backup.error.invalid_vacuum_map_route": "Проверьте сопоставление карт робота пространствам.",
|
||||
"backup.error.invalid_value_source": "Некорректный источник значения",
|
||||
"backup.error.invalid_value_source_attribute": "Некорректный атрибут источника значения",
|
||||
"backup.error.io_error": "Файловая операция на сервере не удалась",
|
||||
|
||||
+42
-1
@@ -3,8 +3,21 @@
|
||||
export interface SpaceDeletionDependencyReport {
|
||||
markerIds: string[];
|
||||
count: number;
|
||||
/**
|
||||
* Markers that live on ANOTHER floor but route one of their robot maps here
|
||||
* (#162). They do not block the deletion — the dock is not in this space —
|
||||
* but the user has to be told how many map assignments disappear with it.
|
||||
*/
|
||||
routeMarkerIds: string[];
|
||||
routeCount: number;
|
||||
}
|
||||
|
||||
/** Ids of routes this marker points at the space being deleted. */
|
||||
const routesIntoSpace = (marker: any, spaceId: string): string[] =>
|
||||
(marker?.vacuum?.map_routes || [])
|
||||
.filter((route: any) => route && route.space === spaceId && typeof route.id === 'string')
|
||||
.map((route: any) => route.id);
|
||||
|
||||
const clone = <T>(value: T): T => JSON.parse(JSON.stringify(value));
|
||||
|
||||
export function collectSpaceMarkerDependencies(
|
||||
@@ -23,7 +36,27 @@ export function collectSpaceMarkerDependencies(
|
||||
|| layout?.[marker.id]?.s === spaceId)
|
||||
.map((marker: any) => marker.id))]
|
||||
.sort((a: string, b: string) => a.localeCompare(b));
|
||||
return { markerIds, count: markerIds.length };
|
||||
const routeMarkerIds = (config?.markers || [])
|
||||
.filter((marker: any) => marker?.removed !== true && typeof marker?.id === 'string')
|
||||
.filter((marker: any) => !markerIds.includes(marker.id))
|
||||
.filter((marker: any) => routesIntoSpace(marker, spaceId).length > 0)
|
||||
.map((marker: any) => marker.id)
|
||||
.sort((a: string, b: string) => a.localeCompare(b));
|
||||
return {
|
||||
markerIds, count: markerIds.length,
|
||||
routeMarkerIds, routeCount: routeMarkerIds.length,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm text for a space delete: the base warning, plus how many robot map
|
||||
* assignments go with it (#162). Composed here so the count cannot quietly
|
||||
* fall out of the dialog when the wording changes.
|
||||
*/
|
||||
export function spaceDeletionMessage(
|
||||
base: string, routesTemplate: string, routeCount: number,
|
||||
): string {
|
||||
return routeCount ? `${base} ${routesTemplate.replace('{count}', String(routeCount))}` : base;
|
||||
}
|
||||
|
||||
export function createSpaceDeletionCandidate(
|
||||
@@ -61,5 +94,13 @@ export function createSpaceDeletionCandidate(
|
||||
for (const [key, position] of Object.entries(layout)) {
|
||||
if ((position as any)?.s === spaceId) delete layout[key];
|
||||
}
|
||||
// #162: a robot docked elsewhere keeps its dock and its other maps; only the
|
||||
// routes that pointed here go, in the same logical operation as the space.
|
||||
for (const marker of config.markers || []) {
|
||||
const routes = marker?.vacuum?.map_routes;
|
||||
if (!Array.isArray(routes)) continue;
|
||||
const kept = routes.filter((route: any) => route?.space !== spaceId);
|
||||
if (kept.length !== routes.length) marker.vacuum.map_routes = kept;
|
||||
}
|
||||
return { config, layout, dependencies };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user