Skip to content

Commit ea3523e

Browse files
committed
Fix modal interactivity, Prowlarr card, and mobile layout
1 parent f5cb0b5 commit ea3523e

File tree

2 files changed

+186
-55
lines changed

2 files changed

+186
-55
lines changed

frontend/static/css/instances-card.css

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@
292292
/* Responsive adjustments */
293293
@media (max-width: 768px) {
294294
.instance-card-grid {
295-
grid-template-columns: 1fr;
295+
grid-template-columns: 1fr !important;
296296
}
297297

298298
.huntarr-modal {
@@ -305,4 +305,16 @@
305305
.modal-form-grid {
306306
grid-template-columns: 1fr;
307307
}
308+
309+
.setting-item {
310+
flex-direction: column;
311+
align-items: flex-start;
312+
}
313+
314+
.setting-item input[type="text"],
315+
.setting-item input[type="number"],
316+
.setting-item select {
317+
width: 100%;
318+
max-width: none;
319+
}
308320
}

frontend/static/js/settings_forms.js

Lines changed: 173 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,15 @@ const SettingsForms = {
126126
`;
127127

128128
// Generate cards for each instance
129-
settings.instances.forEach((instance, index) => {
130-
instancesHtml += SettingsForms.renderInstanceCard('sonarr', instance, index);
131-
});
129+
if (settings.instances && settings.instances.length > 0) {
130+
settings.instances.forEach((instance, index) => {
131+
instancesHtml += SettingsForms.renderInstanceCard('sonarr', instance, index);
132+
});
133+
}
132134

133-
// Add "Add Instance" card
135+
// Add "Add Instance" card (always present)
134136
instancesHtml += `
135-
<div class="add-instance-card" onclick="SettingsForms.openInstanceModal('sonarr')">
137+
<div class="add-instance-card" data-app-type="sonarr">
136138
<div class="add-icon"><i class="fas fa-plus-circle"></i></div>
137139
<div class="add-text">Add Sonarr Instance</div>
138140
</div>
@@ -244,6 +246,29 @@ const SettingsForms = {
244246
container.innerHTML =
245247
sonarrSaveButtonHtml + instancesHtml + searchSettingsHtml;
246248

249+
// Attach event listeners dynamically
250+
const grid = container.querySelector('#sonarr-instances-grid');
251+
if (grid) {
252+
grid.addEventListener('click', (e) => {
253+
const editBtn = e.target.closest('.btn-card.edit');
254+
const deleteBtn = e.target.closest('.btn-card.delete');
255+
const addCard = e.target.closest('.add-instance-card');
256+
257+
if (editBtn) {
258+
const appType = editBtn.dataset.appType;
259+
const index = parseInt(editBtn.dataset.instanceIndex);
260+
SettingsForms.openInstanceModal(appType, index);
261+
} else if (deleteBtn) {
262+
const appType = deleteBtn.dataset.appType;
263+
const index = parseInt(deleteBtn.dataset.instanceIndex);
264+
SettingsForms.deleteInstance(appType, index);
265+
} else if (addCard) {
266+
const appType = addCard.dataset.appType;
267+
SettingsForms.openInstanceModal(appType);
268+
}
269+
});
270+
}
271+
247272

248273
// Add event listeners for custom tags visibility
249274
const tagProcessedItemsToggle = container.querySelector(
@@ -2131,13 +2156,15 @@ const SettingsForms = {
21312156
`;
21322157

21332158
// Generate cards for each instance
2134-
settings.instances.forEach((instance, index) => {
2135-
instancesHtml += SettingsForms.renderInstanceCard('eros', instance, index);
2136-
});
2159+
if (settings.instances && settings.instances.length > 0) {
2160+
settings.instances.forEach((instance, index) => {
2161+
instancesHtml += SettingsForms.renderInstanceCard('eros', instance, index);
2162+
});
2163+
}
21372164

2138-
// Add "Add Instance" card
2165+
// Add "Add Instance" card (always present)
21392166
instancesHtml += `
2140-
<div class="add-instance-card" onclick="SettingsForms.openInstanceModal('eros')">
2167+
<div class="add-instance-card" data-app-type="eros">
21412168
<div class="add-icon"><i class="fas fa-plus-circle"></i></div>
21422169
<div class="add-text">Add Whisparr V3 Instance</div>
21432170
</div>
@@ -2148,10 +2175,37 @@ const SettingsForms = {
21482175
</div> <!-- settings-group -->
21492176
`;
21502177

2151-
// Search Mode dropdown
2152-
let searchSettingsHtml = `
2178+
// Continue with the rest of the settings form
2179+
container.innerHTML = `
2180+
${erosSaveButtonHtml}
2181+
${instancesHtml}
2182+
21532183
<div class="settings-group">
21542184
<h3>Search Settings</h3>
2185+
`;
2186+
2187+
// Attach event listeners dynamically
2188+
const grid = container.querySelector('#eros-instances-grid');
2189+
if (grid) {
2190+
grid.addEventListener('click', (e) => {
2191+
const editBtn = e.target.closest('.btn-card.edit');
2192+
const deleteBtn = e.target.closest('.btn-card.delete');
2193+
const addCard = e.target.closest('.add-instance-card');
2194+
2195+
if (editBtn) {
2196+
const appType = editBtn.dataset.appType;
2197+
const index = parseInt(editBtn.dataset.instanceIndex);
2198+
SettingsForms.openInstanceModal(appType, index);
2199+
} else if (deleteBtn) {
2200+
const appType = deleteBtn.dataset.appType;
2201+
const index = parseInt(deleteBtn.dataset.instanceIndex);
2202+
SettingsForms.deleteInstance(appType, index);
2203+
} else if (addCard) {
2204+
const appType = addCard.dataset.appType;
2205+
SettingsForms.openInstanceModal(appType);
2206+
}
2207+
});
2208+
}
21552209
<div class="setting-item">
21562210
<label for="eros_search_mode"><a href="https://plexguide.github.io/Huntarr.io/apps/eros.html#search-mode" class="info-icon" title="Learn more about search modes" target="_blank" rel="noopener"><i class="fas fa-info-circle"></i></a>Search Mode:</label>
21572211
<select id="eros_search_mode" name="search_mode">
@@ -7517,6 +7571,7 @@ const SettingsForms = {
75177571
`;
75187572

75197573
// Create the Prowlarr configuration container
7574+
// Prowlarr is a single instance, but we reuse the card design
75207575
let prowlarrHtml = `
75217576
<div class="settings-group" style="
75227577
background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #334155 100%);
@@ -7527,57 +7582,119 @@ const SettingsForms = {
75277582
box-shadow: 0 4px 12px rgba(90, 109, 137, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1);
75287583
">
75297584
<h3>Prowlarr Configuration</h3>
7530-
<div class="prowlarr-container">
7531-
<div class="instance-item" data-instance-id="0">
7532-
<div class="instance-header">
7533-
<h4>Prowlarr</h4>
7534-
<div class="instance-actions">
7535-
<span class="connection-status" id="prowlarr-status-0"></span>
7536-
</div>
7537-
</div>
7538-
<div class="instance-content">
7539-
<div class="setting-item">
7540-
<label for="prowlarr-enabled-0">Enabled:</label>
7541-
<label class="toggle-switch" style="width:40px; height:20px; display:inline-block; position:relative;">
7542-
<input type="checkbox" id="prowlarr-enabled-0" name="enabled" ${
7543-
settings.enabled !== false
7544-
? "checked"
7545-
: ""
7546-
}>
7547-
<span class="toggle-slider" style="position:absolute; cursor:pointer; top:0; left:0; right:0; bottom:0; background-color:#3d4353; border-radius:20px; transition:0.4s;"></span>
7548-
</label>
7549-
<p class="setting-help">Enable or disable Prowlarr integration</p>
7550-
</div>
7551-
<div class="setting-item">
7552-
<label for="prowlarr-url-0">URL:</label>
7553-
<input type="text" id="prowlarr-url-0" name="api_url" value="${
7554-
settings.api_url || ""
7555-
}" placeholder="Base URL for Prowlarr (e.g., http://localhost:9696)" data-instance-index="0">
7556-
<p class="setting-help">Base URL for Prowlarr (e.g., http://localhost:9696)</p>
7557-
</div>
7558-
<div class="setting-item">
7559-
<label for="prowlarr-key-0">API Key:</label>
7560-
<input type="text" id="prowlarr-key-0" name="api_key" value="${
7561-
settings.api_key || ""
7562-
}" placeholder="API key for Prowlarr" data-instance-index="0">
7563-
<p class="setting-help">API key for Prowlarr</p>
7564-
</div>
7565-
</div>
7566-
</div>
7585+
<div class="instance-card-grid" id="prowlarr-instances-grid">
7586+
`;
7587+
7588+
// Normalize Prowlarr settings to look like an instance object for the card renderer
7589+
// Prowlarr settings are flat, not in an 'instances' array
7590+
const prowlarrInstance = {
7591+
name: 'Prowlarr',
7592+
api_url: settings.api_url || '',
7593+
api_key: settings.api_key || '',
7594+
enabled: settings.enabled !== false
7595+
};
7596+
7597+
// Render single card
7598+
prowlarrHtml += SettingsForms.renderInstanceCard('prowlarr', prowlarrInstance, 0);
7599+
7600+
prowlarrHtml += `
75677601
</div>
75687602
</div>
75697603
`;
75707604

75717605
// Set the content with save button at the top
75727606
container.innerHTML = prowlarrSaveButtonHtml + prowlarrHtml;
75737607

7574-
// Setup auto-detection (like Sonarr)
7575-
this.setupProwlarrAutoDetection(container);
7608+
// Attach event listeners dynamically
7609+
const grid = container.querySelector('#prowlarr-instances-grid');
7610+
if (grid) {
7611+
grid.addEventListener('click', (e) => {
7612+
const editBtn = e.target.closest('.btn-card.edit');
7613+
if (editBtn) {
7614+
// Open modal for single instance Prowlarr
7615+
SettingsForms.openProwlarrModal();
7616+
}
7617+
});
7618+
}
75767619

75777620
// Set up manual save functionality for Prowlarr
75787621
this.setupAppManualSave(container, "prowlarr", settings);
75797622
},
75807623

7624+
// Open modal specifically for Prowlarr (since it has a different data structure)
7625+
openProwlarrModal: function() {
7626+
const settings = window.huntarrUI.originalSettings.prowlarr;
7627+
if (!settings) return;
7628+
7629+
// Create modal container if it doesn't exist
7630+
let modalOverlay = document.getElementById('huntarr-instance-modal');
7631+
if (!modalOverlay) {
7632+
modalOverlay = document.createElement('div');
7633+
modalOverlay.id = 'huntarr-instance-modal';
7634+
modalOverlay.className = 'huntarr-modal-overlay';
7635+
document.body.appendChild(modalOverlay);
7636+
}
7637+
7638+
const formFields = `
7639+
<div class="modal-form-section">
7640+
<div class="modal-section-title">Connection Details</div>
7641+
<div class="setting-item">
7642+
<label>Enabled</label>
7643+
<label class="toggle-switch">
7644+
<input type="checkbox" id="modal-enabled" ${settings.enabled !== false ? 'checked' : ''}>
7645+
<span class="toggle-slider"></span>
7646+
</label>
7647+
</div>
7648+
<div class="setting-item">
7649+
<label>URL</label>
7650+
<input type="text" id="modal-url" value="${settings.api_url || ''}" placeholder="http://localhost:9696">
7651+
</div>
7652+
<div class="setting-item">
7653+
<label>API Key</label>
7654+
<input type="text" id="modal-key" value="${settings.api_key || ''}" placeholder="API Key">
7655+
</div>
7656+
</div>
7657+
`;
7658+
7659+
modalOverlay.innerHTML = `
7660+
<div class="huntarr-modal">
7661+
<div class="huntarr-modal-header">
7662+
<h3 class="huntarr-modal-title">Edit Prowlarr Configuration</h3>
7663+
<button class="huntarr-modal-close" onclick="document.getElementById('huntarr-instance-modal').classList.remove('active')">
7664+
<i class="fas fa-times"></i>
7665+
</button>
7666+
</div>
7667+
<div class="huntarr-modal-body">
7668+
<div class="modal-form-grid">
7669+
${formFields}
7670+
</div>
7671+
</div>
7672+
<div class="huntarr-modal-footer">
7673+
<button class="btn-modal btn-modal-secondary" onclick="document.getElementById('huntarr-instance-modal').classList.remove('active')">Cancel</button>
7674+
<button class="btn-modal btn-modal-primary" onclick="SettingsForms.saveProwlarrFromModal()">Save Changes</button>
7675+
</div>
7676+
</div>
7677+
`;
7678+
7679+
setTimeout(() => modalOverlay.classList.add('active'), 10);
7680+
},
7681+
7682+
// Save Prowlarr settings from modal
7683+
saveProwlarrFromModal: function() {
7684+
const settings = window.huntarrUI.originalSettings.prowlarr;
7685+
7686+
// Update settings object
7687+
settings.enabled = document.getElementById('modal-enabled').checked;
7688+
settings.api_url = document.getElementById('modal-url').value;
7689+
settings.api_key = document.getElementById('modal-key').value;
7690+
7691+
// Save to server
7692+
this.saveAppSettings('prowlarr', settings);
7693+
7694+
// Close modal
7695+
document.getElementById('huntarr-instance-modal').classList.remove('active');
7696+
},
7697+
75817698
// Setup auto-detection for Prowlarr (similar to Sonarr)
75827699
setupProwlarrAutoDetection: function (container) {
75837700
console.log("[SettingsForms] Setting up Prowlarr auto-detection");
@@ -7639,10 +7756,12 @@ const SettingsForms = {
76397756

76407757
// Render a single instance card
76417758
renderInstanceCard: function(appType, instance, index) {
7642-
const statusClass = instance.enabled ? 'status-connected' : 'status-unknown'; // We can improve this with real status later
7759+
const statusClass = instance.enabled ? 'status-connected' : 'status-unknown';
76437760
const statusIcon = instance.enabled ? 'fa-check-circle' : 'fa-pause-circle';
76447761
const isDefault = index === 0;
7762+
const indexAttr = index !== null ? index : '';
76457763

7764+
// Use window.SettingsForms explicitly in onclick to ensure scope resolution
76467765
return `
76477766
<div class="instance-card ${isDefault ? 'default-instance' : ''}" data-instance-index="${index}">
76487767
<div class="instance-card-header">
@@ -7666,11 +7785,11 @@ const SettingsForms = {
76667785
</div>
76677786
</div>
76687787
<div class="instance-card-footer">
7669-
<button type="button" class="btn-card edit" onclick="SettingsForms.openInstanceModal('${appType}', ${index})">
7788+
<button type="button" class="btn-card edit" data-app-type="${appType}" data-instance-index="${index}">
76707789
<i class="fas fa-edit"></i> Edit
76717790
</button>
76727791
${!isDefault ? `
7673-
<button type="button" class="btn-card delete" onclick="SettingsForms.deleteInstance('${appType}', ${index})">
7792+
<button type="button" class="btn-card delete" data-app-type="${appType}" data-instance-index="${index}">
76747793
<i class="fas fa-trash"></i> Delete
76757794
</button>
76767795
` : ''}

0 commit comments

Comments
 (0)