diff --git a/CHANGELOG.md b/CHANGELOG.md
index a691227..d60c8ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file.
+## 2.6.8 - 2026-08-08
+
+### Fixed
+
+- The screen-group command buttons stay disabled until a real target group is selected, so the placeholder "Select Screen Group" state cannot send commands.
+
## 2.6.7 - 2026-08-08
### Fixed
diff --git a/package.json b/package.json
index a02e80f..896db7e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pulse-signage",
- "version": "2.6.7",
+ "version": "2.6.8",
"private": false,
"description": "Pulse Signage application with MySQL and media storage",
"repository": {
diff --git a/src/web/public/js/dashboard/dashboard-page.js b/src/web/public/js/dashboard/dashboard-page.js
index 46a65fa..d8e2387 100644
--- a/src/web/public/js/dashboard/dashboard-page.js
+++ b/src/web/public/js/dashboard/dashboard-page.js
@@ -114,6 +114,7 @@
}
var selectedSlug = String(select.value || '').trim();
+ var hasSelectedGroup = Boolean(selectedSlug);
var actionTarget = '/clients/' + encodeURIComponent(selectedSlug || '__all__') + '/commands';
var selectedName = getSelectedScreenLabel();
var selectedClients = getSelectedScreenClients(latestDashboardState);
@@ -134,6 +135,10 @@
var action = String(form.getAttribute('data-screen-command-action') || '').trim();
var button = form.querySelector('button[type="submit"]');
+ if (button) {
+ button.disabled = !hasSelectedGroup;
+ }
+
if (action === 'pause' || action === 'blackout') {
updateToggleButton(button, form, latestDashboardState);
return;
diff --git a/src/web/views/signage/clients/list.hbs b/src/web/views/signage/clients/list.hbs
index e88f1c2..8549e8e 100644
--- a/src/web/views/signage/clients/list.hbs
+++ b/src/web/views/signage/clients/list.hbs
@@ -42,17 +42,17 @@
diff --git a/test/web-dashboard-page.test.js b/test/web-dashboard-page.test.js
index c2b6fc6..50b17b3 100644
--- a/test/web-dashboard-page.test.js
+++ b/test/web-dashboard-page.test.js
@@ -342,3 +342,181 @@ test('dashboard move client button opens the move modal for the selected row', (
assert.equal(clientNameInput.value, 'Lobby Client');
assert.equal(playerBaseUrlInput.value, 'http://player.local');
});
+
+test('dashboard screen group controls stay disabled until a group is selected', () => {
+ const script = fs.readFileSync(require.resolve('../src/web/public/js/dashboard/dashboard-page.js'), 'utf8');
+ const pauseInput = { value: 'true' };
+ const blackoutInput = { value: 'true' };
+ const reloadButton = {
+ disabled: false,
+ innerHTML: '',
+ setAttribute() {},
+ classList: { add() {}, remove() {} }
+ };
+ const pauseButton = {
+ disabled: false,
+ innerHTML: '',
+ setAttribute() {},
+ classList: { add() {}, remove() {} }
+ };
+ const blackoutButton = {
+ disabled: false,
+ innerHTML: '',
+ setAttribute() {},
+ classList: { add() {}, remove() {} }
+ };
+ const forms = [
+ {
+ getAttribute(name) {
+ if (name === 'data-screen-command-action') {
+ return 'reload';
+ }
+ return '';
+ },
+ setAttribute() {},
+ querySelector(selector) {
+ if (selector === 'button[type="submit"]') {
+ return reloadButton;
+ }
+ return null;
+ }
+ },
+ {
+ getAttribute(name) {
+ if (name === 'data-screen-command-action') {
+ return 'pause';
+ }
+ return '';
+ },
+ setAttribute() {},
+ querySelector(selector) {
+ if (selector === 'button[type="submit"]') {
+ return pauseButton;
+ }
+ if (selector === 'input[name="paused"]') {
+ return pauseInput;
+ }
+ return null;
+ }
+ },
+ {
+ getAttribute(name) {
+ if (name === 'data-screen-command-action') {
+ return 'blackout';
+ }
+ return '';
+ },
+ setAttribute() {},
+ querySelector(selector) {
+ if (selector === 'button[type="submit"]') {
+ return blackoutButton;
+ }
+ if (selector === 'input[name="blackout"]') {
+ return blackoutInput;
+ }
+ return null;
+ }
+ }
+ ];
+ const select = {
+ value: '',
+ options: [
+ { textContent: 'Select Screen Group', getAttribute() { return ''; } },
+ { textContent: 'Lobby', getAttribute(name) { return name === 'data-screen-name' ? 'Lobby' : ''; } },
+ { textContent: 'All Screens', getAttribute(name) { return name === 'data-screen-name' ? 'All screens' : ''; } }
+ ],
+ selectedIndex: 0,
+ dataset: {},
+ addEventListener(type, handler) {
+ if (type === 'change') {
+ this.changeHandler = handler;
+ }
+ }
+ };
+ const context = {
+ document: {
+ body: {
+ classList: {
+ toggle() {},
+ add() {},
+ remove() {}
+ }
+ },
+ getElementById(id) {
+ if (id === 'screen-command-select') {
+ return select;
+ }
+ if (id === 'dashboard-clients-table') {
+ return { getAttribute() { return 'false'; } };
+ }
+ if (id === 'dashboard-clients-table-body') {
+ return null;
+ }
+ return null;
+ },
+ querySelector(selector) {
+ if (selector === '[data-screen-command-select]') {
+ return select;
+ }
+ return null;
+ },
+ querySelectorAll(selector) {
+ if (selector === '[data-screen-command-form]') {
+ return forms;
+ }
+ return [];
+ },
+ addEventListener() {}
+ },
+ window: {
+ location: {
+ search: '',
+ protocol: 'http:',
+ host: 'example.test'
+ },
+ webUiHelpers: {
+ escapeHtml(value) { return String(value); },
+ formatDashboardDate(value) { return String(value); },
+ getClientRowKey(client) { return String(client && client.id || ''); },
+ getClientDisplayName(client) { return String(client && (client.client_name || client.name || client.clientId) || ''); },
+ setButtonVariant() {},
+ normalizeDisplayIp(value) { return String(value); }
+ },
+ WebSocket: null,
+ setTimeout() { return 1; },
+ clearTimeout() {},
+ alert() {},
+ prompt() {
+ return null;
+ },
+ webHandleDashboardState() {}
+ },
+ WebSocket: function MockWebSocket() {},
+ JSON: JSON,
+ Number: Number,
+ String: String,
+ Boolean: Boolean,
+ Array: Array,
+ Object: Object,
+ Math: Math,
+ Set: Set,
+ URLSearchParams: URLSearchParams,
+ FormData: function FormData() {}
+ };
+ context.window.document = context.document;
+ context.window.WebSocket = context.WebSocket;
+
+ vm.runInNewContext(script, context);
+
+ assert.equal(reloadButton.disabled, true);
+ assert.equal(pauseButton.disabled, true);
+ assert.equal(blackoutButton.disabled, true);
+
+ select.value = 'lobby';
+ select.selectedIndex = 1;
+ select.changeHandler();
+
+ assert.equal(reloadButton.disabled, false);
+ assert.equal(pauseButton.disabled, false);
+ assert.equal(blackoutButton.disabled, false);
+});