fix: improve command palette actions and automation condition button
All checks were successful
Lint & Test / test (push) Successful in 1m16s
All checks were successful
Lint & Test / test (push) Successful in 1m16s
- Action items (start/stop, enable/disable) no longer close the palette - Action items toggle state after success (Start→Stop, Enable→Disable) - Toast z-index raised above command palette backdrop (3000→3500) - Automation condition remove button uses ICON_TRASH SVG instead of ✕
This commit is contained in:
@@ -49,7 +49,7 @@
|
||||
--z-log-overlay: 2100;
|
||||
--z-confirm: 2500;
|
||||
--z-command-palette: 3000;
|
||||
--z-toast: 3000;
|
||||
--z-toast: 3500;
|
||||
--z-overlay-spinner: 9999;
|
||||
--z-lightbox: 10000;
|
||||
--z-connection: 10000;
|
||||
|
||||
@@ -57,26 +57,31 @@ function _buildItems(results: any[], states: any = {}) {
|
||||
nav: ['targets', 'led-targets', 'led-targets', 'data-target-id', tgt.id], running,
|
||||
});
|
||||
}
|
||||
// Action items: start or stop
|
||||
if (running) {
|
||||
items.push({
|
||||
name: tgt.name, detail: t('search.action.stop'), group: 'actions', icon: '■',
|
||||
action: async () => {
|
||||
const resp = await fetchWithAuth(`/output-targets/${tgt.id}/stop`, { method: 'POST' });
|
||||
if (resp.ok) { showToast(t('device.stopped'), 'success'); }
|
||||
else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || t('target.error.stop_failed'), 'error'); }
|
||||
},
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
name: tgt.name, detail: t('search.action.start'), group: 'actions', icon: '▶',
|
||||
action: async () => {
|
||||
const resp = await fetchWithAuth(`/output-targets/${tgt.id}/start`, { method: 'POST' });
|
||||
if (resp.ok) { showToast(t('device.started'), 'success'); }
|
||||
else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || t('target.error.start_failed'), 'error'); }
|
||||
},
|
||||
});
|
||||
}
|
||||
// Action item: toggle start/stop
|
||||
const actionItem: any = {
|
||||
name: tgt.name, group: 'actions',
|
||||
detail: running ? t('search.action.stop') : t('search.action.start'),
|
||||
icon: running ? '■' : '▶',
|
||||
_running: running, _targetId: tgt.id,
|
||||
action: async () => {
|
||||
const isRunning = actionItem._running;
|
||||
const endpoint = isRunning ? 'stop' : 'start';
|
||||
const resp = await fetchWithAuth(`/output-targets/${tgt.id}/${endpoint}`, { method: 'POST' });
|
||||
if (resp.ok) {
|
||||
showToast(t(isRunning ? 'device.stopped' : 'device.started'), 'success');
|
||||
actionItem._running = !isRunning;
|
||||
actionItem.detail = !isRunning ? t('search.action.stop') : t('search.action.start');
|
||||
actionItem.icon = !isRunning ? '■' : '▶';
|
||||
_render();
|
||||
} else {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
const d = err.detail || err.message || '';
|
||||
const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d);
|
||||
showToast(ds || t(`target.error.${endpoint}_failed`), 'error');
|
||||
}
|
||||
},
|
||||
};
|
||||
items.push(actionItem);
|
||||
});
|
||||
|
||||
_mapEntities(css, c => items.push({
|
||||
@@ -89,25 +94,28 @@ function _buildItems(results: any[], states: any = {}) {
|
||||
name: a.name, detail: a.enabled ? 'enabled' : '', group: 'automations', icon: ICON_AUTOMATION,
|
||||
nav: ['automations', null, 'automations', 'data-automation-id', a.id],
|
||||
});
|
||||
if (a.enabled) {
|
||||
items.push({
|
||||
name: a.name, detail: t('search.action.disable'), group: 'actions', icon: ICON_AUTOMATION,
|
||||
action: async () => {
|
||||
const resp = await fetchWithAuth(`/automations/${a.id}/disable`, { method: 'POST' });
|
||||
if (resp.ok) { showToast(t('search.action.disable') + ': ' + a.name, 'success'); }
|
||||
else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || (t('search.action.disable') + ' failed'), 'error'); }
|
||||
},
|
||||
});
|
||||
} else {
|
||||
items.push({
|
||||
name: a.name, detail: t('search.action.enable'), group: 'actions', icon: ICON_AUTOMATION,
|
||||
action: async () => {
|
||||
const resp = await fetchWithAuth(`/automations/${a.id}/enable`, { method: 'POST' });
|
||||
if (resp.ok) { showToast(t('search.action.enable') + ': ' + a.name, 'success'); }
|
||||
else { const err = await resp.json().catch(() => ({})); const d = err.detail || err.message || ''; const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d); showToast(ds || (t('search.action.enable') + ' failed'), 'error'); }
|
||||
},
|
||||
});
|
||||
}
|
||||
const autoItem: any = {
|
||||
name: a.name, group: 'actions', icon: ICON_AUTOMATION,
|
||||
detail: a.enabled ? t('search.action.disable') : t('search.action.enable'),
|
||||
_enabled: a.enabled,
|
||||
action: async () => {
|
||||
const isEnabled = autoItem._enabled;
|
||||
const endpoint = isEnabled ? 'disable' : 'enable';
|
||||
const resp = await fetchWithAuth(`/automations/${a.id}/${endpoint}`, { method: 'POST' });
|
||||
if (resp.ok) {
|
||||
showToast(t('search.action.' + endpoint) + ': ' + a.name, 'success');
|
||||
autoItem._enabled = !isEnabled;
|
||||
autoItem.detail = !isEnabled ? t('search.action.disable') : t('search.action.enable');
|
||||
_render();
|
||||
} else {
|
||||
const err = await resp.json().catch(() => ({}));
|
||||
const d = err.detail || err.message || '';
|
||||
const ds = Array.isArray(d) ? d.map(x => x.msg || x).join('; ') : String(d);
|
||||
showToast(ds || (t('search.action.' + endpoint) + ' failed'), 'error');
|
||||
}
|
||||
},
|
||||
};
|
||||
items.push(autoItem);
|
||||
});
|
||||
|
||||
_mapEntities(capTempl, ct => items.push({
|
||||
@@ -378,13 +386,13 @@ function _onClick(e: Event) {
|
||||
function _selectCurrent() {
|
||||
if (_selectedIdx < 0 || _selectedIdx >= _filtered.length) return;
|
||||
const item = _filtered[_selectedIdx];
|
||||
closeCommandPalette();
|
||||
if (item.action) {
|
||||
item.action().catch(err => {
|
||||
if (!err.isAuth) showToast(err.message || 'Action failed', 'error');
|
||||
});
|
||||
return;
|
||||
}
|
||||
closeCommandPalette();
|
||||
// If graph tab is active, navigate to graph node instead of card
|
||||
const graphTabActive = document.querySelector('.tab-btn[data-tab="graph"].active');
|
||||
if (graphTabActive) {
|
||||
|
||||
@@ -610,7 +610,7 @@ function addAutomationConditionRow(condition: any) {
|
||||
<select class="condition-type-select">
|
||||
${CONDITION_TYPE_KEYS.map(k => `<option value="${k}" ${condType === k ? 'selected' : ''}>${t('automations.condition.' + k)}</option>`).join('')}
|
||||
</select>
|
||||
<button type="button" class="btn-remove-condition" onclick="this.closest('.automation-condition-row').remove(); if(window._autoGenerateAutomationName) window._autoGenerateAutomationName();" title="Remove">✕</button>
|
||||
<button type="button" class="btn-remove-condition" onclick="this.closest('.automation-condition-row').remove(); if(window._autoGenerateAutomationName) window._autoGenerateAutomationName();" title="Remove">${ICON_TRASH}</button>
|
||||
</div>
|
||||
<div class="condition-fields-container"></div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user