diff --git a/web_interface/static/v3/js/app-shell.js b/web_interface/static/v3/js/app-shell.js index bfe6a25f..70aff559 100644 --- a/web_interface/static/v3/js/app-shell.js +++ b/web_interface/static/v3/js/app-shell.js @@ -2775,7 +2775,7 @@ } }; - window.executePluginAction = function(pluginId, actionId) { + window.executePluginAction = function(actionId, actionIndex, pluginId) { fetch(`/api/v3/plugins/action?plugin_id=${pluginId}&action_id=${actionId}`, { method: 'POST' }) diff --git a/web_interface/static/v3/js/widgets/custom-feeds.js b/web_interface/static/v3/js/widgets/custom-feeds.js index 62ed412b..b3f9a6c8 100644 --- a/web_interface/static/v3/js/widgets/custom-feeds.js +++ b/web_interface/static/v3/js/widgets/custom-feeds.js @@ -209,6 +209,7 @@ const removeButton = document.createElement('button'); removeButton.type = 'button'; removeButton.className = 'text-red-600 hover:text-red-800 px-2 py-1'; + removeButton.setAttribute('aria-label', 'Remove feed'); removeButton.addEventListener('click', function() { window.removeCustomFeedRow(this); }); @@ -333,6 +334,7 @@ const removeButton = document.createElement('button'); removeButton.type = 'button'; removeButton.className = 'text-red-600 hover:text-red-800 px-2 py-1'; + removeButton.setAttribute('aria-label', 'Remove feed'); removeButton.addEventListener('click', function() { window.removeCustomFeedRow(this); }); @@ -404,7 +406,10 @@ if (!file) return; const formData = new FormData(); - formData.append('file', file); + // Backend contract (api_v3.upload_plugin_asset): field must be named + // "files" (request.files.getlist('files')), and the response carries + // results in a top-level "uploaded_files" key, not nested under "data". + formData.append('files', file); formData.append('plugin_id', pluginId); fetch('/api/v3/plugins/assets/upload', { @@ -421,8 +426,8 @@ return response.json(); }) .then(data => { - if (data.status === 'success' && data.data && data.data.files && data.data.files.length > 0) { - const uploadedFile = data.data.files[0]; + if (data.status === 'success' && data.uploaded_files && data.uploaded_files.length > 0) { + const uploadedFile = data.uploaded_files[0]; const row = document.querySelector(`#${fieldId}_tbody tr[data-index="${index}"]`); if (row) { const logoCell = row.querySelector('td:nth-child(3)'); @@ -495,8 +500,6 @@ // Append container to logoCell logoCell.appendChild(container); } - // Allow re-uploading the same file - event.target.value = ''; } else { const notifyFn = window.showNotification || alert; notifyFn('Upload failed: ' + (data.message || 'Unknown error'), 'error'); @@ -506,6 +509,12 @@ console.error('Upload error:', error); const notifyFn = window.showNotification || alert; notifyFn('Upload failed: ' + error.message, 'error'); + }) + .finally(() => { + // Reset regardless of outcome, so the same file can be re-selected + // to retry after a failure (browsers won't fire "change" again + // for an input that still holds that exact file). + event.target.value = ''; }); }; diff --git a/web_interface/templates/v3/partials/overview.html b/web_interface/templates/v3/partials/overview.html index 972d31d6..47713d8d 100644 --- a/web_interface/templates/v3/partials/overview.html +++ b/web_interface/templates/v3/partials/overview.html @@ -153,7 +153,10 @@ return; } var appEl = document.querySelector('[x-data="app()"]') || document.querySelector('[x-data]'); - var data = appEl && appEl._x_dataStack && appEl._x_dataStack[0]; + // Same two-tier resolution as settings-search.js's getAppData(): + // _x_dataStack on current Alpine, __x.$data as an older-API fallback. + var data = appEl && ((appEl._x_dataStack && appEl._x_dataStack[0]) || + (appEl.__x && appEl.__x.$data)); if (data) { data.activeTab = btn.dataset.tab; if ('mobileNavOpen' in data) data.mobileNavOpen = false;